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.
22 * SECTION:adg-gtk-area
23 * @short_description: A #GtkWidget specifically designed to contain
24 * an #AdgCanvas entity
26 * This is a #GtkDrawingArea derived object that provides an easy way
27 * to show an ADG based canvas. The associated canvas can be set
28 * directly with the adg_gtk_area_new_with_canvas() constructor
29 * function or by using adg_gtk_area_set_canvas().
31 * The default minimum size of this widget will depend on the canvas
32 * content. The global matrix of the #AdgCanvas will be adjusted to
33 * expose the drawing in the proper position. The empty space around
34 * the drawing can be changed by setting the margins of the underlying
37 * The default implementation reacts to some mouse events: if you drag
38 * the mouse keeping the wheel pressed the canvas will be translated;
39 * if the mouse wheel is rotated the canvas will be scaled up or down
40 * (accordingly to the wheel direction) by the factor specified in the
41 * #AdgGtkArea:factor property.
48 * All fields are private and should not be used directly.
49 * Use its public methods instead.
53 #include "adg-gtk-internal.h"
54 #include "adg-gtk-area.h"
55 #include "adg-gtk-area-private.h"
56 #include "adg-gtk-marshal.h"
58 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_gtk_area_parent_class)
59 #define _ADG_OLD_WIDGET_CLASS ((GtkWidgetClass *) adg_gtk_area_parent_class)
62 G_DEFINE_TYPE(AdgGtkArea
, adg_gtk_area
, GTK_TYPE_DRAWING_AREA
);
78 static void _adg_dispose (GObject
*object
);
79 static void _adg_get_property (GObject
*object
,
83 static void _adg_set_property (GObject
*object
,
87 static void _adg_size_request (GtkWidget
*widget
,
88 GtkRequisition
*requisition
);
89 static void _adg_size_allocate (GtkWidget
*widget
,
90 GtkAllocation
*allocation
);
91 static gboolean
_adg_expose_event (GtkWidget
*widget
,
92 GdkEventExpose
*event
);
93 static gboolean
_adg_scroll_event (GtkWidget
*widget
,
94 GdkEventScroll
*event
);
95 static gboolean
_adg_button_press_event (GtkWidget
*widget
,
96 GdkEventButton
*event
);
97 static gboolean
_adg_motion_notify_event(GtkWidget
*widget
,
98 GdkEventMotion
*event
);
99 static gboolean
_adg_get_map (GtkWidget
*widget
,
100 gboolean local_space
,
102 AdgMatrix
*inverted
);
103 static void _adg_set_map (GtkWidget
*widget
,
104 gboolean local_space
,
105 const AdgMatrix
*map
);
106 static const CpmlExtents
*
107 _adg_get_extents (AdgGtkArea
*area
);
109 static guint _adg_signals
[LAST_SIGNAL
] = { 0 };
113 adg_gtk_area_class_init(AdgGtkAreaClass
*klass
)
115 GObjectClass
*gobject_class
;
116 GtkWidgetClass
*widget_class
;
119 gobject_class
= (GObjectClass
*) klass
;
120 widget_class
= (GtkWidgetClass
*) klass
;
122 g_type_class_add_private(klass
, sizeof(AdgGtkAreaPrivate
));
124 gobject_class
->dispose
= _adg_dispose
;
125 gobject_class
->get_property
= _adg_get_property
;
126 gobject_class
->set_property
= _adg_set_property
;
128 widget_class
->size_request
= _adg_size_request
;
129 widget_class
->size_allocate
= _adg_size_allocate
;
130 widget_class
->expose_event
= _adg_expose_event
;
131 widget_class
->scroll_event
= _adg_scroll_event
;
132 widget_class
->button_press_event
= _adg_button_press_event
;
133 widget_class
->motion_notify_event
= _adg_motion_notify_event
;
135 param
= g_param_spec_object("canvas",
137 P_("The canvas to be shown"),
139 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
);
140 g_object_class_install_property(gobject_class
, PROP_CANVAS
, param
);
142 param
= g_param_spec_double("factor",
144 P_("The factor used in zooming in and out"),
145 1., G_MAXDOUBLE
, 1.05,
147 g_object_class_install_property(gobject_class
, PROP_FACTOR
, param
);
149 param
= g_param_spec_boolean("autozoom",
151 P_("When enabled, automatically adjust the zoom factor in global space at every size allocation"),
154 g_object_class_install_property(gobject_class
, PROP_AUTOZOOM
, param
);
157 * AdgGtkArea::canvas-changed:
158 * @area: an #AdgGtkArea
159 * @old_canvas: the old #AdgCanvas object
161 * Emitted when the #AdgGtkArea has a new canvas. If the new canvas
162 * is the same as the old one, the signal is not emitted.
164 _adg_signals
[CANVAS_CHANGED
] =
165 g_signal_new("canvas-changed", ADG_GTK_TYPE_AREA
,
166 G_SIGNAL_RUN_LAST
|G_SIGNAL_NO_RECURSE
,
167 G_STRUCT_OFFSET(AdgGtkAreaClass
, canvas_changed
),
169 adg_gtk_marshal_VOID__OBJECT
,
170 G_TYPE_NONE
, 1, ADG_TYPE_CANVAS
);
173 * AdgGtkArea::extents-changed:
174 * @area: an #AdgGtkArea
175 * @old_extents: the old #CpmlExtents struct
177 * Emitted when the extents of @area have been changed.
178 * The old extents are always compared to the new ones,
179 * so when the extents are recalculated but the result
180 * is the same the signal is not emitted.
182 _adg_signals
[EXTENTS_CHANGED
] =
183 g_signal_new("extents-changed", ADG_GTK_TYPE_AREA
,
184 G_SIGNAL_RUN_LAST
|G_SIGNAL_NO_RECURSE
,
185 G_STRUCT_OFFSET(AdgGtkAreaClass
, extents_changed
),
187 adg_gtk_marshal_VOID__POINTER
,
188 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
192 adg_gtk_area_init(AdgGtkArea
*area
)
194 AdgGtkAreaPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(area
,
200 data
->autozoom
= FALSE
;
204 data
->initial_zoom
= 0;
208 /* Enable GDK events to catch wheel rotation and drag */
209 gtk_widget_add_events((GtkWidget
*) area
,
210 GDK_BUTTON_PRESS_MASK
|
211 GDK_BUTTON2_MOTION_MASK
|
216 _adg_dispose(GObject
*object
)
218 AdgGtkAreaPrivate
*data
= ((AdgGtkArea
*) object
)->data
;
221 g_object_unref(data
->canvas
);
225 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
226 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
230 _adg_get_property(GObject
*object
, guint prop_id
,
231 GValue
*value
, GParamSpec
*pspec
)
233 AdgGtkAreaPrivate
*data
= ((AdgGtkArea
*) object
)->data
;
237 g_value_set_object(value
, data
->canvas
);
240 g_value_set_double(value
, data
->factor
);
243 g_value_set_boolean(value
, data
->autozoom
);
246 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
252 _adg_set_property(GObject
*object
, guint prop_id
,
253 const GValue
*value
, GParamSpec
*pspec
)
256 AdgGtkAreaPrivate
*data
;
259 area
= (AdgGtkArea
*) object
;
264 canvas
= g_value_get_object(value
);
266 g_object_ref(canvas
);
268 g_object_unref(data
->canvas
);
269 if (data
->canvas
!= canvas
) {
270 data
->canvas
= canvas
;
271 g_signal_emit(area
, _adg_signals
[CANVAS_CHANGED
], 0);
275 data
->factor
= g_value_get_double(value
);
278 data
->autozoom
= g_value_get_boolean(value
);
281 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
290 * Creates a new empty #AdgGtkArea. The widget is useful only after
291 * an #AdgCanvas has been added either using the #AdgGtkArea:canvas
292 * property or with adg_gtk_area_set_canvas().
294 * Returns: the newly created widget
297 adg_gtk_area_new(void)
299 return g_object_new(ADG_GTK_TYPE_AREA
, NULL
);
303 * adg_gtk_area_new_with_canvas:
304 * @canvas: the #AdgCanvas shown by this widget
306 * Creates a new #AdgGtkArea and sets the #AdgGtkArea:canvas property
309 * Returns: the newly created widget
312 adg_gtk_area_new_with_canvas(AdgCanvas
*canvas
)
314 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
316 return g_object_new(ADG_GTK_TYPE_AREA
, "canvas", canvas
, NULL
);
320 * adg_gtk_area_set_canvas:
321 * @area: an #AdgGtkArea
322 * @canvas: the new #AdgCanvas
324 * Sets a new canvas on @area. The old canvas, if presents, is
328 adg_gtk_area_set_canvas(AdgGtkArea
*area
, AdgCanvas
*canvas
)
330 g_return_if_fail(ADG_GTK_IS_AREA(area
));
331 g_object_set(area
, "canvas", canvas
, NULL
);
335 * adg_gtk_area_get_canvas:
336 * @area: an #AdgGtkArea
338 * Gets the canvas associated to @area.
340 * Returns: the requested #AdgCanvas object or %NULL on errors
343 adg_gtk_area_get_canvas(AdgGtkArea
*area
)
345 AdgGtkAreaPrivate
*data
;
347 g_return_val_if_fail(ADG_GTK_IS_AREA(area
), NULL
);
355 * adg_gtk_area_get_extents:
356 * @area: an #AdgGtkArea
358 * Gets the extents of the canvas bound to @area. The returned
359 * struct is owned by @area and should not modified or freed.
360 * This is a convenient function that gets the extents with
361 * adg_entity_get_extents() and add to it the margins of the
364 * If @area still does not have any canvas associated to it or
365 * the canvas is invalid or empty, %NULL is returned.
367 * The canvas will be updated, meaning the adg_entity_arrange()
368 * will be called before the extents computation.
370 * Returns: the extents of the @area canvas or %NULL on errors
373 adg_gtk_area_get_extents(AdgGtkArea
*area
)
375 g_return_val_if_fail(ADG_GTK_IS_AREA(area
), NULL
);
377 return _adg_get_extents(area
);
381 * adg_gtk_area_get_zoom:
382 * @area: an #AdgGtkArea
384 * Gets the current zoom factor applied on the canvas of @area.
385 * If the #AdgGtkArea:autozoom property is %FALSE, the value
386 * returned is always %1.
388 * Returns: the current zoom factor
391 adg_gtk_area_get_zoom(AdgGtkArea
*area
)
393 AdgGtkAreaPrivate
*data
;
395 g_return_val_if_fail(ADG_GTK_IS_AREA(area
), 0.);
402 * adg_gtk_area_set_factor:
403 * @area: an #AdgGtkArea
404 * @factor: the new zoom factor
406 * Sets a new zoom factor to @area. If the factor is less than
407 * 1, it will be clamped to 1.
410 adg_gtk_area_set_factor(AdgGtkArea
*area
, gdouble factor
)
412 g_return_if_fail(ADG_GTK_IS_AREA(area
));
413 g_object_set(area
, "factor", factor
, NULL
);
417 * adg_gtk_area_get_factor:
418 * @area: an #AdgGtkArea
420 * Gets the zoom factor associated to @area. The zoom factor is
421 * directly used to zoom in (that is, the default zoom factor of
422 * 1.05 will zoom of 5% every iteration) and it is reversed while
423 * zooming out (that is, the default factor will use 1/1.05).
425 * Returns: the requested zoom factor or 0 on error
428 adg_gtk_area_get_factor(AdgGtkArea
*area
)
430 AdgGtkAreaPrivate
*data
;
432 g_return_val_if_fail(ADG_GTK_IS_AREA(area
), 0.);
439 * adg_gtk_area_switch_autozoom:
440 * @area: an #AdgGtkArea
441 * @state: the new autozoom state
443 * Sets the #AdgGtkArea:autozoom property of @area to @state. When the
444 * autozoom feature is enabled, @area reacts to any size allocation
445 * by adjusting its zoom factor in global space. This means the
446 * drawing will fill the available space (keeping its aspect ratio)
447 * when resizing the window.
450 adg_gtk_area_switch_autozoom(AdgGtkArea
*area
, gboolean state
)
452 g_return_if_fail(ADG_GTK_IS_AREA(area
));
453 g_object_set(area
, "autozoom", state
, NULL
);
457 * adg_gtk_area_has_autozoom:
458 * @area: an #AdgGtkArea
460 * Gets the current state of the #AdgGtkArea:autozoom property on
463 * Returns: the current autozoom state
466 adg_gtk_area_has_autozoom(AdgGtkArea
*area
)
468 AdgGtkAreaPrivate
*data
;
470 g_return_val_if_fail(ADG_GTK_IS_AREA(area
), FALSE
);
473 return data
->autozoom
;
477 * adg_gtk_area_canvas_changed:
478 * @area: an #AdgGtkArea
479 * @canvas: the old canvas bound to @area
481 * Emits the #AdgGtkArea::canvas-changed signal on @area.
484 adg_gtk_area_canvas_changed(AdgGtkArea
*area
, AdgCanvas
*old_canvas
)
486 g_return_if_fail(ADG_GTK_IS_AREA(area
));
488 g_signal_emit(area
, _adg_signals
[CANVAS_CHANGED
], 0, old_canvas
);
492 * adg_gtk_area_extents_changed:
493 * @area: an #AdgGtkArea
494 * @old_extents: the old extents of @area
496 * Emits the #AdgGtkArea::extents-changed signal on @area.
499 adg_gtk_area_extents_changed(AdgGtkArea
*area
, const CpmlExtents
*old_extents
)
501 g_return_if_fail(ADG_GTK_IS_AREA(area
));
503 g_signal_emit(area
, _adg_signals
[EXTENTS_CHANGED
], 0, old_extents
);
508 _adg_size_request(GtkWidget
*widget
, GtkRequisition
*requisition
)
510 const CpmlExtents
*extents
= _adg_get_extents((AdgGtkArea
*) widget
);
512 if (extents
!= NULL
&& extents
->is_defined
) {
513 requisition
->width
= extents
->size
.x
;
514 requisition
->height
= extents
->size
.y
;
519 * _adg_size_allocate:
520 * @widget: an #AdgGtkArea widget
521 * @allocation: the new allocation struct
523 * Scales the drawing according to the new allocation.
525 * TODO: the current implementation keeps the point in the center
526 * of the #AdgCanvas at the center of the new #AdgGtkArea. This is
527 * not what a user would likely expect because all the eventual
528 * translations in global space will be lost. In other words the
529 * resulting drawing is always centered also when the original
530 * drawing (that is, the drawing before the new size allocation)
534 _adg_size_allocate(GtkWidget
*widget
, GtkAllocation
*allocation
)
537 AdgGtkAreaPrivate
*data
;
540 CpmlVector
*initial_size
;
542 gdouble top
, right
, bottom
, left
;
543 gdouble vmargins
, hmargins
;
547 if (_ADG_OLD_WIDGET_CLASS
->size_allocate
)
548 _ADG_OLD_WIDGET_CLASS
->size_allocate(widget
, allocation
);
550 area
= (AdgGtkArea
*) widget
;
552 canvas
= data
->canvas
;
557 top
= adg_canvas_get_top_margin(canvas
);
558 right
= adg_canvas_get_right_margin(canvas
);
559 bottom
= adg_canvas_get_bottom_margin(canvas
);
560 left
= adg_canvas_get_left_margin(canvas
);
561 vmargins
= top
+ bottom
;
562 hmargins
= left
+ right
;
564 /* Check if the allocated space is enough:
565 * if not, there is not much we can do... */
566 g_return_if_fail(allocation
->width
> hmargins
);
567 g_return_if_fail(allocation
->height
> vmargins
);
569 entity
= (AdgEntity
*) canvas
;
570 initial_size
= &data
->initial_size
;
571 size
.x
= allocation
->width
- hmargins
;
572 size
.y
= allocation
->height
- vmargins
;
574 adg_matrix_copy(&map
, adg_entity_get_global_map(entity
));
576 if (data
->initial_zoom
<= 0) {
577 /* First call: register the initial size and zoom */
578 const CpmlExtents
*extents
= _adg_get_extents(area
);
580 if (extents
== NULL
|| !extents
->is_defined
)
583 if (extents
->size
.x
<= 0 || extents
->size
.y
<= 0)
586 *initial_size
= size
;
587 data
->initial_zoom
= 1;
590 if (data
->autozoom
) {
591 /* Adjust the zoom according to the initial size */
592 ratio
.x
= data
->initial_zoom
* size
.x
/ initial_size
->x
;
593 ratio
.y
= data
->initial_zoom
* size
.y
/ initial_size
->y
;
594 data
->zoom
= MIN(ratio
.x
, ratio
.y
);
597 map
.x0
= (size
.x
- initial_size
->x
* data
->zoom
) / 2;
598 map
.y0
= (size
.y
- initial_size
->y
* data
->zoom
) / 2;
599 map
.xx
= map
.yy
= data
->zoom
;
600 adg_entity_set_global_map(entity
, &map
);
604 _adg_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
)
606 AdgGtkAreaPrivate
*data
;
609 data
= ((AdgGtkArea
*) widget
)->data
;
610 canvas
= data
->canvas
;
612 if (canvas
&& event
->window
) {
613 cairo_t
*cr
= gdk_cairo_create(event
->window
);
615 adg_canvas_get_left_margin(canvas
),
616 adg_canvas_get_top_margin(canvas
));
617 adg_entity_render((AdgEntity
*) canvas
, cr
);
621 if (_ADG_OLD_WIDGET_CLASS
->expose_event
== NULL
)
624 return _ADG_OLD_WIDGET_CLASS
->expose_event(widget
, event
);
628 _adg_scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
)
630 gboolean zoom_in
, zoom_out
, local_space
, global_space
;
631 AdgMatrix map
, inverted
;
632 AdgGtkAreaPrivate
*data
;
635 zoom_in
= event
->direction
== GDK_SCROLL_UP
;
636 zoom_out
= event
->direction
== GDK_SCROLL_DOWN
;
637 local_space
= (event
->state
& ADG_GTK_MODIFIERS
) == 0;
638 global_space
= (event
->state
& ADG_GTK_MODIFIERS
) == GDK_SHIFT_MASK
;
640 if ((zoom_in
|| zoom_out
) && (local_space
|| global_space
) &&
641 _adg_get_map(widget
, local_space
, &map
, &inverted
)) {
642 data
= ((AdgGtkArea
*) widget
)->data
;
643 factor
= zoom_in
? data
->factor
: 1. / data
->factor
;
647 cairo_matrix_transform_point(&inverted
, &x
, &y
);
648 cairo_matrix_scale(&map
, factor
, factor
);
649 cairo_matrix_translate(&map
, x
/factor
- x
, y
/factor
- y
);
651 _adg_set_map(widget
, local_space
, &map
);
653 gtk_widget_queue_draw(widget
);
656 if (_ADG_OLD_WIDGET_CLASS
->scroll_event
== NULL
)
659 return _ADG_OLD_WIDGET_CLASS
->scroll_event(widget
, event
);
663 _adg_button_press_event(GtkWidget
*widget
, GdkEventButton
*event
)
665 AdgGtkAreaPrivate
*data
= ((AdgGtkArea
*) widget
)->data
;
667 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 2) {
668 /* Remember the starting coordinates of a (probable) translation */
669 data
->x_event
= event
->x
;
670 data
->y_event
= event
->y
;
673 if (_ADG_OLD_WIDGET_CLASS
->button_press_event
== NULL
)
676 return _ADG_OLD_WIDGET_CLASS
->button_press_event(widget
, event
);
680 _adg_motion_notify_event(GtkWidget
*widget
, GdkEventMotion
*event
)
682 gboolean translating
, local_space
, global_space
;
683 AdgMatrix map
, inverted
;
684 AdgGtkAreaPrivate
*data
;
687 translating
= (event
->state
& GDK_BUTTON2_MASK
) == GDK_BUTTON2_MASK
;
688 local_space
= (event
->state
& ADG_GTK_MODIFIERS
) == 0;
689 global_space
= (event
->state
& ADG_GTK_MODIFIERS
) == GDK_SHIFT_MASK
;
691 if (translating
&& (local_space
|| global_space
) &&
692 _adg_get_map(widget
, local_space
, &map
, &inverted
)) {
693 data
= ((AdgGtkArea
*) widget
)->data
;
694 x
= event
->x
- data
->x_event
;
695 y
= event
->y
- data
->y_event
;
697 cairo_matrix_transform_distance(&inverted
, &x
, &y
);
698 cairo_matrix_translate(&map
, x
, y
);
699 data
->x_event
= event
->x
;
700 data
->y_event
= event
->y
;
702 _adg_set_map(widget
, local_space
, &map
);
704 gtk_widget_queue_draw(widget
);
707 if (_ADG_OLD_WIDGET_CLASS
->motion_notify_event
== NULL
)
710 return _ADG_OLD_WIDGET_CLASS
->motion_notify_event(widget
, event
);
714 _adg_get_map(GtkWidget
*widget
, gboolean local_space
,
715 AdgMatrix
*map
, AdgMatrix
*inverted
)
717 AdgGtkAreaPrivate
*data
;
720 data
= ((AdgGtkArea
*) widget
)->data
;
721 entity
= (AdgEntity
*) data
->canvas
;
726 adg_matrix_copy(map
, adg_entity_get_local_map(entity
));
728 /* The inverted map is subject to the global matrix */
729 adg_matrix_copy(inverted
, adg_entity_get_global_matrix(entity
));
730 adg_matrix_transform(inverted
, map
, ADG_TRANSFORM_BEFORE
);
732 adg_matrix_copy(map
, adg_entity_get_global_map(entity
));
733 adg_matrix_copy(inverted
, map
);
736 return cairo_matrix_invert(inverted
) == CAIRO_STATUS_SUCCESS
;
740 _adg_set_map(GtkWidget
*widget
, gboolean local_space
, const AdgMatrix
*map
)
742 AdgGtkAreaPrivate
*data
;
745 data
= ((AdgGtkArea
*) widget
)->data
;
746 entity
= (AdgEntity
*) data
->canvas
;
752 adg_entity_set_local_map(entity
, map
);
754 adg_entity_set_global_map(entity
, map
);
756 _adg_get_extents((AdgGtkArea
*) widget
);
759 static const CpmlExtents
*
760 _adg_get_extents(AdgGtkArea
*area
)
762 AdgGtkAreaPrivate
*data
;
764 CpmlExtents old_extents
;
765 gdouble top
, right
, bottom
, left
;
768 old_extents
= data
->extents
;
769 data
->extents
.is_defined
= FALSE
;
771 canvas
= data
->canvas
;
773 if (ADG_IS_CANVAS(canvas
)) {
774 const CpmlExtents
*extents
;
777 entity
= (AdgEntity
*) canvas
;
779 adg_entity_arrange(entity
);
781 extents
= adg_entity_get_extents(entity
);
782 if (extents
!= NULL
&& extents
->is_defined
) {
783 top
= adg_canvas_get_top_margin(canvas
);
784 right
= adg_canvas_get_right_margin(canvas
);
785 bottom
= adg_canvas_get_bottom_margin(canvas
);
786 left
= adg_canvas_get_left_margin(canvas
);
788 data
->extents
= *extents
;
789 data
->extents
.org
.x
-= left
;
790 data
->extents
.org
.y
-= top
;
791 data
->extents
.size
.x
+= left
+ right
;
792 data
->extents
.size
.y
+= top
+ bottom
;
793 data
->extents
.is_defined
= TRUE
;
797 if (!cpml_extents_equal(&data
->extents
, &old_extents
))
798 g_signal_emit(area
, _adg_signals
[EXTENTS_CHANGED
], 0, &old_extents
);
800 return &data
->extents
;