[ADG] Avoid to forcibly set the CTM on the cr
[adg.git] / src / adg / adg-canvas.c
blobd3bfce35372937231729d5eac4bd3b612ef96a38
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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-canvas.h"
69 #include "adg-canvas-private.h"
70 #include "adg-dress-builtins.h"
71 #include "adg-color-style.h"
73 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_canvas_parent_class)
74 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_canvas_parent_class)
77 G_DEFINE_TYPE(AdgCanvas, adg_canvas, ADG_TYPE_CONTAINER);
79 enum {
80 PROP_0,
81 PROP_SIZE,
82 PROP_BACKGROUND_DRESS,
83 PROP_FRAME_DRESS,
84 PROP_TITLE_BLOCK,
85 PROP_TOP_MARGIN,
86 PROP_RIGHT_MARGIN,
87 PROP_BOTTOM_MARGIN,
88 PROP_LEFT_MARGIN,
89 PROP_HAS_FRAME,
90 PROP_TOP_PADDING,
91 PROP_RIGHT_PADDING,
92 PROP_BOTTOM_PADDING,
93 PROP_LEFT_PADDING
97 static void _adg_dispose (GObject *object);
98 static void _adg_get_property (GObject *object,
99 guint param_id,
100 GValue *value,
101 GParamSpec *pspec);
102 static void _adg_set_property (GObject *object,
103 guint param_id,
104 const GValue *value,
105 GParamSpec *pspec);
106 static void _adg_global_changed (AdgEntity *entity);
107 static void _adg_local_changed (AdgEntity *entity);
108 static void _adg_invalidate (AdgEntity *entity);
109 static void _adg_arrange (AdgEntity *entity);
110 static void _adg_render (AdgEntity *entity,
111 cairo_t *cr);
114 static void
115 adg_canvas_class_init(AdgCanvasClass *klass)
117 GObjectClass *gobject_class;
118 AdgEntityClass *entity_class;
119 GParamSpec *param;
121 gobject_class = (GObjectClass *) klass;
122 entity_class = (AdgEntityClass *) klass;
124 g_type_class_add_private(klass, sizeof(AdgCanvasPrivate));
126 gobject_class->dispose = _adg_dispose;
127 gobject_class->get_property = _adg_get_property;
128 gobject_class->set_property = _adg_set_property;
130 entity_class->global_changed = _adg_global_changed;
131 entity_class->local_changed = _adg_local_changed;
132 entity_class->invalidate = _adg_invalidate;
133 entity_class->arrange = _adg_arrange;
134 entity_class->render = _adg_render;
136 param = g_param_spec_boxed("size",
137 P_("Canvas Size"),
138 P_("The size set on this canvas: use 0 to have an automatic dimension based on the canvas extents"),
139 ADG_TYPE_PAIR,
140 G_PARAM_READWRITE);
141 g_object_class_install_property(gobject_class, PROP_SIZE, param);
143 param = adg_param_spec_dress("background-dress",
144 P_("Background Dress"),
145 P_("The color dress to use for the canvas background"),
146 ADG_DRESS_COLOR_BACKGROUND,
147 G_PARAM_READWRITE);
148 g_object_class_install_property(gobject_class, PROP_BACKGROUND_DRESS, param);
150 param = adg_param_spec_dress("frame-dress",
151 P_("Frame Dress"),
152 P_("Line dress to use while drawing the frame around the canvas"),
153 ADG_DRESS_LINE_FRAME,
154 G_PARAM_READWRITE);
155 g_object_class_install_property(gobject_class, PROP_FRAME_DRESS, param);
157 param = g_param_spec_object("title-block",
158 P_("Title Block"),
159 P_("The title block to assign to this canvas"),
160 ADG_TYPE_TITLE_BLOCK,
161 G_PARAM_READWRITE);
162 g_object_class_install_property(gobject_class, PROP_TITLE_BLOCK, param);
164 param = g_param_spec_double("top-margin",
165 P_("Top Margin"),
166 P_("The margin (in identity space) to leave above the frame"),
167 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
168 G_PARAM_READWRITE);
169 g_object_class_install_property(gobject_class, PROP_TOP_MARGIN, param);
171 param = g_param_spec_double("right-margin",
172 P_("Right Margin"),
173 P_("The margin (in identity space) to leave empty at the right of the frame"),
174 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
175 G_PARAM_READWRITE);
176 g_object_class_install_property(gobject_class, PROP_RIGHT_MARGIN, param);
178 param = g_param_spec_double("bottom-margin",
179 P_("Bottom Margin"),
180 P_("The margin (in identity space) to leave empty below the frame"),
181 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
182 G_PARAM_READWRITE);
183 g_object_class_install_property(gobject_class, PROP_BOTTOM_MARGIN, param);
185 param = g_param_spec_double("left-margin",
186 P_("Left Margin"),
187 P_("The margin (in identity space) to leave empty at the left of the frame"),
188 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
189 G_PARAM_READWRITE);
190 g_object_class_install_property(gobject_class, PROP_LEFT_MARGIN, param);
192 param = g_param_spec_boolean("has-frame",
193 P_("Has Frame Flag"),
194 P_("If enabled, a frame using the frame dress will be drawn around the canvas extents, taking into account the margins"),
195 TRUE,
196 G_PARAM_READWRITE);
197 g_object_class_install_property(gobject_class, PROP_HAS_FRAME, param);
199 param = g_param_spec_double("top-padding",
200 P_("Top Padding"),
201 P_("The padding (in identity space) to leave empty above between the drawing and the frame"),
202 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
203 G_PARAM_READWRITE);
204 g_object_class_install_property(gobject_class, PROP_TOP_PADDING, param);
206 param = g_param_spec_double("right-padding",
207 P_("Right Padding"),
208 P_("The padding (in identity space) to leave empty at the right 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_RIGHT_PADDING, param);
213 param = g_param_spec_double("bottom-padding",
214 P_("Bottom Padding"),
215 P_("The padding (in identity space) to leave empty below 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_BOTTOM_PADDING, param);
220 param = g_param_spec_double("left-padding",
221 P_("Left Padding"),
222 P_("The padding (in identity space) to leave empty at the left 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_LEFT_PADDING, param);
228 static void
229 adg_canvas_init(AdgCanvas *canvas)
231 AdgCanvasPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(canvas,
232 ADG_TYPE_CANVAS,
233 AdgCanvasPrivate);
235 data->size.x = 0;
236 data->size.y = 0;
237 data->background_dress = ADG_DRESS_COLOR_BACKGROUND;
238 data->frame_dress = ADG_DRESS_LINE_FRAME;
239 data->title_block = NULL;
240 data->top_margin = 15;
241 data->right_margin = 15;
242 data->bottom_margin = 15;
243 data->left_margin = 15;
244 data->has_frame = TRUE;
245 data->top_padding = 15;
246 data->right_padding = 15;
247 data->bottom_padding = 15;
248 data->left_padding = 15;
250 canvas->data = data;
253 static void
254 _adg_dispose(GObject *object)
256 AdgCanvasPrivate *data = ((AdgCanvas *) object)->data;
258 if (data->title_block) {
259 g_object_unref(data->title_block);
260 data->title_block = NULL;
263 if (_ADG_OLD_OBJECT_CLASS->dispose)
264 _ADG_OLD_OBJECT_CLASS->dispose(object);
268 static void
269 _adg_get_property(GObject *object, guint prop_id,
270 GValue *value, GParamSpec *pspec)
272 AdgCanvasPrivate *data = ((AdgCanvas *) object)->data;
274 switch (prop_id) {
275 case PROP_SIZE:
276 g_value_set_boxed(value, &data->size);
277 break;
278 case PROP_BACKGROUND_DRESS:
279 g_value_set_int(value, data->background_dress);
280 break;
281 case PROP_FRAME_DRESS:
282 g_value_set_int(value, data->frame_dress);
283 break;
284 case PROP_TITLE_BLOCK:
285 g_value_set_object(value, data->title_block);
286 break;
287 case PROP_TOP_MARGIN:
288 g_value_set_double(value, data->top_margin);
289 break;
290 case PROP_RIGHT_MARGIN:
291 g_value_set_double(value, data->right_margin);
292 break;
293 case PROP_BOTTOM_MARGIN:
294 g_value_set_double(value, data->bottom_margin);
295 break;
296 case PROP_LEFT_MARGIN:
297 g_value_set_double(value, data->left_margin);
298 break;
299 case PROP_HAS_FRAME:
300 g_value_set_boolean(value, data->has_frame);
301 break;
302 case PROP_TOP_PADDING:
303 g_value_set_double(value, data->top_padding);
304 break;
305 case PROP_RIGHT_PADDING:
306 g_value_set_double(value, data->right_padding);
307 break;
308 case PROP_BOTTOM_PADDING:
309 g_value_set_double(value, data->bottom_padding);
310 break;
311 case PROP_LEFT_PADDING:
312 g_value_set_double(value, data->left_padding);
313 break;
314 default:
315 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
316 break;
320 static void
321 _adg_set_property(GObject *object, guint prop_id,
322 const GValue *value, GParamSpec *pspec)
324 AdgCanvas *canvas;
325 AdgCanvasPrivate *data;
326 AdgTitleBlock *title_block;
328 canvas = (AdgCanvas *) object;
329 data = canvas->data;
331 switch (prop_id) {
332 case PROP_SIZE:
333 adg_pair_copy(&data->size, g_value_get_boxed(value));
334 break;
335 case PROP_BACKGROUND_DRESS:
336 data->background_dress = g_value_get_int(value);
337 break;
338 case PROP_FRAME_DRESS:
339 data->frame_dress = g_value_get_int(value);
340 break;
341 case PROP_TITLE_BLOCK:
342 title_block = g_value_get_object(value);
343 if (title_block) {
344 g_object_ref(title_block);
345 adg_entity_set_parent((AdgEntity *) title_block,
346 (AdgEntity *) canvas);
348 if (data->title_block)
349 g_object_unref(data->title_block);
350 data->title_block = title_block;
351 break;
352 case PROP_TOP_MARGIN:
353 data->top_margin = g_value_get_double(value);
354 break;
355 case PROP_RIGHT_MARGIN:
356 data->right_margin = g_value_get_double(value);
357 break;
358 case PROP_BOTTOM_MARGIN:
359 data->bottom_margin = g_value_get_double(value);
360 break;
361 case PROP_LEFT_MARGIN:
362 data->left_margin = g_value_get_double(value);
363 break;
364 case PROP_HAS_FRAME:
365 data->has_frame = g_value_get_boolean(value);
366 break;
367 case PROP_TOP_PADDING:
368 data->top_padding = g_value_get_double(value);
369 break;
370 case PROP_RIGHT_PADDING:
371 data->right_padding = g_value_get_double(value);
372 break;
373 case PROP_BOTTOM_PADDING:
374 data->bottom_padding = g_value_get_double(value);
375 break;
376 case PROP_LEFT_PADDING:
377 data->left_padding = g_value_get_double(value);
378 break;
379 default:
380 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
381 break;
387 * adg_canvas_new:
389 * Creates a new empty canvas object.
391 * Returns: the canvas
393 AdgCanvas *
394 adg_canvas_new(void)
396 return g_object_new(ADG_TYPE_CANVAS, NULL);
400 * adg_canvas_set_size:
401 * @canvas: an #AdgCanvas
402 * @size: the new size for the canvas
404 * Sets a specific size on @canvas. The x and/or y
405 * components of the returned #AdgPair could be %0, in which
406 * case the size returned by adg_entity_get_extents() on
407 * @canvas will be used instead.
409 void
410 adg_canvas_set_size(AdgCanvas *canvas, const AdgPair *size)
412 g_return_if_fail(ADG_IS_CANVAS(canvas));
413 g_return_if_fail(size != NULL);
415 g_object_set((GObject *) canvas, "size", size, NULL);
419 * adg_canvas_set_size_explicit:
420 * @canvas: an #AdgCanvas
421 * @x: the new width of the canvas or %0 to reset
422 * @y: the new height of the canvas or %0 to reset
424 * A convenient function to set the size of @canvas using
425 * explicit coordinates. Check adg_canvas_set_size() for
426 * further details.
428 void
429 adg_canvas_set_size_explicit(AdgCanvas *canvas, gdouble x, gdouble y)
431 AdgPair size;
433 size.x = x;
434 size.y = y;
436 adg_canvas_set_size(canvas, &size);
440 * adg_canvas_get_size:
441 * @canvas: an #AdgCanvas
443 * Gets the specific size set on @canvas. The x and/or y
444 * components of the returned #AdgPair could be %0, in which
445 * case the size returned by adg_entity_get_extents() on
446 * @canvas will be used instead.
448 * Returns: the explicit size set on this canvas or %NULL on errors
450 const AdgPair *
451 adg_canvas_get_size(AdgCanvas *canvas)
453 AdgCanvasPrivate *data;
455 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
457 data = canvas->data;
458 return &data->size;
462 * adg_canvas_set_background_dress:
463 * @canvas: an #AdgCanvas
464 * @dress: the new #AdgDress to use
466 * Sets a new background dress for rendering @canvas: the new
467 * dress must be a color dress.
469 void
470 adg_canvas_set_background_dress(AdgCanvas *canvas, AdgDress dress)
472 g_return_if_fail(ADG_IS_CANVAS(canvas));
473 g_object_set((GObject *) canvas, "background-dress", dress, NULL);
477 * adg_canvas_get_background_dress:
478 * @canvas: an #AdgCanvas
480 * Gets the background dress to be used in rendering @canvas.
482 * Returns: the current background dress
484 AdgDress
485 adg_canvas_get_background_dress(AdgCanvas *canvas)
487 AdgCanvasPrivate *data;
489 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
491 data = canvas->data;
493 return data->background_dress;
497 * adg_canvas_set_frame_dress:
498 * @canvas: an #AdgCanvas
499 * @dress: the new #AdgDress to use
501 * Sets the #AdgCanvas:frame-dress property of @canvas to @dress:
502 * the new dress must be a line dress.
504 void
505 adg_canvas_set_frame_dress(AdgCanvas *canvas, AdgDress dress)
507 g_return_if_fail(ADG_IS_CANVAS(canvas));
508 g_object_set((GObject *) canvas, "frame-dress", dress, NULL);
512 * adg_canvas_get_frame_dress:
513 * @canvas: an #AdgCanvas
515 * Gets the frame dress to be used in rendering the border of @canvas.
517 * Returns: the current frame dress
519 AdgDress
520 adg_canvas_get_frame_dress(AdgCanvas *canvas)
522 AdgCanvasPrivate *data;
524 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
526 data = canvas->data;
527 return data->frame_dress;
531 * adg_canvas_set_title_block:
532 * @canvas: an #AdgCanvas
533 * @title_block: a title block
535 * Sets the #AdgCanvas:title-block property of @canvas to @title_block.
537 * Although a title block entity could be added to @canvas in the usual
538 * way, that is using the adg_container_add() method, assigning a title
539 * block with adg_canvas_set_title_block() is somewhat different:
541 * - @title_block will be automatically attached to the bottom right
542 * corner of to the @canvas frame (this could be accomplished in the
543 * usual way too, by resetting the right and bottom paddings);
544 * - the @title_block boundary box is not taken into account while
545 * computing the extents of @canvas.
547 void
548 adg_canvas_set_title_block(AdgCanvas *canvas, AdgTitleBlock *title_block)
550 g_return_if_fail(ADG_IS_CANVAS(canvas));
551 g_return_if_fail(title_block == NULL || ADG_IS_TITLE_BLOCK(title_block));
552 g_object_set((GObject *) canvas, "title-block", title_block, NULL);
556 * adg_canvas_get_title_block:
557 * @canvas: an #AdgCanvas
559 * Gets the #AdgTitleBlock object of @canvas: check
560 * adg_canvas_set_title_block() for details.
562 * Returns: the title block object or %NULL
564 AdgTitleBlock *
565 adg_canvas_get_title_block(AdgCanvas *canvas)
567 AdgCanvasPrivate *data;
569 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
571 data = canvas->data;
572 return data->title_block;
576 * adg_canvas_set_top_margin:
577 * @canvas: an #AdgCanvas
578 * @value: the new margin, in identity space
580 * Changes the top margin of @canvas by setting #AdgCanvas:top-margin
581 * to @value. Negative values are allowed.
583 void
584 adg_canvas_set_top_margin(AdgCanvas *canvas, gdouble value)
586 g_return_if_fail(ADG_IS_CANVAS(canvas));
587 g_object_set((GObject *) canvas, "top-margin", value, NULL);
591 * adg_canvas_get_top_margin:
592 * @canvas: an #AdgCanvas
594 * Gets the top margin (in identity space) of @canvas.
596 * Returns: the requested margin or %0 on error
598 gdouble
599 adg_canvas_get_top_margin(AdgCanvas *canvas)
601 AdgCanvasPrivate *data;
603 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
605 data = canvas->data;
606 return data->top_margin;
610 * adg_canvas_set_right_margin:
611 * @canvas: an #AdgCanvas
612 * @value: the new margin, in identity space
614 * Changes the right margin of @canvas by setting #AdgCanvas:right-margin
615 * to @value. Negative values are allowed.
617 void
618 adg_canvas_set_right_margin(AdgCanvas *canvas, gdouble value)
620 g_return_if_fail(ADG_IS_CANVAS(canvas));
621 g_object_set((GObject *) canvas, "right-margin", value, NULL);
625 * adg_canvas_get_right_margin:
626 * @canvas: an #AdgCanvas
628 * Gets the right margin (in identity space) of @canvas.
630 * Returns: the requested margin or %0 on error
632 gdouble
633 adg_canvas_get_right_margin(AdgCanvas *canvas)
635 AdgCanvasPrivate *data;
637 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
639 data = canvas->data;
640 return data->right_margin;
645 * adg_canvas_set_bottom_margin:
646 * @canvas: an #AdgCanvas
647 * @value: the new margin, in identity space
649 * Changes the bottom margin of @canvas by setting #AdgCanvas:bottom-margin
650 * to @value. Negative values are allowed.
652 void
653 adg_canvas_set_bottom_margin(AdgCanvas *canvas, gdouble value)
655 g_return_if_fail(ADG_IS_CANVAS(canvas));
656 g_object_set((GObject *) canvas, "bottom-margin", value, NULL);
660 * adg_canvas_get_bottom_margin:
661 * @canvas: an #AdgCanvas
663 * Gets the bottom margin (in identity space) of @canvas.
665 * Returns: the requested margin or %0 on error
667 gdouble
668 adg_canvas_get_bottom_margin(AdgCanvas *canvas)
670 AdgCanvasPrivate *data;
672 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
674 data = canvas->data;
675 return data->bottom_margin;
679 * adg_canvas_set_left_margin:
680 * @canvas: an #AdgCanvas
681 * @value: the new margin, in identity space
683 * Changes the left margin of @canvas by setting #AdgCanvas:left-margin
684 * to @value. Negative values are allowed.
686 void
687 adg_canvas_set_left_margin(AdgCanvas *canvas, gdouble value)
689 g_return_if_fail(ADG_IS_CANVAS(canvas));
690 g_object_set((GObject *) canvas, "left-margin", value, NULL);
694 * adg_canvas_get_left_margin:
695 * @canvas: an #AdgCanvas
697 * Gets the left margin (in identity space) of @canvas.
699 * Returns: the requested margin or %0 on error
701 gdouble
702 adg_canvas_get_left_margin(AdgCanvas *canvas)
704 AdgCanvasPrivate *data;
706 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
708 data = canvas->data;
709 return data->left_margin;
713 * adg_canvas_set_margins:
714 * @canvas: an #AdgCanvas
715 * @top: top margin, in identity space
716 * @right: right margin, in identity space
717 * @bottom: bottom margin, in identity space
718 * @left: left margin, in identity space
720 * Convenient function to set all the margins at once.
722 void
723 adg_canvas_set_margins(AdgCanvas *canvas, gdouble top, gdouble right,
724 gdouble bottom, gdouble left)
726 g_return_if_fail(ADG_IS_CANVAS(canvas));
727 g_object_set((GObject *) canvas,
728 "top-margin", top,
729 "right-margin", right,
730 "bottom-margin", bottom,
731 "left-margin", left,
732 NULL);
736 * adg_canvas_switch_frame:
737 * @canvas: an #AdgCanvas
738 * @new_state: the new flag status
740 * Sets a new status on the #AdgCanvas:has-frame property: %TRUE
741 * means a border around the canvas extents (less the margins)
742 * should be rendered.
744 void
745 adg_canvas_switch_frame(AdgCanvas *canvas, gboolean new_state)
747 g_return_if_fail(ADG_IS_CANVAS(canvas));
748 g_object_set((GObject *) canvas, "has-frame", new_state, NULL);
752 * adg_canvas_has_frame:
753 * @canvas: an #AdgCanvas
755 * Gets the current status of the #AdgCanvas:has-frame property,
756 * that is whether a border around the canvas extents (less the
757 * margins) should be rendered (%TRUE) or not (%FALSE).
759 * Returns: the current status of the frame flag
761 gboolean
762 adg_canvas_has_frame(AdgCanvas *canvas)
764 AdgCanvasPrivate *data;
766 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
768 data = canvas->data;
769 return data->has_frame;
773 * adg_canvas_set_top_padding:
774 * @canvas: an #AdgCanvas
775 * @value: the new padding, in identity space
777 * Changes the top padding of @canvas by setting #AdgCanvas:top-padding
778 * to @value. Negative values are allowed.
780 void
781 adg_canvas_set_top_padding(AdgCanvas *canvas, gdouble value)
783 g_return_if_fail(ADG_IS_CANVAS(canvas));
784 g_object_set((GObject *) canvas, "top-padding", value, NULL);
788 * adg_canvas_get_top_padding:
789 * @canvas: an #AdgCanvas
791 * Gets the top padding (in identity space) of @canvas.
793 * Returns: the requested padding or %0 on error
795 gdouble
796 adg_canvas_get_top_padding(AdgCanvas *canvas)
798 AdgCanvasPrivate *data;
800 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
802 data = canvas->data;
803 return data->top_padding;
807 * adg_canvas_set_right_padding:
808 * @canvas: an #AdgCanvas
809 * @value: the new padding, in identity space
811 * Changes the right padding of @canvas by setting #AdgCanvas:right-padding
812 * to @value. Negative values are allowed.
814 void
815 adg_canvas_set_right_padding(AdgCanvas *canvas, gdouble value)
817 g_return_if_fail(ADG_IS_CANVAS(canvas));
818 g_object_set((GObject *) canvas, "right-padding", value, NULL);
822 * adg_canvas_get_right_padding:
823 * @canvas: an #AdgCanvas
825 * Gets the right padding (in identity space) of @canvas.
827 * Returns: the requested padding or %0 on error
829 gdouble
830 adg_canvas_get_right_padding(AdgCanvas *canvas)
832 AdgCanvasPrivate *data;
834 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
836 data = canvas->data;
837 return data->right_padding;
842 * adg_canvas_set_bottom_padding:
843 * @canvas: an #AdgCanvas
844 * @value: the new padding, in identity space
846 * Changes the bottom padding of @canvas by setting #AdgCanvas:bottom-padding
847 * to @value. Negative values are allowed.
849 void
850 adg_canvas_set_bottom_padding(AdgCanvas *canvas, gdouble value)
852 g_return_if_fail(ADG_IS_CANVAS(canvas));
853 g_object_set((GObject *) canvas, "bottom-padding", value, NULL);
857 * adg_canvas_get_bottom_padding:
858 * @canvas: an #AdgCanvas
860 * Gets the bottom padding (in identity space) of @canvas.
862 * Returns: the requested padding or %0 on error
864 gdouble
865 adg_canvas_get_bottom_padding(AdgCanvas *canvas)
867 AdgCanvasPrivate *data;
869 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
871 data = canvas->data;
872 return data->bottom_padding;
876 * adg_canvas_set_left_padding:
877 * @canvas: an #AdgCanvas
878 * @value: the new padding, in identity space
880 * Changes the left padding of @canvas by setting #AdgCanvas:left-padding
881 * to @value. Negative values are allowed.
883 void
884 adg_canvas_set_left_padding(AdgCanvas *canvas, gdouble value)
886 g_return_if_fail(ADG_IS_CANVAS(canvas));
887 g_object_set((GObject *) canvas, "left-padding", value, NULL);
891 * adg_canvas_get_left_padding:
892 * @canvas: an #AdgCanvas
894 * Gets the left padding (in identity space) of @canvas.
896 * Returns: the requested padding or %0 on error
898 gdouble
899 adg_canvas_get_left_padding(AdgCanvas *canvas)
901 AdgCanvasPrivate *data;
903 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
905 data = canvas->data;
906 return data->left_padding;
910 * adg_canvas_set_paddings:
911 * @canvas: an #AdgCanvas
912 * @top: top padding, in identity space
913 * @right: right padding, in identity space
914 * @bottom: bottom padding, in identity space
915 * @left: left padding, in identity space
917 * Convenient function to set all the paddings at once.
919 void
920 adg_canvas_set_paddings(AdgCanvas *canvas, gdouble top, gdouble right,
921 gdouble bottom, gdouble left)
923 g_return_if_fail(ADG_IS_CANVAS(canvas));
924 g_object_set((GObject *) canvas,
925 "top-padding", top,
926 "right-padding", right,
927 "bottom-padding", bottom,
928 "left-padding", left,
929 NULL);
933 static void
934 _adg_global_changed(AdgEntity *entity)
936 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
938 if (_ADG_OLD_ENTITY_CLASS->global_changed)
939 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
941 if (data->title_block)
942 adg_entity_global_changed((AdgEntity *) data->title_block);
945 static void
946 _adg_local_changed(AdgEntity *entity)
948 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
950 if (_ADG_OLD_ENTITY_CLASS->local_changed)
951 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
953 if (data->title_block)
954 adg_entity_local_changed((AdgEntity *) data->title_block);
957 static void
958 _adg_invalidate(AdgEntity *entity)
960 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
962 if (_ADG_OLD_ENTITY_CLASS->invalidate)
963 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
965 if (data->title_block)
966 adg_entity_invalidate((AdgEntity *) data->title_block);
969 static void
970 _adg_arrange(AdgEntity *entity)
972 AdgCanvasPrivate *data;
973 CpmlExtents extents;
975 if (_ADG_OLD_ENTITY_CLASS->arrange)
976 _ADG_OLD_ENTITY_CLASS->arrange(entity);
978 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
980 /* The extents should be defined, otherwise there is no drawing */
981 g_return_if_fail(extents.is_defined);
983 data = ((AdgCanvas *) entity)->data;
985 if (data->size.x > 0 || data->size.y > 0) {
986 const AdgMatrix *global = adg_entity_get_global_matrix(entity);
987 CpmlExtents paper;
989 paper.org.x = data->left_margin;
990 paper.org.y = data->top_margin;
991 paper.size.x = data->size.x;
992 paper.size.y = data->size.y;
994 cairo_matrix_transform_point(global, &paper.org.x, &paper.org.y);
995 cairo_matrix_transform_distance(global, &paper.size.x, &paper.size.y);
997 if (data->size.x > 0) {
998 extents.org.x = paper.org.x;
999 extents.size.x = paper.size.x;
1001 if (data->size.y > 0) {
1002 extents.org.y = paper.org.y;
1003 extents.size.y = paper.size.y;
1007 if (data->size.x == 0) {
1008 extents.org.x -= data->left_padding;
1009 extents.size.x += data->left_padding + data->right_padding;
1011 if (data->size.y == 0) {
1012 extents.org.y -= data->top_padding;
1013 extents.size.y += data->top_padding + data->bottom_padding;
1016 /* Add margins */
1017 extents.org.x -= data->left_margin;
1018 extents.org.y -= data->top_margin;
1019 extents.size.x += data->left_margin + data->right_margin;
1020 extents.size.y += data->top_margin + data->bottom_margin;
1022 /* Impose the new extents */
1023 adg_entity_set_extents(entity, &extents);
1025 if (data->title_block) {
1026 AdgEntity *title_block_entity;
1027 const CpmlExtents *title_block_extents;
1028 AdgPair shift;
1030 title_block_entity = (AdgEntity *) data->title_block;
1031 adg_entity_arrange(title_block_entity);
1032 title_block_extents = adg_entity_get_extents(title_block_entity);
1034 shift.x = extents.org.x + extents.size.x - title_block_extents->org.x
1035 - title_block_extents->size.x - data->right_margin;
1036 shift.y = extents.org.y + extents.size.y - title_block_extents->org.y
1037 - title_block_extents->size.y - data->bottom_margin;
1039 /* The following block could be optimized by skipping tiny shift,
1040 * usually left by rounding errors */
1041 if (shift.x != 0 || shift.y != 0) {
1042 AdgMatrix unglobal, map;
1043 adg_matrix_copy(&unglobal, adg_entity_get_global_matrix(entity));
1044 cairo_matrix_invert(&unglobal);
1046 cairo_matrix_transform_distance(&unglobal, &shift.x, &shift.y);
1047 cairo_matrix_init_translate(&map, shift.x, shift.y);
1048 adg_entity_transform_global_map(title_block_entity, &map,
1049 ADG_TRANSFORM_AFTER);
1051 adg_entity_global_changed(title_block_entity);
1052 adg_entity_arrange(title_block_entity);
1057 static void
1058 _adg_render(AdgEntity *entity, cairo_t *cr)
1060 AdgCanvasPrivate *data;
1061 const CpmlExtents *extents;
1063 data = ((AdgCanvas *) entity)->data;
1064 extents = adg_entity_get_extents(entity);
1066 cairo_save(cr);
1068 /* Background fill */
1069 cairo_rectangle(cr, extents->org.x, extents->org.y,
1070 extents->size.x, extents->size.y);
1071 adg_entity_apply_dress(entity, data->background_dress, cr);
1072 cairo_fill(cr);
1074 /* Frame line */
1075 if (data->has_frame) {
1076 CpmlExtents frame;
1077 cpml_extents_copy(&frame, extents);
1079 frame.org.x += data->left_margin;
1080 frame.org.y += data->top_margin;
1081 frame.size.x -= data->left_margin + data->right_margin;
1082 frame.size.y -= data->top_margin + data->bottom_margin;
1084 cairo_rectangle(cr, frame.org.x, frame.org.y,
1085 frame.size.x, frame.size.y);
1086 cairo_transform(cr, adg_entity_get_global_matrix(entity));
1087 adg_entity_apply_dress(entity, data->frame_dress, cr);
1088 cairo_stroke(cr);
1091 cairo_restore(cr);
1093 if (data->title_block)
1094 adg_entity_render((AdgEntity *) data->title_block, cr);
1096 if (_ADG_OLD_ENTITY_CLASS->render)
1097 _ADG_OLD_ENTITY_CLASS->render(entity, cr);