1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2021 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-layout
23 * @short_description: A scrollable AdgGtkArea based widget
25 * This is an #AdgGtkArea derived object with scrolling capabilities.
26 * It means an #AdgGtkLayout object can be added directly to a
27 * #GtkScrolledWindow container without the need for an intermediate
28 * #GtkViewport container.
36 * All fields are private and should not be used directly.
37 * Use its public methods instead.
43 #include "adg-internal.h"
46 #include "adg-container.h"
47 #include "adg-table.h"
48 #include "adg-title-block.h"
49 #include <adg-canvas.h>
50 #include "adg-gtk-area.h"
52 #include "adg-gtk-layout.h"
53 #include "adg-gtk-layout-private.h"
55 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_gtk_layout_parent_class)
56 #define _ADG_OLD_WIDGET_CLASS ((GtkWidgetClass *) adg_gtk_layout_parent_class)
57 #define _ADG_OLD_AREA_CLASS ((AdgGtkAreaClass *) adg_gtk_layout_parent_class)
66 G_DEFINE_TYPE_WITH_PRIVATE(AdgGtkLayout
, adg_gtk_layout
, ADG_GTK_TYPE_AREA
)
69 _adg_set_scroll_adjustments(GtkWidget
*widget
,
70 GtkAdjustment
*hadjustment
,
71 GtkAdjustment
*vadjustment
)
74 "hadjustment", hadjustment
,
75 "vadjustment", vadjustment
,
89 G_DEFINE_TYPE_WITH_CODE(AdgGtkLayout
, adg_gtk_layout
, ADG_GTK_TYPE_AREA
,
90 G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE
, NULL
)
91 G_ADD_PRIVATE(AdgGtkLayout
))
96 _adg_set_parent_size(AdgGtkLayout
*layout
)
99 AdgGtkLayoutPrivate
*data
;
101 const CpmlExtents
*sheet
;
102 GtkScrolledWindow
*scrolled_window
;
104 widget
= (GtkWidget
*) layout
;
106 /* When the widget is realized it is too late to suggest a size */
107 if (gtk_widget_get_realized(widget
))
110 data
= adg_gtk_layout_get_instance_private(layout
);
111 parent
= gtk_widget_get_parent(widget
);
112 if (!GTK_IS_WIDGET(parent
))
115 sheet
= adg_gtk_area_get_extents((AdgGtkArea
*) layout
);
116 if (sheet
== NULL
|| !sheet
->is_defined
)
119 gtk_widget_set_size_request(parent
, sheet
->size
.x
+ 2, sheet
->size
.y
+ 2);
121 if (GTK_IS_SCROLLED_WINDOW(parent
) && !data
->policy_stored
) {
122 scrolled_window
= (GtkScrolledWindow
*) parent
;
124 gtk_scrolled_window_get_policy(scrolled_window
,
125 &data
->hpolicy
, &data
->vpolicy
);
126 gtk_scrolled_window_set_policy(scrolled_window
,
127 GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
129 data
->policy_stored
= TRUE
;
134 _adg_parent_set(GtkWidget
*widget
, GtkWidget
*old_parent
)
136 AdgGtkLayout
*layout
= (AdgGtkLayout
*) widget
;
138 if (_ADG_OLD_WIDGET_CLASS
->parent_set
!= NULL
)
139 _ADG_OLD_WIDGET_CLASS
->parent_set(widget
, old_parent
);
141 _adg_set_parent_size(layout
);
145 * _adg_update_adjustments:
146 * @layout: an #AdgGtkLayout
148 * Updates the scrollbars according to the new extents of the canvas
149 * of @area and to the current viewport.
151 * The algorithm uses three local #CpmlExtents variables: the
152 * <var>viewport</var> (what physically shown by the graphic device),
153 * the <var>sheet</var> (the extents of the drawing, margins
154 * included) and <var>surface</var> (a helper variable that is the
155 * union of the previous two extents).
158 _adg_update_adjustments(AdgGtkLayout
*layout
)
160 AdgGtkArea
*area
= (AdgGtkArea
*) layout
;
161 AdgCanvas
*canvas
= adg_gtk_area_get_canvas(area
);
162 const CpmlExtents
*sheet
, *viewport
;
163 AdgGtkLayoutPrivate
*data
;
164 GtkAdjustment
*hadj
, *vadj
;
170 sheet
= adg_gtk_area_get_extents(area
);
171 if (sheet
== NULL
|| !sheet
->is_defined
)
174 data
= adg_gtk_layout_get_instance_private(layout
);
175 hadj
= data
->hadjustment
;
176 vadj
= data
->vadjustment
;
177 viewport
= &data
->viewport
;
179 cpml_extents_add(&surface
, viewport
);
181 if (data
->policy_stored
) {
182 /* Restore the original policy for the scrollbars */
184 GtkScrolledWindow
*scrolled_window
;
186 parent
= gtk_widget_get_parent((GtkWidget
*) layout
);
187 scrolled_window
= (GtkScrolledWindow
*) parent
;
189 gtk_scrolled_window_set_policy(scrolled_window
,
190 data
->hpolicy
, data
->vpolicy
);
192 data
->policy_stored
= TRUE
;
196 "lower", surface
.org
.x
,
197 "upper", surface
.org
.x
+ surface
.size
.x
,
198 "page-size", viewport
->size
.x
,
199 "value", viewport
->org
.x
,
202 "lower", surface
.org
.y
,
203 "upper", surface
.org
.y
+ surface
.size
.y
,
204 "page-size", viewport
->size
.y
,
205 "value", viewport
->org
.y
,
210 _adg_size_allocate(GtkWidget
*widget
, GtkAllocation
*allocation
)
212 AdgGtkLayout
*layout
= (AdgGtkLayout
*) widget
;
213 AdgGtkLayoutPrivate
*data
= adg_gtk_layout_get_instance_private(layout
);
215 if (_ADG_OLD_WIDGET_CLASS
->size_allocate
!= NULL
)
216 _ADG_OLD_WIDGET_CLASS
->size_allocate(widget
, allocation
);
218 /* Resize the viewport on a new allocation.
219 * TODO: plan other policies instead of forcibly set only the
220 * size field on the viewport struct, such as modifying the
221 * org to keep the sheet centered in the allocation space. */
222 data
->viewport
.size
.x
= allocation
->width
;
223 data
->viewport
.size
.y
= allocation
->height
;
224 data
->viewport
.is_defined
= TRUE
;
226 _adg_update_adjustments(layout
);
230 _adg_canvas_changed(AdgGtkArea
*area
, AdgCanvas
*old_canvas
)
232 AdgGtkLayout
*layout
= (AdgGtkLayout
*) area
;
233 AdgGtkLayoutPrivate
*data
= adg_gtk_layout_get_instance_private(layout
);
235 if (_ADG_OLD_AREA_CLASS
->canvas_changed
!= NULL
)
236 _ADG_OLD_AREA_CLASS
->canvas_changed(area
, old_canvas
);
238 /* By convention, expect the origin of a new canvas to be
239 * on the top/left corner of the allocation area */
240 data
->viewport
.org
.x
= 0;
241 data
->viewport
.org
.y
= 0;
243 _adg_set_parent_size(layout
);
247 _adg_extents_changed(AdgGtkArea
*area
, const CpmlExtents
*old_extents
)
249 if (_ADG_OLD_AREA_CLASS
->extents_changed
!= NULL
)
250 _ADG_OLD_AREA_CLASS
->extents_changed(area
, old_extents
);
252 _adg_update_adjustments((AdgGtkLayout
*) area
);
256 _adg_value_changed(AdgGtkLayout
*layout
)
260 AdgGtkLayoutPrivate
*data
;
264 widget
= (GtkWidget
*) layout
;
266 if (!gtk_widget_get_realized(widget
))
269 area
= (AdgGtkArea
*) layout
;
270 data
= adg_gtk_layout_get_instance_private(layout
);
271 org
.x
= gtk_adjustment_get_value(data
->hadjustment
);
272 org
.y
= gtk_adjustment_get_value(data
->vadjustment
);
274 cairo_matrix_init_translate(&map
, data
->viewport
.org
.x
- org
.x
,
275 data
->viewport
.org
.y
- org
.y
);
276 adg_gtk_area_transform_render_map(area
, &map
, ADG_TRANSFORM_BEFORE
);
278 gtk_widget_queue_draw(widget
);
279 _adg_update_adjustments(layout
);
283 _adg_set_adjustment(AdgGtkLayout
*layout
,
284 GtkAdjustment
**dst
, GtkAdjustment
*src
)
286 GCallback value_changed
;
291 value_changed
= G_CALLBACK(_adg_value_changed
);
294 /* Release the old adjustment */
295 g_signal_handlers_disconnect_by_func(*dst
, (gpointer
) value_changed
, layout
);
296 g_object_unref(*dst
);
299 g_signal_connect_swapped(src
, "value-changed", value_changed
, layout
);
300 g_object_ref_sink(src
);
306 _adg_dispose(GObject
*object
)
308 AdgGtkLayoutPrivate
*data
= adg_gtk_layout_get_instance_private((AdgGtkLayout
*) object
);
310 if (data
->hadjustment
!= NULL
) {
311 g_object_unref(data
->hadjustment
);
312 data
->hadjustment
= NULL
;
315 if (data
->vadjustment
!= NULL
) {
316 g_object_unref(data
->vadjustment
);
317 data
->vadjustment
= NULL
;
320 if (_ADG_OLD_OBJECT_CLASS
->dispose
!= NULL
)
321 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
325 _adg_get_property(GObject
*object
, guint prop_id
,
326 GValue
*value
, GParamSpec
*pspec
)
328 AdgGtkLayoutPrivate
*data
= adg_gtk_layout_get_instance_private((AdgGtkLayout
*) object
);
331 case PROP_HADJUSTMENT
:
332 g_value_set_object(value
, data
->hadjustment
);
334 case PROP_VADJUSTMENT
:
335 g_value_set_object(value
, data
->vadjustment
);
338 case PROP_HSCROLL_POLICY
:
339 g_value_set_enum(value
, data
->hpolicy
);
341 case PROP_VSCROLL_POLICY
:
342 g_value_set_enum(value
, data
->vpolicy
);
346 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
352 _adg_set_property(GObject
*object
, guint prop_id
,
353 const GValue
*value
, GParamSpec
*pspec
)
355 AdgGtkLayout
*layout
= (AdgGtkLayout
*) object
;
356 AdgGtkLayoutPrivate
*data
= adg_gtk_layout_get_instance_private(layout
);
357 GtkAdjustment
*adjustment
;
360 case PROP_HADJUSTMENT
:
361 adjustment
= g_value_get_object(value
);
362 if (adjustment
== NULL
)
363 adjustment
= (GtkAdjustment
*) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
365 _adg_set_adjustment(layout
, &data
->hadjustment
, adjustment
);
367 case PROP_VADJUSTMENT
:
368 adjustment
= g_value_get_object(value
);
369 if (adjustment
== NULL
)
370 adjustment
= (GtkAdjustment
*) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
372 _adg_set_adjustment(layout
, &data
->vadjustment
, adjustment
);
375 case PROP_HSCROLL_POLICY
:
376 data
->hpolicy
= g_value_get_enum(value
);
378 case PROP_VSCROLL_POLICY
:
379 data
->vpolicy
= g_value_get_enum(value
);
383 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
389 adg_gtk_layout_class_init(AdgGtkLayoutClass
*klass
)
391 GObjectClass
*gobject_class
;
392 GtkWidgetClass
*widget_class
;
393 AdgGtkAreaClass
*area_class
;
395 gobject_class
= (GObjectClass
*) klass
;
396 widget_class
= (GtkWidgetClass
*) klass
;
397 area_class
= (AdgGtkAreaClass
*) klass
;
399 gobject_class
->dispose
= _adg_dispose
;
400 gobject_class
->get_property
= _adg_get_property
;
401 gobject_class
->set_property
= _adg_set_property
;
403 widget_class
->parent_set
= _adg_parent_set
;
404 widget_class
->size_allocate
= _adg_size_allocate
;
406 area_class
->canvas_changed
= _adg_canvas_changed
;
407 area_class
->extents_changed
= _adg_extents_changed
;
410 g_object_class_install_property(gobject_class
, PROP_HADJUSTMENT
,
411 g_param_spec_object("hadjustment",
412 P_("Horizontal adjustment"),
413 P_("The GtkAdjustment that determines the values of the horizontal position for this viewport"),
415 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
));
417 g_object_class_install_property(gobject_class
, PROP_VADJUSTMENT
,
418 g_param_spec_object("vadjustment",
419 P_("Vertical adjustment"),
420 P_("The GtkAdjustment that determines the values of the vertical position for this viewport"),
422 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
));
425 * AdgGtkLayout::set-scroll-adjustments:
426 * @layout: an #AdgGtkLayout
427 * @hadjustment: the horizontal #GtkAdjustment
428 * @vadjustment: the vertical #GtkAdjustment
430 * Emitted when the adjustments of the scroll bars must be changed.
434 widget_class
->set_scroll_adjustments_signal
=
435 g_signal_new_class_handler("set-scroll-adjustments", ADG_GTK_TYPE_LAYOUT
,
437 G_CALLBACK(_adg_set_scroll_adjustments
),
439 adg_marshal_VOID__OBJECT_OBJECT
,
441 GTK_TYPE_ADJUSTMENT
, GTK_TYPE_ADJUSTMENT
);
445 g_object_class_override_property(gobject_class
, PROP_HADJUSTMENT
,
447 g_object_class_override_property(gobject_class
, PROP_HSCROLL_POLICY
,
449 g_object_class_override_property(gobject_class
, PROP_VADJUSTMENT
,
451 g_object_class_override_property(gobject_class
, PROP_VSCROLL_POLICY
,
457 adg_gtk_layout_init(AdgGtkLayout
*layout
)
459 AdgGtkLayoutPrivate
*data
= adg_gtk_layout_get_instance_private(layout
);
460 data
->hadjustment
= NULL
;
461 data
->vadjustment
= NULL
;
462 data
->policy_stored
= FALSE
;
463 data
->viewport
.is_defined
= FALSE
;
468 * adg_gtk_layout_new:
470 * Creates a new empty #AdgGtkLayout. The widget is useful only after
471 * an #AdgCanvas has been added either using the #AdgGtkArea:canvas
472 * property or with adg_gtk_area_set_canvas().
474 * Returns: (transfer full): the newly created widget.
479 adg_gtk_layout_new(void)
481 return g_object_new(ADG_GTK_TYPE_LAYOUT
, NULL
);
485 * adg_gtk_layout_new_with_canvas:
486 * @canvas: the #AdgCanvas shown by this widget
488 * Creates a new #AdgGtkLayout and sets the #AdgGtkArea:canvas
489 * property to @canvas.
491 * Returns: (transfer full): the newly created widget.
496 adg_gtk_layout_new_with_canvas(AdgCanvas
*canvas
)
498 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
500 return g_object_new(ADG_GTK_TYPE_LAYOUT
, "canvas", canvas
, NULL
);
504 * adg_gtk_layout_set_hadjustment:
505 * @layout: an #AdgGtkLayout
506 * @hadjustment: the new adjustment
508 * Sets the new horizontal adjustment for @layout to @hadjustment.
509 * The old adjustment, if present, is unreferenced.
511 * This is basically the same as manually setting the
512 * #GtkScrollable:hadjustment property with g_object_set().
517 adg_gtk_layout_set_hadjustment(AdgGtkLayout
*layout
,
518 GtkAdjustment
*hadjustment
)
520 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout
));
521 g_object_set(layout
, "hadjustment", hadjustment
, NULL
);
525 * adg_gtk_layout_get_hadjustment:
526 * @layout: an #AdgGtkLayout
528 * Retrieves the current horizontal adjustment of @layout.
530 * The returned alignment is owned by @layout and should
531 * not be modified or freed.
533 * Returns: (transfer none): the alignment of @layout.
538 adg_gtk_layout_get_hadjustment(AdgGtkLayout
*layout
)
540 AdgGtkLayoutPrivate
*data
;
542 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
544 data
= adg_gtk_layout_get_instance_private(layout
);
545 return data
->hadjustment
;
549 * adg_gtk_layout_set_vadjustment:
550 * @layout: an #AdgGtkLayout
551 * @vadjustment: the new adjustment
553 * Sets the new vertical adjustment for @layout to @vadjustment.
554 * The old adjustment, if present, is unreferenced.
556 * This is basically the same as manually setting the
557 * #GtkScrollable:vadjustment property with g_object_set().
562 adg_gtk_layout_set_vadjustment(AdgGtkLayout
*layout
,
563 GtkAdjustment
*vadjustment
)
565 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout
));
566 g_object_set(layout
, "vadjustment", vadjustment
, NULL
);
570 * adg_gtk_layout_get_vadjustment:
571 * @layout: an #AdgGtkLayout
573 * Retrieves the current vertical adjustment of @layout.
575 * The returned alignment is owned by @layout and should
576 * not be modified or freed.
578 * Returns: (transfer none): the alignment of @layout.
583 adg_gtk_layout_get_vadjustment(AdgGtkLayout
*layout
)
585 AdgGtkLayoutPrivate
*data
;
587 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
589 data
= adg_gtk_layout_get_instance_private(layout
);
590 return data
->vadjustment
;