1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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.
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
64 * All fields are private and should not be used directly.
65 * Use its public methods instead.
71 #include "adg-internal.h"
72 #if GTK3_ENABLED || GTK2_ENABLED
76 #include "adg-container.h"
77 #include "adg-table.h"
78 #include "adg-title-block.h"
79 #include "adg-style.h"
80 #include "adg-color-style.h"
81 #include "adg-dress.h"
82 #include "adg-param-dress.h"
84 #include <adg-canvas.h>
85 #include "adg-canvas-private.h"
88 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_canvas_parent_class)
89 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_canvas_parent_class)
92 G_DEFINE_TYPE(AdgCanvas
, adg_canvas
, ADG_TYPE_CONTAINER
)
98 PROP_BACKGROUND_DRESS
,
113 static void _adg_dispose (GObject
*object
);
114 static void _adg_get_property (GObject
*object
,
118 static void _adg_set_property (GObject
*object
,
122 static void _adg_global_changed (AdgEntity
*entity
);
123 static void _adg_local_changed (AdgEntity
*entity
);
124 static void _adg_invalidate (AdgEntity
*entity
);
125 static void _adg_arrange (AdgEntity
*entity
);
126 static void _adg_render (AdgEntity
*entity
,
131 adg_canvas_class_init(AdgCanvasClass
*klass
)
133 GObjectClass
*gobject_class
;
134 AdgEntityClass
*entity_class
;
137 gobject_class
= (GObjectClass
*) klass
;
138 entity_class
= (AdgEntityClass
*) klass
;
140 g_type_class_add_private(klass
, sizeof(AdgCanvasPrivate
));
142 gobject_class
->dispose
= _adg_dispose
;
143 gobject_class
->get_property
= _adg_get_property
;
144 gobject_class
->set_property
= _adg_set_property
;
146 entity_class
->global_changed
= _adg_global_changed
;
147 entity_class
->local_changed
= _adg_local_changed
;
148 entity_class
->invalidate
= _adg_invalidate
;
149 entity_class
->arrange
= _adg_arrange
;
150 entity_class
->render
= _adg_render
;
152 param
= g_param_spec_boxed("size",
154 P_("The size set on this canvas: use 0 to have an automatic dimension based on the canvas extents"),
157 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
159 param
= g_param_spec_boxed("scales",
161 P_("List of scales to be tested while autoscaling the drawing"),
164 g_object_class_install_property(gobject_class
, PROP_SCALES
, param
);
166 param
= adg_param_spec_dress("background-dress",
167 P_("Background Dress"),
168 P_("The color dress to use for the canvas background"),
169 ADG_DRESS_COLOR_BACKGROUND
,
171 g_object_class_install_property(gobject_class
, PROP_BACKGROUND_DRESS
, param
);
173 param
= adg_param_spec_dress("frame-dress",
175 P_("Line dress to use while drawing the frame around the canvas"),
176 ADG_DRESS_LINE_FRAME
,
178 g_object_class_install_property(gobject_class
, PROP_FRAME_DRESS
, param
);
180 param
= g_param_spec_object("title-block",
182 P_("The title block to assign to this canvas"),
183 ADG_TYPE_TITLE_BLOCK
,
185 g_object_class_install_property(gobject_class
, PROP_TITLE_BLOCK
, param
);
187 param
= g_param_spec_double("top-margin",
189 P_("The margin (in global space) to leave above the frame"),
190 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
192 g_object_class_install_property(gobject_class
, PROP_TOP_MARGIN
, param
);
194 param
= g_param_spec_double("right-margin",
196 P_("The margin (in global space) to leave empty at the right of the frame"),
197 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
199 g_object_class_install_property(gobject_class
, PROP_RIGHT_MARGIN
, param
);
201 param
= g_param_spec_double("bottom-margin",
203 P_("The margin (in global space) to leave empty below the frame"),
204 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
206 g_object_class_install_property(gobject_class
, PROP_BOTTOM_MARGIN
, param
);
208 param
= g_param_spec_double("left-margin",
210 P_("The margin (in global space) to leave empty at the left of the frame"),
211 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
213 g_object_class_install_property(gobject_class
, PROP_LEFT_MARGIN
, param
);
215 param
= g_param_spec_boolean("has-frame",
216 P_("Has Frame Flag"),
217 P_("If enabled, a frame using the frame dress will be drawn around the canvas extents, taking into account the margins"),
220 g_object_class_install_property(gobject_class
, PROP_HAS_FRAME
, param
);
222 param
= g_param_spec_double("top-padding",
224 P_("The padding (in global space) to leave empty above between the drawing and the frame"),
225 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
227 g_object_class_install_property(gobject_class
, PROP_TOP_PADDING
, param
);
229 param
= g_param_spec_double("right-padding",
231 P_("The padding (in global space) to leave empty at the right between the drawing and the frame"),
232 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
234 g_object_class_install_property(gobject_class
, PROP_RIGHT_PADDING
, param
);
236 param
= g_param_spec_double("bottom-padding",
237 P_("Bottom Padding"),
238 P_("The padding (in global space) to leave empty below between the drawing and the frame"),
239 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
241 g_object_class_install_property(gobject_class
, PROP_BOTTOM_PADDING
, param
);
243 param
= g_param_spec_double("left-padding",
245 P_("The padding (in global space) to leave empty at the left between the drawing and the frame"),
246 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
248 g_object_class_install_property(gobject_class
, PROP_LEFT_PADDING
, param
);
252 adg_canvas_init(AdgCanvas
*canvas
)
254 AdgCanvasPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(canvas
,
257 const gchar
*scales
[] = {
258 "10:1", "5:1", "3:1", "2:1", "1:1", "1:2", "1:3", "1:5", "1:10",
265 data
->scales
= g_strdupv((gchar
**) scales
);
266 data
->background_dress
= ADG_DRESS_COLOR_BACKGROUND
;
267 data
->frame_dress
= ADG_DRESS_LINE_FRAME
;
268 data
->title_block
= NULL
;
269 data
->top_margin
= 15;
270 data
->right_margin
= 15;
271 data
->bottom_margin
= 15;
272 data
->left_margin
= 15;
273 data
->has_frame
= TRUE
;
274 data
->top_padding
= 15;
275 data
->right_padding
= 15;
276 data
->bottom_padding
= 15;
277 data
->left_padding
= 15;
283 _adg_dispose(GObject
*object
)
286 AdgCanvasPrivate
*data
;
288 canvas
= (AdgCanvas
*) object
;
291 if (data
->title_block
) {
292 g_object_unref(data
->title_block
);
293 data
->title_block
= NULL
;
296 if (data
->scales
!= NULL
) {
297 g_strfreev(data
->scales
);
301 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
302 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
307 _adg_get_property(GObject
*object
, guint prop_id
,
308 GValue
*value
, GParamSpec
*pspec
)
310 AdgCanvasPrivate
*data
= ((AdgCanvas
*) object
)->data
;
314 g_value_set_boxed(value
, &data
->size
);
317 g_value_set_boxed(value
, data
->scales
);
319 case PROP_BACKGROUND_DRESS
:
320 g_value_set_enum(value
, data
->background_dress
);
322 case PROP_FRAME_DRESS
:
323 g_value_set_enum(value
, data
->frame_dress
);
325 case PROP_TITLE_BLOCK
:
326 g_value_set_object(value
, data
->title_block
);
328 case PROP_TOP_MARGIN
:
329 g_value_set_double(value
, data
->top_margin
);
331 case PROP_RIGHT_MARGIN
:
332 g_value_set_double(value
, data
->right_margin
);
334 case PROP_BOTTOM_MARGIN
:
335 g_value_set_double(value
, data
->bottom_margin
);
337 case PROP_LEFT_MARGIN
:
338 g_value_set_double(value
, data
->left_margin
);
341 g_value_set_boolean(value
, data
->has_frame
);
343 case PROP_TOP_PADDING
:
344 g_value_set_double(value
, data
->top_padding
);
346 case PROP_RIGHT_PADDING
:
347 g_value_set_double(value
, data
->right_padding
);
349 case PROP_BOTTOM_PADDING
:
350 g_value_set_double(value
, data
->bottom_padding
);
352 case PROP_LEFT_PADDING
:
353 g_value_set_double(value
, data
->left_padding
);
356 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
362 _adg_set_property(GObject
*object
, guint prop_id
,
363 const GValue
*value
, GParamSpec
*pspec
)
366 AdgCanvasPrivate
*data
;
367 AdgTitleBlock
*title_block
;
369 canvas
= (AdgCanvas
*) object
;
374 cpml_pair_copy(&data
->size
, g_value_get_boxed(value
));
377 g_strfreev(data
->scales
);
378 data
->scales
= g_value_dup_boxed(value
);
380 case PROP_BACKGROUND_DRESS
:
381 data
->background_dress
= g_value_get_enum(value
);
383 case PROP_FRAME_DRESS
:
384 data
->frame_dress
= g_value_get_enum(value
);
386 case PROP_TITLE_BLOCK
:
387 title_block
= g_value_get_object(value
);
389 g_object_ref(title_block
);
390 adg_entity_set_parent((AdgEntity
*) title_block
,
391 (AdgEntity
*) canvas
);
393 if (data
->title_block
)
394 g_object_unref(data
->title_block
);
395 data
->title_block
= title_block
;
397 case PROP_TOP_MARGIN
:
398 data
->top_margin
= g_value_get_double(value
);
400 case PROP_RIGHT_MARGIN
:
401 data
->right_margin
= g_value_get_double(value
);
403 case PROP_BOTTOM_MARGIN
:
404 data
->bottom_margin
= g_value_get_double(value
);
406 case PROP_LEFT_MARGIN
:
407 data
->left_margin
= g_value_get_double(value
);
410 data
->has_frame
= g_value_get_boolean(value
);
412 case PROP_TOP_PADDING
:
413 data
->top_padding
= g_value_get_double(value
);
415 case PROP_RIGHT_PADDING
:
416 data
->right_padding
= g_value_get_double(value
);
418 case PROP_BOTTOM_PADDING
:
419 data
->bottom_padding
= g_value_get_double(value
);
421 case PROP_LEFT_PADDING
:
422 data
->left_padding
= g_value_get_double(value
);
425 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
434 * Creates a new empty canvas object.
436 * Returns: (transfer full): the newly created canvas.
443 return g_object_new(ADG_TYPE_CANVAS
, NULL
);
447 * adg_canvas_set_size:
448 * @canvas: an #AdgCanvas
449 * @size: (transfer none): the new size for the canvas
451 * Sets a specific size on @canvas. The x and/or y
452 * component of @size can be set to 0, in which case
453 * the exact value will be autocalculated, that is the
454 * size component returned by adg_entity_get_extents()
455 * on @canvas will be used instead.
460 adg_canvas_set_size(AdgCanvas
*canvas
, const CpmlPair
*size
)
462 g_return_if_fail(ADG_IS_CANVAS(canvas
));
463 g_return_if_fail(size
!= NULL
);
465 g_object_set(canvas
, "size", size
, NULL
);
469 * adg_canvas_set_size_explicit:
470 * @canvas: an #AdgCanvas
471 * @x: the new width of the canvas or 0 to reset
472 * @y: the new height of the canvas or 0 to reset
474 * A convenient function to set the size of @canvas using
475 * explicit coordinates. Check adg_canvas_set_size() for
481 adg_canvas_set_size_explicit(AdgCanvas
*canvas
, gdouble x
, gdouble y
)
488 adg_canvas_set_size(canvas
, &size
);
492 * adg_canvas_get_size:
493 * @canvas: an #AdgCanvas
495 * Gets the specific size set on @canvas. The x and/or y
496 * components of the returned #CpmlPair could be 0, in which
497 * case the size returned by adg_entity_get_extents() on @canvas
498 * will be used instead.
500 * Returns: (transfer none): the explicit size set on this canvas or <constant>NULL</constant> on errors.
505 adg_canvas_get_size(AdgCanvas
*canvas
)
507 AdgCanvasPrivate
*data
;
509 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
516 * adg_canvas_set_scales:
517 * @canvas: an #AdgCanvas
518 * @...: <constant>NULL</constant> terminated list of strings
520 * Sets the scales allowed by @canvas. Every scale identifies a
521 * specific factor to be applied to the local matrix of @canvas.
522 * When adg_canvas_autoscale() will be called, the greatest
523 * scale that can render every entity inside a box of
524 * #AdgCanvas:size dimensions will be applied. The drawing will
525 * be centered inside that box.
527 * Every scale should be expressed with a string in the form of
528 * "x:y", where x and y are positive integers that identifies
529 * numerator an denominator of a fraction. That string itself
530 * will be put into the title block when used.
535 adg_canvas_set_scales(AdgCanvas
*canvas
, ...)
539 va_start(var_args
, canvas
);
540 adg_canvas_set_scales_valist(canvas
, var_args
);
545 * adg_canvas_set_scales_valist:
546 * @canvas: an #AdgCanvas
547 * @var_args: <constant>NULL</constant> terminated list of strings
549 * Vararg variant of adg_canvas_set_scales().
554 adg_canvas_set_scales_valist(AdgCanvas
*canvas
, va_list var_args
)
560 g_return_if_fail(ADG_IS_CANVAS(canvas
));
564 while ((scale
= va_arg(var_args
, const gchar
*)) != NULL
) {
566 scales
= g_realloc(scales
, sizeof(const gchar
*) * (n
+ 1));
567 scales
[n
-1] = g_strdup(scale
);
571 g_object_set(canvas
, "scales", scales
, NULL
);
576 * adg_canvas_set_scales_array: (rename-to adg_canvas_set_scales)
577 * @canvas: an #AdgCanvas
578 * @scales: (array zero-terminated=1) (allow-none): <constant>NULL</constant> terminated array of scales
580 * Array variant of adg_canvas_set_scales().
585 adg_canvas_set_scales_array(AdgCanvas
*canvas
, gchar
**scales
)
587 g_return_if_fail(ADG_IS_CANVAS(canvas
));
588 g_object_set(canvas
, "scales", scales
, NULL
);
592 * adg_canvas_get_scales:
593 * @canvas: an #AdgCanvas
595 * Gets the list of scales set on @canvas: check adg_canvas_set_scales()
596 * to get an idea of what scales are supposed to be.
598 * If no scales are set, <constant>NULL</constant> is returned.
600 * Returns: (element-type utf8) (transfer none): the <constant>NULL</constant> terminated list of valid scales.
605 adg_canvas_get_scales(AdgCanvas
*canvas
)
607 AdgCanvasPrivate
*data
;
609 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
616 * adg_canvas_autoscale:
617 * @canvas: an #AdgCanvas
619 * Applies one scale per time, in the order they have been provided
620 * in the adg_canvas_set_scales() call, until the drawing can be
621 * entirely contained into the current paper. If successful, the
622 * scale of the title block is changed accordingly and the drawing
623 * is centered inside the paper.
628 adg_canvas_autoscale(AdgCanvas
*canvas
)
630 AdgCanvasPrivate
*data
;
634 const CpmlExtents
*extents
;
635 AdgTitleBlock
*title_block
;
638 g_return_if_fail(ADG_IS_CANVAS(canvas
));
639 g_return_if_fail(_ADG_OLD_ENTITY_CLASS
->arrange
!= NULL
);
642 entity
= (AdgEntity
*) canvas
;
643 title_block
= data
->title_block
;
645 for (p_scale
= data
->scales
; p_scale
!= NULL
&& *p_scale
!= NULL
; ++p_scale
) {
646 const gchar
*scale
= *p_scale
;
647 gdouble factor
= adg_scale_factor(scale
);
651 cairo_matrix_init_scale(&map
, factor
, factor
);
652 adg_entity_set_local_map(entity
, &map
);
653 adg_entity_local_changed(entity
);
655 /* Arrange the entities inside the canvas, but not the canvas itself,
656 * just to get the bounding box of the drawing without the paper */
657 _ADG_OLD_ENTITY_CLASS
->arrange(entity
);
658 extents
= adg_entity_get_extents(entity
);
660 /* Just in case @canvas is emtpy */
661 if (! extents
->is_defined
)
664 if (title_block
!= NULL
)
665 adg_title_block_set_scale(title_block
, scale
);
667 /* Bail out if paper size is not specified or invalid */
668 if (data
->size
.x
<= 0 || data
->size
.y
<= 0)
671 /* If the drawing extents are fully contained inside the paper size,
672 * center the drawing in the paper and bail out */
673 delta
.x
= data
->size
.x
- extents
->size
.x
;
674 delta
.y
= data
->size
.y
- extents
->size
.y
;
675 if (delta
.x
>= 0 && delta
.y
>= 0) {
676 cairo_matrix_t transform
;
677 cairo_matrix_init_translate(&transform
,
678 delta
.x
/ 2 - extents
->org
.x
,
679 delta
.y
/ 2 - extents
->org
.y
);
680 adg_entity_transform_local_map(entity
, &transform
,
681 ADG_TRANSFORM_AFTER
);
688 * adg_canvas_set_background_dress:
689 * @canvas: an #AdgCanvas
690 * @dress: the new #AdgDress to use
692 * Sets a new background dress for rendering @canvas: the new
693 * dress must be a color dress.
698 adg_canvas_set_background_dress(AdgCanvas
*canvas
, AdgDress dress
)
700 g_return_if_fail(ADG_IS_CANVAS(canvas
));
701 g_object_set(canvas
, "background-dress", dress
, NULL
);
705 * adg_canvas_get_background_dress:
706 * @canvas: an #AdgCanvas
708 * Gets the background dress to be used in rendering @canvas.
710 * Returns: the current background dress.
715 adg_canvas_get_background_dress(AdgCanvas
*canvas
)
717 AdgCanvasPrivate
*data
;
719 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), ADG_DRESS_UNDEFINED
);
723 return data
->background_dress
;
727 * adg_canvas_set_frame_dress:
728 * @canvas: an #AdgCanvas
729 * @dress: the new #AdgDress to use
731 * Sets the #AdgCanvas:frame-dress property of @canvas to @dress:
732 * the new dress must be a line dress.
737 adg_canvas_set_frame_dress(AdgCanvas
*canvas
, AdgDress dress
)
739 g_return_if_fail(ADG_IS_CANVAS(canvas
));
740 g_object_set(canvas
, "frame-dress", dress
, NULL
);
744 * adg_canvas_get_frame_dress:
745 * @canvas: an #AdgCanvas
747 * Gets the frame dress to be used in rendering the border of @canvas.
749 * Returns: the current frame dress.
754 adg_canvas_get_frame_dress(AdgCanvas
*canvas
)
756 AdgCanvasPrivate
*data
;
758 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), ADG_DRESS_UNDEFINED
);
761 return data
->frame_dress
;
765 * adg_canvas_set_title_block:
766 * @canvas: an #AdgCanvas
767 * @title_block: (transfer full): a title block
769 * Sets the #AdgCanvas:title-block property of @canvas to @title_block.
771 * Although a title block entity could be added to @canvas in the usual
772 * way, that is using the adg_container_add() method, assigning a title
773 * block with adg_canvas_set_title_block() is somewhat different:
775 * - @title_block will be automatically attached to the bottom right
776 * corner of to the @canvas frame (this could be accomplished in the
777 * usual way too, by resetting the right and bottom paddings);
778 * - the @title_block boundary box is not taken into account while
779 * computing the extents of @canvas.
784 adg_canvas_set_title_block(AdgCanvas
*canvas
,
785 AdgTitleBlock
*title_block
)
787 g_return_if_fail(ADG_IS_CANVAS(canvas
));
788 g_return_if_fail(title_block
== NULL
|| ADG_IS_TITLE_BLOCK(title_block
));
789 g_object_set(canvas
, "title-block", title_block
, NULL
);
793 * adg_canvas_get_title_block:
794 * @canvas: an #AdgCanvas
796 * Gets the #AdgTitleBlock object of @canvas: check
797 * adg_canvas_set_title_block() for details.
799 * The returned entity is owned by @canvas and should not be
802 * Returns: (transfer none): the title block object or <constant>NULL</constant>.
807 adg_canvas_get_title_block(AdgCanvas
*canvas
)
809 AdgCanvasPrivate
*data
;
811 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
814 return data
->title_block
;
818 * adg_canvas_set_top_margin:
819 * @canvas: an #AdgCanvas
820 * @value: the new margin, in global space
822 * Changes the top margin of @canvas by setting #AdgCanvas:top-margin
823 * to @value. Negative values are allowed.
828 adg_canvas_set_top_margin(AdgCanvas
*canvas
, gdouble value
)
830 g_return_if_fail(ADG_IS_CANVAS(canvas
));
831 g_object_set(canvas
, "top-margin", value
, NULL
);
835 * adg_canvas_get_top_margin:
836 * @canvas: an #AdgCanvas
838 * Gets the top margin (in global space) of @canvas.
840 * Returns: the requested margin or 0 on error.
845 adg_canvas_get_top_margin(AdgCanvas
*canvas
)
847 AdgCanvasPrivate
*data
;
849 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
852 return data
->top_margin
;
856 * adg_canvas_set_right_margin:
857 * @canvas: an #AdgCanvas
858 * @value: the new margin, in global space
860 * Changes the right margin of @canvas by setting
861 * #AdgCanvas:right-margin to @value. Negative values are allowed.
866 adg_canvas_set_right_margin(AdgCanvas
*canvas
, gdouble value
)
868 g_return_if_fail(ADG_IS_CANVAS(canvas
));
869 g_object_set(canvas
, "right-margin", value
, NULL
);
873 * adg_canvas_get_right_margin:
874 * @canvas: an #AdgCanvas
876 * Gets the right margin (in global space) of @canvas.
878 * Returns: the requested margin or 0 on error.
883 adg_canvas_get_right_margin(AdgCanvas
*canvas
)
885 AdgCanvasPrivate
*data
;
887 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
890 return data
->right_margin
;
894 * adg_canvas_set_bottom_margin:
895 * @canvas: an #AdgCanvas
896 * @value: the new margin, in global space
898 * Changes the bottom margin of @canvas by setting
899 * #AdgCanvas:bottom-margin to @value. Negative values are allowed.
904 adg_canvas_set_bottom_margin(AdgCanvas
*canvas
, gdouble value
)
906 g_return_if_fail(ADG_IS_CANVAS(canvas
));
907 g_object_set(canvas
, "bottom-margin", value
, NULL
);
911 * adg_canvas_get_bottom_margin:
912 * @canvas: an #AdgCanvas
914 * Gets the bottom margin (in global space) of @canvas.
916 * Returns: the requested margin or 0 on error.
921 adg_canvas_get_bottom_margin(AdgCanvas
*canvas
)
923 AdgCanvasPrivate
*data
;
925 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
928 return data
->bottom_margin
;
932 * adg_canvas_set_left_margin:
933 * @canvas: an #AdgCanvas
934 * @value: the new margin, in global space
936 * Changes the left margin of @canvas by setting
937 * #AdgCanvas:left-margin to @value. Negative values are allowed.
942 adg_canvas_set_left_margin(AdgCanvas
*canvas
, gdouble value
)
944 g_return_if_fail(ADG_IS_CANVAS(canvas
));
945 g_object_set(canvas
, "left-margin", value
, NULL
);
949 * adg_canvas_get_left_margin:
950 * @canvas: an #AdgCanvas
952 * Gets the left margin (in global space) of @canvas.
954 * Returns: the requested margin or 0 on error.
959 adg_canvas_get_left_margin(AdgCanvas
*canvas
)
961 AdgCanvasPrivate
*data
;
963 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
966 return data
->left_margin
;
970 * adg_canvas_set_margins:
971 * @canvas: an #AdgCanvas
972 * @top: top margin, in global space
973 * @right: right margin, in global space
974 * @bottom: bottom margin, in global space
975 * @left: left margin, in global space
977 * Convenient function to set all the margins at once.
982 adg_canvas_set_margins(AdgCanvas
*canvas
,
983 gdouble top
, gdouble right
,
984 gdouble bottom
, gdouble left
)
986 g_return_if_fail(ADG_IS_CANVAS(canvas
));
987 g_object_set(canvas
, "top-margin", top
, "right-margin", right
,
988 "bottom-margin", bottom
, "left-margin", left
, NULL
);
992 * adg_canvas_apply_margins:
993 * @canvas: an #AdgCanvas
994 * @extents: where apply the margins
996 * A convenient function to apply the margins of @canvas to the
997 * arbitrary #CpmlExtents struct @extents.
1002 adg_canvas_apply_margins(AdgCanvas
*canvas
, CpmlExtents
*extents
)
1004 AdgCanvasPrivate
*data
;
1006 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1008 data
= canvas
->data
;
1010 extents
->org
.x
-= data
->left_margin
;
1011 extents
->org
.y
-= data
->top_margin
;
1012 extents
->size
.x
+= data
->left_margin
+ data
->right_margin
;
1013 extents
->size
.y
+= data
->top_margin
+ data
->bottom_margin
;
1017 * adg_canvas_switch_frame:
1018 * @canvas: an #AdgCanvas
1019 * @new_state: the new flag status
1021 * Sets a new status on the #AdgCanvas:has-frame
1022 * property: <constant>TRUE</constant> means a border around
1023 * the canvas extents (less the margins) should be rendered.
1028 adg_canvas_switch_frame(AdgCanvas
*canvas
, gboolean new_state
)
1030 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1031 g_object_set(canvas
, "has-frame", new_state
, NULL
);
1035 * adg_canvas_has_frame:
1036 * @canvas: an #AdgCanvas
1038 * Gets the current status of the #AdgCanvas:has-frame property,
1039 * that is whether a border around the canvas extents (less the
1040 * margins) should be rendered (<constant>TRUE</constant>) or not
1041 * (<constant>FALSE</constant>).
1043 * Returns: the current status of the frame flag.
1048 adg_canvas_has_frame(AdgCanvas
*canvas
)
1050 AdgCanvasPrivate
*data
;
1052 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), FALSE
);
1054 data
= canvas
->data
;
1055 return data
->has_frame
;
1059 * adg_canvas_set_top_padding:
1060 * @canvas: an #AdgCanvas
1061 * @value: the new padding, in global space
1063 * Changes the top padding of @canvas by setting
1064 * #AdgCanvas:top-padding to @value. Negative values are allowed.
1069 adg_canvas_set_top_padding(AdgCanvas
*canvas
, gdouble value
)
1071 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1072 g_object_set(canvas
, "top-padding", value
, NULL
);
1076 * adg_canvas_get_top_padding:
1077 * @canvas: an #AdgCanvas
1079 * Gets the top padding (in global space) of @canvas.
1081 * Returns: the requested padding or 0 on error.
1086 adg_canvas_get_top_padding(AdgCanvas
*canvas
)
1088 AdgCanvasPrivate
*data
;
1090 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1092 data
= canvas
->data
;
1093 return data
->top_padding
;
1097 * adg_canvas_set_right_padding:
1098 * @canvas: an #AdgCanvas
1099 * @value: the new padding, in global space
1101 * Changes the right padding of @canvas by setting #AdgCanvas:right-padding
1102 * to @value. Negative values are allowed.
1107 adg_canvas_set_right_padding(AdgCanvas
*canvas
, gdouble value
)
1109 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1110 g_object_set(canvas
, "right-padding", value
, NULL
);
1114 * adg_canvas_get_right_padding:
1115 * @canvas: an #AdgCanvas
1117 * Gets the right padding (in global space) of @canvas.
1119 * Returns: the requested padding or 0 on error.
1124 adg_canvas_get_right_padding(AdgCanvas
*canvas
)
1126 AdgCanvasPrivate
*data
;
1128 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1130 data
= canvas
->data
;
1131 return data
->right_padding
;
1136 * adg_canvas_set_bottom_padding:
1137 * @canvas: an #AdgCanvas
1138 * @value: the new padding, in global space
1140 * Changes the bottom padding of @canvas by setting
1141 * #AdgCanvas:bottom-padding to @value. Negative values are allowed.
1146 adg_canvas_set_bottom_padding(AdgCanvas
*canvas
, gdouble value
)
1148 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1149 g_object_set(canvas
, "bottom-padding", value
, NULL
);
1153 * adg_canvas_get_bottom_padding:
1154 * @canvas: an #AdgCanvas
1156 * Gets the bottom padding (in global space) of @canvas.
1158 * Returns: the requested padding or 0 on error.
1163 adg_canvas_get_bottom_padding(AdgCanvas
*canvas
)
1165 AdgCanvasPrivate
*data
;
1167 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1169 data
= canvas
->data
;
1170 return data
->bottom_padding
;
1174 * adg_canvas_set_left_padding:
1175 * @canvas: an #AdgCanvas
1176 * @value: the new padding, in global space
1178 * Changes the left padding of @canvas by setting
1179 * #AdgCanvas:left-padding to @value. Negative values are allowed.
1184 adg_canvas_set_left_padding(AdgCanvas
*canvas
, gdouble value
)
1186 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1187 g_object_set(canvas
, "left-padding", value
, NULL
);
1191 * adg_canvas_get_left_padding:
1192 * @canvas: an #AdgCanvas
1194 * Gets the left padding (in global space) of @canvas.
1196 * Returns: the requested padding or 0 on error.
1201 adg_canvas_get_left_padding(AdgCanvas
*canvas
)
1203 AdgCanvasPrivate
*data
;
1205 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1207 data
= canvas
->data
;
1208 return data
->left_padding
;
1212 * adg_canvas_set_paddings:
1213 * @canvas: an #AdgCanvas
1214 * @top: top padding, in global space
1215 * @right: right padding, in global space
1216 * @bottom: bottom padding, in global space
1217 * @left: left padding, in global space
1219 * Convenient function to set all the paddings at once.
1224 adg_canvas_set_paddings(AdgCanvas
*canvas
,
1225 gdouble top
, gdouble right
,
1226 gdouble bottom
, gdouble left
)
1228 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1229 g_object_set(canvas
, "top-padding", top
, "right-padding", right
,
1230 "bottom-padding", bottom
, "left-padding", left
, NULL
);
1235 _adg_global_changed(AdgEntity
*entity
)
1237 AdgCanvasPrivate
*data
= ((AdgCanvas
*) entity
)->data
;
1239 if (_ADG_OLD_ENTITY_CLASS
->global_changed
)
1240 _ADG_OLD_ENTITY_CLASS
->global_changed(entity
);
1242 if (data
->title_block
)
1243 adg_entity_global_changed((AdgEntity
*) data
->title_block
);
1247 _adg_local_changed(AdgEntity
*entity
)
1249 AdgCanvasPrivate
*data
= ((AdgCanvas
*) entity
)->data
;
1250 AdgTitleBlock
*title_block
= data
->title_block
;
1252 if (_ADG_OLD_ENTITY_CLASS
->local_changed
)
1253 _ADG_OLD_ENTITY_CLASS
->local_changed(entity
);
1255 if (title_block
!= NULL
) {
1256 const gchar
*scale
= adg_title_block_get_scale(title_block
);
1258 if (scale
!= NULL
&& scale
[0] != '\0') {
1259 const cairo_matrix_t
*map
= adg_entity_get_local_map(entity
);
1260 gdouble factor
= adg_scale_factor(scale
);
1262 if (map
->xx
!= factor
|| map
->yy
!= factor
)
1263 adg_title_block_set_scale(title_block
, "---");
1266 adg_entity_local_changed((AdgEntity
*) title_block
);
1271 _adg_invalidate(AdgEntity
*entity
)
1273 AdgCanvasPrivate
*data
= ((AdgCanvas
*) entity
)->data
;
1275 if (_ADG_OLD_ENTITY_CLASS
->invalidate
)
1276 _ADG_OLD_ENTITY_CLASS
->invalidate(entity
);
1278 if (data
->title_block
)
1279 adg_entity_invalidate((AdgEntity
*) data
->title_block
);
1283 _adg_arrange(AdgEntity
*entity
)
1285 AdgCanvasPrivate
*data
;
1286 CpmlExtents extents
;
1288 if (_ADG_OLD_ENTITY_CLASS
->arrange
)
1289 _ADG_OLD_ENTITY_CLASS
->arrange(entity
);
1291 cpml_extents_copy(&extents
, adg_entity_get_extents(entity
));
1293 /* The extents should be defined, otherwise there is no drawing */
1294 g_return_if_fail(extents
.is_defined
);
1296 data
= ((AdgCanvas
*) entity
)->data
;
1298 if (data
->size
.x
> 0 || data
->size
.y
> 0) {
1299 const cairo_matrix_t
*global
= adg_entity_get_global_matrix(entity
);
1304 paper
.size
.x
= data
->size
.x
;
1305 paper
.size
.y
= data
->size
.y
;
1307 cairo_matrix_transform_point(global
, &paper
.org
.x
, &paper
.org
.y
);
1308 cairo_matrix_transform_distance(global
, &paper
.size
.x
, &paper
.size
.y
);
1310 if (data
->size
.x
> 0) {
1311 extents
.org
.x
= paper
.org
.x
;
1312 extents
.size
.x
= paper
.size
.x
;
1314 if (data
->size
.y
> 0) {
1315 extents
.org
.y
= paper
.org
.y
;
1316 extents
.size
.y
= paper
.size
.y
;
1320 if (data
->size
.x
== 0) {
1321 extents
.org
.x
-= data
->left_padding
;
1322 extents
.size
.x
+= data
->left_padding
+ data
->right_padding
;
1324 if (data
->size
.y
== 0) {
1325 extents
.org
.y
-= data
->top_padding
;
1326 extents
.size
.y
+= data
->top_padding
+ data
->bottom_padding
;
1329 /* Impose the new extents */
1330 adg_entity_set_extents(entity
, &extents
);
1332 if (data
->title_block
) {
1333 AdgEntity
*title_block_entity
;
1334 const CpmlExtents
*title_block_extents
;
1337 title_block_entity
= (AdgEntity
*) data
->title_block
;
1338 adg_entity_arrange(title_block_entity
);
1339 title_block_extents
= adg_entity_get_extents(title_block_entity
);
1341 shift
.x
= extents
.org
.x
+ extents
.size
.x
- title_block_extents
->org
.x
1342 - title_block_extents
->size
.x
;
1343 shift
.y
= extents
.org
.y
+ extents
.size
.y
- title_block_extents
->org
.y
1344 - title_block_extents
->size
.y
;
1346 /* The following block could be optimized by skipping tiny shift,
1347 * usually left by rounding errors */
1348 if (shift
.x
!= 0 || shift
.y
!= 0) {
1349 cairo_matrix_t unglobal
, map
;
1350 adg_matrix_copy(&unglobal
, adg_entity_get_global_matrix(entity
));
1351 cairo_matrix_invert(&unglobal
);
1353 cairo_matrix_transform_distance(&unglobal
, &shift
.x
, &shift
.y
);
1354 cairo_matrix_init_translate(&map
, shift
.x
, shift
.y
);
1355 adg_entity_transform_global_map(title_block_entity
, &map
,
1356 ADG_TRANSFORM_AFTER
);
1358 adg_entity_global_changed(title_block_entity
);
1359 adg_entity_arrange(title_block_entity
);
1365 _adg_render(AdgEntity
*entity
, cairo_t
*cr
)
1367 AdgCanvasPrivate
*data
;
1368 const CpmlExtents
*extents
;
1370 data
= ((AdgCanvas
*) entity
)->data
;
1371 extents
= adg_entity_get_extents(entity
);
1375 /* Background fill */
1376 cairo_rectangle(cr
, extents
->org
.x
- data
->left_margin
,
1377 extents
->org
.y
- data
->top_margin
,
1378 extents
->size
.x
+ data
->left_margin
+ data
->right_margin
,
1379 extents
->size
.y
+ data
->top_margin
+ data
->bottom_margin
);
1380 adg_entity_apply_dress(entity
, data
->background_dress
, cr
);
1384 if (data
->has_frame
) {
1385 cairo_rectangle(cr
, extents
->org
.x
, extents
->org
.y
,
1386 extents
->size
.x
, extents
->size
.y
);
1387 cairo_transform(cr
, adg_entity_get_global_matrix(entity
));
1388 adg_entity_apply_dress(entity
, data
->frame_dress
, cr
);
1394 if (data
->title_block
)
1395 adg_entity_render((AdgEntity
*) data
->title_block
, cr
);
1397 if (_ADG_OLD_ENTITY_CLASS
->render
)
1398 _ADG_OLD_ENTITY_CLASS
->render(entity
, cr
);
1402 #if GTK3_ENABLED || GTK2_ENABLED
1405 * adg_canvas_set_paper:
1406 * @canvas: an #AdgCanvas
1407 * @paper_name: a paper name
1408 * @orientation: the page orientation
1410 * A convenient function to set the size of @canvas using a
1411 * @paper_name and an @orientation value. This should be a
1412 * PWG 5101.1-2002 paper name and it will be passed as is to
1413 * gtk_paper_size_new(), so use any valid name accepted by
1416 * To reset this size, you could use adg_canvas_set_size()
1417 * with a <constant>NULL</constant> size: in this way the
1418 * size will match the boundary boxes of the entities
1419 * contained by the canvas.
1421 * Furthermore, the margins will be set to their default values,
1422 * that is the margins returned by the #GtkPaperSize API.
1423 * If you want to use your own margins on a named paper size,
1424 * set them <emphasis>after</emphasis> the call to this function.
1429 adg_canvas_set_paper(AdgCanvas
*canvas
,
1430 const gchar
*paper_name
,
1431 GtkPageOrientation orientation
)
1433 GtkPageSetup
*page_setup
;
1434 GtkPaperSize
*paper_size
;
1436 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1437 g_return_if_fail(paper_name
!= NULL
);
1439 page_setup
= gtk_page_setup_new();
1440 paper_size
= gtk_paper_size_new(paper_name
);
1442 gtk_page_setup_set_paper_size(page_setup
, paper_size
);
1443 gtk_page_setup_set_orientation(page_setup
, orientation
);
1444 gtk_paper_size_free(paper_size
);
1446 adg_canvas_set_page_setup(canvas
, page_setup
);
1447 g_object_unref(page_setup
);
1451 * adg_canvas_set_page_setup:
1452 * @canvas: an #AdgCanvas
1453 * @page_setup: (allow-none) (transfer none): the page setup
1455 * A convenient function to setup the page of @canvas so it can
1456 * also be subsequentially used for printing. It is allowed to
1457 * pass <constant>NULL</constant> for @page_setup to unset the
1458 * setup data from @canvas.
1460 * A reference to @page_setup is added, so there is no need to keep
1461 * alive this object outside this function. The @page_setup pointer
1462 * is stored in the associative key <constant>"_adg_page_setup"</constant>
1463 * and can be retrieved at any time with adg_canvas_get_page_setup().
1465 * The size and margins provided by @page_setup are used to set the
1466 * size and margins of @canvas much in the same way as what
1467 * adg_canvas_set_paper() does. This means if you set a page and
1468 * then unset it, the canvas will retain size and margins of the
1469 * original page although @page_setup will not be used for printing.
1470 * You must unset the size with adg_canvas_set_size()
1471 * with a <constant>NULL</constant> size.
1473 * <informalexample><programlisting language="C">
1474 * // By default, canvas does not have an explicit size
1475 * adg_canvas_set_page_setup(canvas, a4);
1476 * // Here canvas has the size and margins specified by a4
1477 * adg_canvas_set_page_setup(canvas, NULL);
1478 * // Now the only difference is that canvas is no more bound
1479 * // to the a4 page setup, so the following will return NULL:
1480 * page_setup = adg_canvas_get_page_setup(canvas);
1481 * // To restore the original status and have an autocomputed size:
1482 * adg_canvas_set_size(canvas, NULL);
1483 * </programlisting></informalexample>
1488 adg_canvas_set_page_setup(AdgCanvas
*canvas
, GtkPageSetup
*page_setup
)
1490 gdouble top
, right
, bottom
, left
;
1493 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1495 if (page_setup
== NULL
) {
1496 /* By convention, NULL resets the default page but
1497 * does not change any other property */
1498 g_object_set_data((GObject
*) canvas
, "_adg_page_setup", NULL
);
1502 g_return_if_fail(GTK_IS_PAGE_SETUP(page_setup
));
1504 top
= gtk_page_setup_get_top_margin(page_setup
, GTK_UNIT_POINTS
);
1505 right
= gtk_page_setup_get_right_margin(page_setup
, GTK_UNIT_POINTS
);
1506 bottom
= gtk_page_setup_get_bottom_margin(page_setup
, GTK_UNIT_POINTS
);
1507 left
= gtk_page_setup_get_left_margin(page_setup
, GTK_UNIT_POINTS
);
1508 size
.x
= gtk_page_setup_get_page_width(page_setup
, GTK_UNIT_POINTS
);
1509 size
.y
= gtk_page_setup_get_page_height(page_setup
, GTK_UNIT_POINTS
);
1511 adg_canvas_set_size(canvas
, &size
);
1512 adg_canvas_set_margins(canvas
, top
, right
, bottom
, left
);
1514 g_object_ref(page_setup
);
1515 g_object_set_data_full((GObject
*) canvas
, "_adg_page_setup",
1516 page_setup
, g_object_unref
);
1520 * adg_canvas_get_page_setup:
1521 * @canvas: an #AdgCanvas
1523 * If adg_canvas_set_page_setup() is called, a #GtkPageSetup object
1524 * is created and bound to @canvas. This metho returns a pointer
1525 * to that internal object or <constant>NULL</constant> if
1526 * adg_canvas_set_page_setup() has not been called before.
1528 * Returns: (allow-none) (transfer none): the #GtkPageSetup with size and margins of @canvas of <constant>NULL</constant> on no setup found or errors.
1533 adg_canvas_get_page_setup(AdgCanvas
*canvas
)
1535 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
1536 return g_object_get_data((GObject
*) canvas
, "_adg_page_setup");