[tests] Implemented basic CpmlPrimitive test
[adg.git] / src / adg / adg-canvas.c
blobf26b25ab967cc5640d150a9e91aadaf4951d3d92
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011 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.
21 /**
22 * SECTION:adg-canvas
23 * @short_description: The drawing container
25 * The canvas is the toplevel entity of an ADG drawing. It can be
26 * bound to a GTK+ widget, such as #AdgGtkArea, or manually rendered
27 * to a custom surface.
29 * Tipically, the canvas contains the description and properties of
30 * the media used, such as such as size (if relevant), margins,
31 * border and paddings. This approach clearly follows the block model
32 * of the CSS specifications level 2.
34 * The paddings specify the distance between the entities contained
35 * by the canvas and the border. The margins specify the distance
36 * between the canvas border and the media extents.
38 * The canvas (hence the media) size can be explicitely specified
39 * by directly writing to the #AdgCanvas:size property or using any
40 * valid setter, such as adg_canvas_set_size(),
41 * adg_canvas_set_size_explicit() or the convenient
42 * adg_canvas_set_paper() GTK+ wrapper. You can also set explicitely
43 * only one dimension and let the other one be computed automatically.
44 * This is done by using the special value %0 that specifies a side
45 * must be autocalculated.
47 * By default either width and height must be autocalculated (are
48 * set to %0), so the arrange() phase on the canvas is performed.
49 * Margins and paddings are then added to the extents to get the
50 * border coordinates and the final bounding box.
52 * When the size is explicitely set, instead, the final bounding
53 * box is forcibly set to this value without taking the canvas
54 * extents into account. The margins are then subtracted to get
55 * the coordinates of the border. In this case, the paddings are
56 * simply ignored.
57 **/
59 /**
60 * AdgCanvas:
62 * All fields are private and should not be used directly.
63 * Use its public methods instead.
64 **/
67 #include "adg-internal.h"
68 #include "adg-container.h"
69 #include "adg-table.h"
70 #include "adg-title-block.h"
71 #include "adg-style.h"
72 #include "adg-color-style.h"
73 #include "adg-dress.h"
74 #include "adg-dress-builtins.h"
76 #include "adg-canvas.h"
77 #include "adg-canvas-private.h"
80 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_canvas_parent_class)
81 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_canvas_parent_class)
84 G_DEFINE_TYPE(AdgCanvas, adg_canvas, ADG_TYPE_CONTAINER)
86 enum {
87 PROP_0,
88 PROP_SIZE,
89 PROP_BACKGROUND_DRESS,
90 PROP_FRAME_DRESS,
91 PROP_TITLE_BLOCK,
92 PROP_TOP_MARGIN,
93 PROP_RIGHT_MARGIN,
94 PROP_BOTTOM_MARGIN,
95 PROP_LEFT_MARGIN,
96 PROP_HAS_FRAME,
97 PROP_TOP_PADDING,
98 PROP_RIGHT_PADDING,
99 PROP_BOTTOM_PADDING,
100 PROP_LEFT_PADDING
104 static void _adg_dispose (GObject *object);
105 static void _adg_get_property (GObject *object,
106 guint param_id,
107 GValue *value,
108 GParamSpec *pspec);
109 static void _adg_set_property (GObject *object,
110 guint param_id,
111 const GValue *value,
112 GParamSpec *pspec);
113 static void _adg_global_changed (AdgEntity *entity);
114 static void _adg_local_changed (AdgEntity *entity);
115 static void _adg_invalidate (AdgEntity *entity);
116 static void _adg_arrange (AdgEntity *entity);
117 static void _adg_render (AdgEntity *entity,
118 cairo_t *cr);
121 static void
122 adg_canvas_class_init(AdgCanvasClass *klass)
124 GObjectClass *gobject_class;
125 AdgEntityClass *entity_class;
126 GParamSpec *param;
128 gobject_class = (GObjectClass *) klass;
129 entity_class = (AdgEntityClass *) klass;
131 g_type_class_add_private(klass, sizeof(AdgCanvasPrivate));
133 gobject_class->dispose = _adg_dispose;
134 gobject_class->get_property = _adg_get_property;
135 gobject_class->set_property = _adg_set_property;
137 entity_class->global_changed = _adg_global_changed;
138 entity_class->local_changed = _adg_local_changed;
139 entity_class->invalidate = _adg_invalidate;
140 entity_class->arrange = _adg_arrange;
141 entity_class->render = _adg_render;
143 param = g_param_spec_boxed("size",
144 P_("Canvas Size"),
145 P_("The size set on this canvas: use 0 to have an automatic dimension based on the canvas extents"),
146 ADG_TYPE_PAIR,
147 G_PARAM_READWRITE);
148 g_object_class_install_property(gobject_class, PROP_SIZE, param);
150 param = adg_param_spec_dress("background-dress",
151 P_("Background Dress"),
152 P_("The color dress to use for the canvas background"),
153 ADG_DRESS_COLOR_BACKGROUND,
154 G_PARAM_READWRITE);
155 g_object_class_install_property(gobject_class, PROP_BACKGROUND_DRESS, param);
157 param = adg_param_spec_dress("frame-dress",
158 P_("Frame Dress"),
159 P_("Line dress to use while drawing the frame around the canvas"),
160 ADG_DRESS_LINE_FRAME,
161 G_PARAM_READWRITE);
162 g_object_class_install_property(gobject_class, PROP_FRAME_DRESS, param);
164 param = g_param_spec_object("title-block",
165 P_("Title Block"),
166 P_("The title block to assign to this canvas"),
167 ADG_TYPE_TITLE_BLOCK,
168 G_PARAM_READWRITE);
169 g_object_class_install_property(gobject_class, PROP_TITLE_BLOCK, param);
171 param = g_param_spec_double("top-margin",
172 P_("Top Margin"),
173 P_("The margin (in global space) to leave above the frame"),
174 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
175 G_PARAM_READWRITE);
176 g_object_class_install_property(gobject_class, PROP_TOP_MARGIN, param);
178 param = g_param_spec_double("right-margin",
179 P_("Right Margin"),
180 P_("The margin (in global space) to leave empty at the right of the frame"),
181 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
182 G_PARAM_READWRITE);
183 g_object_class_install_property(gobject_class, PROP_RIGHT_MARGIN, param);
185 param = g_param_spec_double("bottom-margin",
186 P_("Bottom Margin"),
187 P_("The margin (in global space) to leave empty below the frame"),
188 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
189 G_PARAM_READWRITE);
190 g_object_class_install_property(gobject_class, PROP_BOTTOM_MARGIN, param);
192 param = g_param_spec_double("left-margin",
193 P_("Left Margin"),
194 P_("The margin (in global space) to leave empty at the left of the frame"),
195 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
196 G_PARAM_READWRITE);
197 g_object_class_install_property(gobject_class, PROP_LEFT_MARGIN, param);
199 param = g_param_spec_boolean("has-frame",
200 P_("Has Frame Flag"),
201 P_("If enabled, a frame using the frame dress will be drawn around the canvas extents, taking into account the margins"),
202 TRUE,
203 G_PARAM_READWRITE);
204 g_object_class_install_property(gobject_class, PROP_HAS_FRAME, param);
206 param = g_param_spec_double("top-padding",
207 P_("Top Padding"),
208 P_("The padding (in global space) to leave empty above between the drawing and the frame"),
209 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
210 G_PARAM_READWRITE);
211 g_object_class_install_property(gobject_class, PROP_TOP_PADDING, param);
213 param = g_param_spec_double("right-padding",
214 P_("Right Padding"),
215 P_("The padding (in global space) to leave empty at the right between the drawing and the frame"),
216 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
217 G_PARAM_READWRITE);
218 g_object_class_install_property(gobject_class, PROP_RIGHT_PADDING, param);
220 param = g_param_spec_double("bottom-padding",
221 P_("Bottom Padding"),
222 P_("The padding (in global space) to leave empty below between the drawing and the frame"),
223 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
224 G_PARAM_READWRITE);
225 g_object_class_install_property(gobject_class, PROP_BOTTOM_PADDING, param);
227 param = g_param_spec_double("left-padding",
228 P_("Left Padding"),
229 P_("The padding (in global space) to leave empty at the left between the drawing and the frame"),
230 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
231 G_PARAM_READWRITE);
232 g_object_class_install_property(gobject_class, PROP_LEFT_PADDING, param);
235 static void
236 adg_canvas_init(AdgCanvas *canvas)
238 AdgCanvasPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(canvas,
239 ADG_TYPE_CANVAS,
240 AdgCanvasPrivate);
242 data->size.x = 0;
243 data->size.y = 0;
244 data->background_dress = ADG_DRESS_COLOR_BACKGROUND;
245 data->frame_dress = ADG_DRESS_LINE_FRAME;
246 data->title_block = NULL;
247 data->top_margin = 15;
248 data->right_margin = 15;
249 data->bottom_margin = 15;
250 data->left_margin = 15;
251 data->has_frame = TRUE;
252 data->top_padding = 15;
253 data->right_padding = 15;
254 data->bottom_padding = 15;
255 data->left_padding = 15;
257 canvas->data = data;
260 static void
261 _adg_dispose(GObject *object)
263 AdgCanvasPrivate *data = ((AdgCanvas *) object)->data;
265 if (data->title_block) {
266 g_object_unref(data->title_block);
267 data->title_block = NULL;
270 if (_ADG_OLD_OBJECT_CLASS->dispose)
271 _ADG_OLD_OBJECT_CLASS->dispose(object);
275 static void
276 _adg_get_property(GObject *object, guint prop_id,
277 GValue *value, GParamSpec *pspec)
279 AdgCanvasPrivate *data = ((AdgCanvas *) object)->data;
281 switch (prop_id) {
282 case PROP_SIZE:
283 g_value_set_boxed(value, &data->size);
284 break;
285 case PROP_BACKGROUND_DRESS:
286 g_value_set_int(value, data->background_dress);
287 break;
288 case PROP_FRAME_DRESS:
289 g_value_set_int(value, data->frame_dress);
290 break;
291 case PROP_TITLE_BLOCK:
292 g_value_set_object(value, data->title_block);
293 break;
294 case PROP_TOP_MARGIN:
295 g_value_set_double(value, data->top_margin);
296 break;
297 case PROP_RIGHT_MARGIN:
298 g_value_set_double(value, data->right_margin);
299 break;
300 case PROP_BOTTOM_MARGIN:
301 g_value_set_double(value, data->bottom_margin);
302 break;
303 case PROP_LEFT_MARGIN:
304 g_value_set_double(value, data->left_margin);
305 break;
306 case PROP_HAS_FRAME:
307 g_value_set_boolean(value, data->has_frame);
308 break;
309 case PROP_TOP_PADDING:
310 g_value_set_double(value, data->top_padding);
311 break;
312 case PROP_RIGHT_PADDING:
313 g_value_set_double(value, data->right_padding);
314 break;
315 case PROP_BOTTOM_PADDING:
316 g_value_set_double(value, data->bottom_padding);
317 break;
318 case PROP_LEFT_PADDING:
319 g_value_set_double(value, data->left_padding);
320 break;
321 default:
322 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
323 break;
327 static void
328 _adg_set_property(GObject *object, guint prop_id,
329 const GValue *value, GParamSpec *pspec)
331 AdgCanvas *canvas;
332 AdgCanvasPrivate *data;
333 AdgTitleBlock *title_block;
335 canvas = (AdgCanvas *) object;
336 data = canvas->data;
338 switch (prop_id) {
339 case PROP_SIZE:
340 adg_pair_copy(&data->size, g_value_get_boxed(value));
341 break;
342 case PROP_BACKGROUND_DRESS:
343 data->background_dress = g_value_get_int(value);
344 break;
345 case PROP_FRAME_DRESS:
346 data->frame_dress = g_value_get_int(value);
347 break;
348 case PROP_TITLE_BLOCK:
349 title_block = g_value_get_object(value);
350 if (title_block) {
351 g_object_ref(title_block);
352 adg_entity_set_parent((AdgEntity *) title_block,
353 (AdgEntity *) canvas);
355 if (data->title_block)
356 g_object_unref(data->title_block);
357 data->title_block = title_block;
358 break;
359 case PROP_TOP_MARGIN:
360 data->top_margin = g_value_get_double(value);
361 break;
362 case PROP_RIGHT_MARGIN:
363 data->right_margin = g_value_get_double(value);
364 break;
365 case PROP_BOTTOM_MARGIN:
366 data->bottom_margin = g_value_get_double(value);
367 break;
368 case PROP_LEFT_MARGIN:
369 data->left_margin = g_value_get_double(value);
370 break;
371 case PROP_HAS_FRAME:
372 data->has_frame = g_value_get_boolean(value);
373 break;
374 case PROP_TOP_PADDING:
375 data->top_padding = g_value_get_double(value);
376 break;
377 case PROP_RIGHT_PADDING:
378 data->right_padding = g_value_get_double(value);
379 break;
380 case PROP_BOTTOM_PADDING:
381 data->bottom_padding = g_value_get_double(value);
382 break;
383 case PROP_LEFT_PADDING:
384 data->left_padding = g_value_get_double(value);
385 break;
386 default:
387 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
388 break;
394 * adg_canvas_new:
396 * Creates a new empty canvas object.
398 * Returns: the canvas
400 AdgCanvas *
401 adg_canvas_new(void)
403 return g_object_new(ADG_TYPE_CANVAS, NULL);
407 * adg_canvas_set_size:
408 * @canvas: an #AdgCanvas
409 * @size: the new size for the canvas
411 * Sets a specific size on @canvas. The x and/or y
412 * components of the returned #AdgPair could be %0, in which
413 * case the size returned by adg_entity_get_extents() on
414 * @canvas will be used instead.
416 void
417 adg_canvas_set_size(AdgCanvas *canvas, const AdgPair *size)
419 g_return_if_fail(ADG_IS_CANVAS(canvas));
420 g_return_if_fail(size != NULL);
422 g_object_set(canvas, "size", size, NULL);
426 * adg_canvas_set_size_explicit:
427 * @canvas: an #AdgCanvas
428 * @x: the new width of the canvas or %0 to reset
429 * @y: the new height of the canvas or %0 to reset
431 * A convenient function to set the size of @canvas using
432 * explicit coordinates. Check adg_canvas_set_size() for
433 * further details.
435 void
436 adg_canvas_set_size_explicit(AdgCanvas *canvas, gdouble x, gdouble y)
438 AdgPair size;
440 size.x = x;
441 size.y = y;
443 adg_canvas_set_size(canvas, &size);
447 * adg_canvas_get_size:
448 * @canvas: an #AdgCanvas
450 * Gets the specific size set on @canvas. The x and/or y
451 * components of the returned #AdgPair could be %0, in which
452 * case the size returned by adg_entity_get_extents() on
453 * @canvas will be used instead.
455 * Returns: the explicit size set on this canvas or %NULL on errors
457 const AdgPair *
458 adg_canvas_get_size(AdgCanvas *canvas)
460 AdgCanvasPrivate *data;
462 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
464 data = canvas->data;
465 return &data->size;
469 * adg_canvas_set_background_dress:
470 * @canvas: an #AdgCanvas
471 * @dress: the new #AdgDress to use
473 * Sets a new background dress for rendering @canvas: the new
474 * dress must be a color dress.
476 void
477 adg_canvas_set_background_dress(AdgCanvas *canvas, AdgDress dress)
479 g_return_if_fail(ADG_IS_CANVAS(canvas));
480 g_object_set(canvas, "background-dress", dress, NULL);
484 * adg_canvas_get_background_dress:
485 * @canvas: an #AdgCanvas
487 * Gets the background dress to be used in rendering @canvas.
489 * Returns: the current background dress
491 AdgDress
492 adg_canvas_get_background_dress(AdgCanvas *canvas)
494 AdgCanvasPrivate *data;
496 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
498 data = canvas->data;
500 return data->background_dress;
504 * adg_canvas_set_frame_dress:
505 * @canvas: an #AdgCanvas
506 * @dress: the new #AdgDress to use
508 * Sets the #AdgCanvas:frame-dress property of @canvas to @dress:
509 * the new dress must be a line dress.
511 void
512 adg_canvas_set_frame_dress(AdgCanvas *canvas, AdgDress dress)
514 g_return_if_fail(ADG_IS_CANVAS(canvas));
515 g_object_set(canvas, "frame-dress", dress, NULL);
519 * adg_canvas_get_frame_dress:
520 * @canvas: an #AdgCanvas
522 * Gets the frame dress to be used in rendering the border of @canvas.
524 * Returns: the current frame dress
526 AdgDress
527 adg_canvas_get_frame_dress(AdgCanvas *canvas)
529 AdgCanvasPrivate *data;
531 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
533 data = canvas->data;
534 return data->frame_dress;
538 * adg_canvas_set_title_block:
539 * @canvas: an #AdgCanvas
540 * @title_block: a title block
542 * Sets the #AdgCanvas:title-block property of @canvas to @title_block.
544 * Although a title block entity could be added to @canvas in the usual
545 * way, that is using the adg_container_add() method, assigning a title
546 * block with adg_canvas_set_title_block() is somewhat different:
548 * - @title_block will be automatically attached to the bottom right
549 * corner of to the @canvas frame (this could be accomplished in the
550 * usual way too, by resetting the right and bottom paddings);
551 * - the @title_block boundary box is not taken into account while
552 * computing the extents of @canvas.
554 void
555 adg_canvas_set_title_block(AdgCanvas *canvas, AdgTitleBlock *title_block)
557 g_return_if_fail(ADG_IS_CANVAS(canvas));
558 g_return_if_fail(title_block == NULL || ADG_IS_TITLE_BLOCK(title_block));
559 g_object_set(canvas, "title-block", title_block, NULL);
563 * adg_canvas_get_title_block:
564 * @canvas: an #AdgCanvas
566 * Gets the #AdgTitleBlock object of @canvas: check
567 * adg_canvas_set_title_block() for details.
569 * Returns: the title block object or %NULL
571 AdgTitleBlock *
572 adg_canvas_get_title_block(AdgCanvas *canvas)
574 AdgCanvasPrivate *data;
576 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
578 data = canvas->data;
579 return data->title_block;
583 * adg_canvas_set_top_margin:
584 * @canvas: an #AdgCanvas
585 * @value: the new margin, in global space
587 * Changes the top margin of @canvas by setting #AdgCanvas:top-margin
588 * to @value. Negative values are allowed.
590 void
591 adg_canvas_set_top_margin(AdgCanvas *canvas, gdouble value)
593 g_return_if_fail(ADG_IS_CANVAS(canvas));
594 g_object_set(canvas, "top-margin", value, NULL);
598 * adg_canvas_get_top_margin:
599 * @canvas: an #AdgCanvas
601 * Gets the top margin (in global space) of @canvas.
603 * Returns: the requested margin or %0 on error
605 gdouble
606 adg_canvas_get_top_margin(AdgCanvas *canvas)
608 AdgCanvasPrivate *data;
610 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
612 data = canvas->data;
613 return data->top_margin;
617 * adg_canvas_set_right_margin:
618 * @canvas: an #AdgCanvas
619 * @value: the new margin, in global space
621 * Changes the right margin of @canvas by setting #AdgCanvas:right-margin
622 * to @value. Negative values are allowed.
624 void
625 adg_canvas_set_right_margin(AdgCanvas *canvas, gdouble value)
627 g_return_if_fail(ADG_IS_CANVAS(canvas));
628 g_object_set(canvas, "right-margin", value, NULL);
632 * adg_canvas_get_right_margin:
633 * @canvas: an #AdgCanvas
635 * Gets the right margin (in global space) of @canvas.
637 * Returns: the requested margin or %0 on error
639 gdouble
640 adg_canvas_get_right_margin(AdgCanvas *canvas)
642 AdgCanvasPrivate *data;
644 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
646 data = canvas->data;
647 return data->right_margin;
652 * adg_canvas_set_bottom_margin:
653 * @canvas: an #AdgCanvas
654 * @value: the new margin, in global space
656 * Changes the bottom margin of @canvas by setting #AdgCanvas:bottom-margin
657 * to @value. Negative values are allowed.
659 void
660 adg_canvas_set_bottom_margin(AdgCanvas *canvas, gdouble value)
662 g_return_if_fail(ADG_IS_CANVAS(canvas));
663 g_object_set(canvas, "bottom-margin", value, NULL);
667 * adg_canvas_get_bottom_margin:
668 * @canvas: an #AdgCanvas
670 * Gets the bottom margin (in global space) of @canvas.
672 * Returns: the requested margin or %0 on error
674 gdouble
675 adg_canvas_get_bottom_margin(AdgCanvas *canvas)
677 AdgCanvasPrivate *data;
679 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
681 data = canvas->data;
682 return data->bottom_margin;
686 * adg_canvas_set_left_margin:
687 * @canvas: an #AdgCanvas
688 * @value: the new margin, in global space
690 * Changes the left margin of @canvas by setting #AdgCanvas:left-margin
691 * to @value. Negative values are allowed.
693 void
694 adg_canvas_set_left_margin(AdgCanvas *canvas, gdouble value)
696 g_return_if_fail(ADG_IS_CANVAS(canvas));
697 g_object_set(canvas, "left-margin", value, NULL);
701 * adg_canvas_get_left_margin:
702 * @canvas: an #AdgCanvas
704 * Gets the left margin (in global space) of @canvas.
706 * Returns: the requested margin or %0 on error
708 gdouble
709 adg_canvas_get_left_margin(AdgCanvas *canvas)
711 AdgCanvasPrivate *data;
713 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
715 data = canvas->data;
716 return data->left_margin;
720 * adg_canvas_set_margins:
721 * @canvas: an #AdgCanvas
722 * @top: top margin, in global space
723 * @right: right margin, in global space
724 * @bottom: bottom margin, in global space
725 * @left: left margin, in global space
727 * Convenient function to set all the margins at once.
729 void
730 adg_canvas_set_margins(AdgCanvas *canvas, gdouble top, gdouble right,
731 gdouble bottom, gdouble left)
733 g_return_if_fail(ADG_IS_CANVAS(canvas));
734 g_object_set(canvas, "top-margin", top, "right-margin", right,
735 "bottom-margin", bottom, "left-margin", left, NULL);
739 * adg_canvas_apply_margins:
740 * @canvas: an #AdgCanvas
741 * @extents: where apply the margins
743 * A convenient function to apply the margins of @canvas to the
744 * arbitrary #CpmlExtents struct @extents.
746 void
747 adg_canvas_apply_margins(AdgCanvas *canvas, CpmlExtents *extents)
749 AdgCanvasPrivate *data;
751 g_return_if_fail(ADG_IS_CANVAS(canvas));
753 data = canvas->data;
755 extents->org.x -= data->left_margin;
756 extents->org.y -= data->top_margin;
757 extents->size.x += data->left_margin + data->right_margin;
758 extents->size.y += data->top_margin + data->bottom_margin;
762 * adg_canvas_switch_frame:
763 * @canvas: an #AdgCanvas
764 * @new_state: the new flag status
766 * Sets a new status on the #AdgCanvas:has-frame property: %TRUE
767 * means a border around the canvas extents (less the margins)
768 * should be rendered.
770 void
771 adg_canvas_switch_frame(AdgCanvas *canvas, gboolean new_state)
773 g_return_if_fail(ADG_IS_CANVAS(canvas));
774 g_object_set(canvas, "has-frame", new_state, NULL);
778 * adg_canvas_has_frame:
779 * @canvas: an #AdgCanvas
781 * Gets the current status of the #AdgCanvas:has-frame property,
782 * that is whether a border around the canvas extents (less the
783 * margins) should be rendered (%TRUE) or not (%FALSE).
785 * Returns: the current status of the frame flag
787 gboolean
788 adg_canvas_has_frame(AdgCanvas *canvas)
790 AdgCanvasPrivate *data;
792 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
794 data = canvas->data;
795 return data->has_frame;
799 * adg_canvas_set_top_padding:
800 * @canvas: an #AdgCanvas
801 * @value: the new padding, in global space
803 * Changes the top padding of @canvas by setting #AdgCanvas:top-padding
804 * to @value. Negative values are allowed.
806 void
807 adg_canvas_set_top_padding(AdgCanvas *canvas, gdouble value)
809 g_return_if_fail(ADG_IS_CANVAS(canvas));
810 g_object_set(canvas, "top-padding", value, NULL);
814 * adg_canvas_get_top_padding:
815 * @canvas: an #AdgCanvas
817 * Gets the top padding (in global space) of @canvas.
819 * Returns: the requested padding or %0 on error
821 gdouble
822 adg_canvas_get_top_padding(AdgCanvas *canvas)
824 AdgCanvasPrivate *data;
826 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
828 data = canvas->data;
829 return data->top_padding;
833 * adg_canvas_set_right_padding:
834 * @canvas: an #AdgCanvas
835 * @value: the new padding, in global space
837 * Changes the right padding of @canvas by setting #AdgCanvas:right-padding
838 * to @value. Negative values are allowed.
840 void
841 adg_canvas_set_right_padding(AdgCanvas *canvas, gdouble value)
843 g_return_if_fail(ADG_IS_CANVAS(canvas));
844 g_object_set(canvas, "right-padding", value, NULL);
848 * adg_canvas_get_right_padding:
849 * @canvas: an #AdgCanvas
851 * Gets the right padding (in global space) of @canvas.
853 * Returns: the requested padding or %0 on error
855 gdouble
856 adg_canvas_get_right_padding(AdgCanvas *canvas)
858 AdgCanvasPrivate *data;
860 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
862 data = canvas->data;
863 return data->right_padding;
868 * adg_canvas_set_bottom_padding:
869 * @canvas: an #AdgCanvas
870 * @value: the new padding, in global space
872 * Changes the bottom padding of @canvas by setting #AdgCanvas:bottom-padding
873 * to @value. Negative values are allowed.
875 void
876 adg_canvas_set_bottom_padding(AdgCanvas *canvas, gdouble value)
878 g_return_if_fail(ADG_IS_CANVAS(canvas));
879 g_object_set(canvas, "bottom-padding", value, NULL);
883 * adg_canvas_get_bottom_padding:
884 * @canvas: an #AdgCanvas
886 * Gets the bottom padding (in global space) of @canvas.
888 * Returns: the requested padding or %0 on error
890 gdouble
891 adg_canvas_get_bottom_padding(AdgCanvas *canvas)
893 AdgCanvasPrivate *data;
895 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
897 data = canvas->data;
898 return data->bottom_padding;
902 * adg_canvas_set_left_padding:
903 * @canvas: an #AdgCanvas
904 * @value: the new padding, in global space
906 * Changes the left padding of @canvas by setting #AdgCanvas:left-padding
907 * to @value. Negative values are allowed.
909 void
910 adg_canvas_set_left_padding(AdgCanvas *canvas, gdouble value)
912 g_return_if_fail(ADG_IS_CANVAS(canvas));
913 g_object_set(canvas, "left-padding", value, NULL);
917 * adg_canvas_get_left_padding:
918 * @canvas: an #AdgCanvas
920 * Gets the left padding (in global space) of @canvas.
922 * Returns: the requested padding or %0 on error
924 gdouble
925 adg_canvas_get_left_padding(AdgCanvas *canvas)
927 AdgCanvasPrivate *data;
929 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
931 data = canvas->data;
932 return data->left_padding;
936 * adg_canvas_set_paddings:
937 * @canvas: an #AdgCanvas
938 * @top: top padding, in global space
939 * @right: right padding, in global space
940 * @bottom: bottom padding, in global space
941 * @left: left padding, in global space
943 * Convenient function to set all the paddings at once.
945 void
946 adg_canvas_set_paddings(AdgCanvas *canvas, gdouble top, gdouble right,
947 gdouble bottom, gdouble left)
949 g_return_if_fail(ADG_IS_CANVAS(canvas));
950 g_object_set(canvas, "top-padding", top, "right-padding", right,
951 "bottom-padding", bottom, "left-padding", left, NULL);
955 static void
956 _adg_global_changed(AdgEntity *entity)
958 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
960 if (_ADG_OLD_ENTITY_CLASS->global_changed)
961 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
963 if (data->title_block)
964 adg_entity_global_changed((AdgEntity *) data->title_block);
967 static void
968 _adg_local_changed(AdgEntity *entity)
970 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
972 if (_ADG_OLD_ENTITY_CLASS->local_changed)
973 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
975 if (data->title_block)
976 adg_entity_local_changed((AdgEntity *) data->title_block);
979 static void
980 _adg_invalidate(AdgEntity *entity)
982 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
984 if (_ADG_OLD_ENTITY_CLASS->invalidate)
985 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
987 if (data->title_block)
988 adg_entity_invalidate((AdgEntity *) data->title_block);
991 static void
992 _adg_arrange(AdgEntity *entity)
994 AdgCanvasPrivate *data;
995 CpmlExtents extents;
997 if (_ADG_OLD_ENTITY_CLASS->arrange)
998 _ADG_OLD_ENTITY_CLASS->arrange(entity);
1000 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
1002 /* The extents should be defined, otherwise there is no drawing */
1003 g_return_if_fail(extents.is_defined);
1005 data = ((AdgCanvas *) entity)->data;
1007 if (data->size.x > 0 || data->size.y > 0) {
1008 const AdgMatrix *global = adg_entity_get_global_matrix(entity);
1009 CpmlExtents paper;
1011 paper.org.x = 0;
1012 paper.org.y = 0;
1013 paper.size.x = data->size.x;
1014 paper.size.y = data->size.y;
1016 cairo_matrix_transform_point(global, &paper.org.x, &paper.org.y);
1017 cairo_matrix_transform_distance(global, &paper.size.x, &paper.size.y);
1019 if (data->size.x > 0) {
1020 extents.org.x = paper.org.x;
1021 extents.size.x = paper.size.x;
1023 if (data->size.y > 0) {
1024 extents.org.y = paper.org.y;
1025 extents.size.y = paper.size.y;
1029 if (data->size.x == 0) {
1030 extents.org.x -= data->left_padding;
1031 extents.size.x += data->left_padding + data->right_padding;
1033 if (data->size.y == 0) {
1034 extents.org.y -= data->top_padding;
1035 extents.size.y += data->top_padding + data->bottom_padding;
1038 /* Impose the new extents */
1039 adg_entity_set_extents(entity, &extents);
1041 if (data->title_block) {
1042 AdgEntity *title_block_entity;
1043 const CpmlExtents *title_block_extents;
1044 AdgPair shift;
1046 title_block_entity = (AdgEntity *) data->title_block;
1047 adg_entity_arrange(title_block_entity);
1048 title_block_extents = adg_entity_get_extents(title_block_entity);
1050 shift.x = extents.org.x + extents.size.x - title_block_extents->org.x
1051 - title_block_extents->size.x;
1052 shift.y = extents.org.y + extents.size.y - title_block_extents->org.y
1053 - title_block_extents->size.y;
1055 /* The following block could be optimized by skipping tiny shift,
1056 * usually left by rounding errors */
1057 if (shift.x != 0 || shift.y != 0) {
1058 AdgMatrix unglobal, map;
1059 adg_matrix_copy(&unglobal, adg_entity_get_global_matrix(entity));
1060 cairo_matrix_invert(&unglobal);
1062 cairo_matrix_transform_distance(&unglobal, &shift.x, &shift.y);
1063 cairo_matrix_init_translate(&map, shift.x, shift.y);
1064 adg_entity_transform_global_map(title_block_entity, &map,
1065 ADG_TRANSFORM_AFTER);
1067 adg_entity_global_changed(title_block_entity);
1068 adg_entity_arrange(title_block_entity);
1073 static void
1074 _adg_render(AdgEntity *entity, cairo_t *cr)
1076 AdgCanvasPrivate *data;
1077 const CpmlExtents *extents;
1079 data = ((AdgCanvas *) entity)->data;
1080 extents = adg_entity_get_extents(entity);
1082 cairo_save(cr);
1084 /* Background fill */
1085 cairo_rectangle(cr, extents->org.x - data->left_margin,
1086 extents->org.y - data->top_margin,
1087 extents->size.x + data->left_margin + data->right_margin,
1088 extents->size.y + data->top_margin + data->bottom_margin);
1089 adg_entity_apply_dress(entity, data->background_dress, cr);
1090 cairo_fill(cr);
1092 /* Frame line */
1093 if (data->has_frame) {
1094 cairo_rectangle(cr, extents->org.x, extents->org.y,
1095 extents->size.x, extents->size.y);
1096 cairo_transform(cr, adg_entity_get_global_matrix(entity));
1097 adg_entity_apply_dress(entity, data->frame_dress, cr);
1098 cairo_stroke(cr);
1101 cairo_restore(cr);
1103 if (data->title_block)
1104 adg_entity_render((AdgEntity *) data->title_block, cr);
1106 if (_ADG_OLD_ENTITY_CLASS->render)
1107 _ADG_OLD_ENTITY_CLASS->render(entity, cr);