doc: update copyright line for 2019
[adg.git] / src / adg / adg-gtk-layout.c
blob19ae439fbc106bd4639a73ea21305aeb3da3c801
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2019 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-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.
30 * Since: 1.0
31 **/
33 /**
34 * AdgGtkLayout:
36 * All fields are private and should not be used directly.
37 * Use its public methods instead.
39 * Since: 1.0
40 **/
43 #include "adg-internal.h"
44 #include <gtk/gtk.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)
59 #ifdef GTK2_ENABLED
60 enum {
61 PROP_0,
62 PROP_HADJUSTMENT,
63 PROP_VADJUSTMENT
66 G_DEFINE_TYPE(AdgGtkLayout, adg_gtk_layout, ADG_GTK_TYPE_AREA)
68 static void
69 _adg_set_scroll_adjustments(GtkWidget *widget,
70 GtkAdjustment *hadjustment,
71 GtkAdjustment *vadjustment)
73 g_object_set(widget,
74 "hadjustment", hadjustment,
75 "vadjustment", vadjustment,
76 NULL);
78 #endif
80 #ifdef GTK3_ENABLED
81 enum {
82 PROP_0,
83 PROP_HADJUSTMENT,
84 PROP_VADJUSTMENT,
85 PROP_HSCROLL_POLICY,
86 PROP_VSCROLL_POLICY
89 G_DEFINE_TYPE_WITH_CODE(AdgGtkLayout, adg_gtk_layout, ADG_GTK_TYPE_AREA,
90 G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, NULL))
91 #endif
94 static void
95 _adg_set_parent_size(AdgGtkLayout *layout)
97 GtkWidget *widget;
98 AdgGtkLayoutPrivate *data;
99 GtkWidget *parent;
100 const CpmlExtents *sheet;
101 GtkScrolledWindow *scrolled_window;
103 widget = (GtkWidget *) layout;
105 /* When the widget is realized it is too late to suggest a size */
106 if (gtk_widget_get_realized(widget))
107 return;
109 data = layout->data;
110 parent = gtk_widget_get_parent(widget);
111 if (!GTK_IS_WIDGET(parent))
112 return;
114 sheet = adg_gtk_area_get_extents((AdgGtkArea *) layout);
115 if (sheet == NULL || !sheet->is_defined)
116 return;
118 gtk_widget_set_size_request(parent, sheet->size.x + 2, sheet->size.y + 2);
120 if (GTK_IS_SCROLLED_WINDOW(parent) && !data->policy_stored) {
121 scrolled_window = (GtkScrolledWindow *) parent;
123 gtk_scrolled_window_get_policy(scrolled_window,
124 &data->hpolicy, &data->vpolicy);
125 gtk_scrolled_window_set_policy(scrolled_window,
126 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
128 data->policy_stored = TRUE;
132 static void
133 _adg_parent_set(GtkWidget *widget, GtkWidget *old_parent)
135 AdgGtkLayout *layout = (AdgGtkLayout *) widget;
137 if (_ADG_OLD_WIDGET_CLASS->parent_set != NULL)
138 _ADG_OLD_WIDGET_CLASS->parent_set(widget, old_parent);
140 _adg_set_parent_size(layout);
144 * _adg_update_adjustments:
145 * @layout: an #AdgGtkLayout
147 * Updates the scrollbars according to the new extents of the canvas
148 * of @area and to the current viewport.
150 * The algorithm uses three local #CpmlExtents variables: the
151 * <var>viewport</var> (what physically shown by the graphic device),
152 * the <var>sheet</var> (the extents of the drawing, margins
153 * included) and <var>surface</var> (a helper variable that is the
154 * union of the previous two extents).
156 static void
157 _adg_update_adjustments(AdgGtkLayout *layout)
159 AdgGtkArea *area;
160 AdgCanvas *canvas;
161 const CpmlExtents *sheet, *viewport;
162 AdgGtkLayoutPrivate *data;
163 GtkAdjustment *hadj, *vadj;
164 CpmlExtents surface;
166 area = (AdgGtkArea *) layout;
167 canvas = adg_gtk_area_get_canvas(area);
168 if (canvas == NULL)
169 return;
171 sheet = adg_gtk_area_get_extents(area);
172 if (sheet == NULL || !sheet->is_defined)
173 return;
175 data = layout->data;
176 hadj = data->hadjustment;
177 vadj = data->vadjustment;
178 viewport = &data->viewport;
179 surface = *sheet;
180 cpml_extents_add(&surface, viewport);
182 if (data->policy_stored) {
183 /* Restore the original policy for the scrollbars */
184 GtkWidget *parent;
185 GtkScrolledWindow *scrolled_window;
187 parent = gtk_widget_get_parent((GtkWidget *) layout);
188 scrolled_window = (GtkScrolledWindow *) parent;
190 gtk_scrolled_window_set_policy(scrolled_window,
191 data->hpolicy, data->vpolicy);
193 data->policy_stored = TRUE;
196 g_object_set(hadj,
197 "lower", surface.org.x,
198 "upper", surface.org.x + surface.size.x,
199 "page-size", viewport->size.x,
200 "value", viewport->org.x,
201 NULL);
202 g_object_set(vadj,
203 "lower", surface.org.y,
204 "upper", surface.org.y + surface.size.y,
205 "page-size", viewport->size.y,
206 "value", viewport->org.y,
207 NULL);
210 static void
211 _adg_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
213 AdgGtkLayout *layout;
214 AdgGtkLayoutPrivate *data;
216 layout = (AdgGtkLayout *) widget;
217 data = layout->data;
219 if (_ADG_OLD_WIDGET_CLASS->size_allocate != NULL)
220 _ADG_OLD_WIDGET_CLASS->size_allocate(widget, allocation);
222 /* Resize the viewport on a new allocation.
223 * TODO: plan other policies instead of forcibly set only the
224 * size field on the viewport struct, such as modifying the
225 * org to keep the sheet centered in the allocation space. */
226 data->viewport.size.x = allocation->width;
227 data->viewport.size.y = allocation->height;
228 data->viewport.is_defined = TRUE;
230 _adg_update_adjustments(layout);
233 static void
234 _adg_canvas_changed(AdgGtkArea *area, AdgCanvas *old_canvas)
236 AdgGtkLayout *layout;
237 AdgGtkLayoutPrivate *data;
239 layout = (AdgGtkLayout *) area;
240 data = layout->data;
242 if (_ADG_OLD_AREA_CLASS->canvas_changed != NULL)
243 _ADG_OLD_AREA_CLASS->canvas_changed(area, old_canvas);
245 /* By convention, expect the origin of a new canvas to be
246 * on the top/left corner of the allocation area */
247 data->viewport.org.x = 0;
248 data->viewport.org.y = 0;
250 _adg_set_parent_size(layout);
253 static void
254 _adg_extents_changed(AdgGtkArea *area, const CpmlExtents *old_extents)
256 if (_ADG_OLD_AREA_CLASS->extents_changed != NULL)
257 _ADG_OLD_AREA_CLASS->extents_changed(area, old_extents);
259 _adg_update_adjustments((AdgGtkLayout *) area);
262 static void
263 _adg_value_changed(AdgGtkLayout *layout)
265 GtkWidget *widget;
266 AdgGtkArea *area;
267 AdgGtkLayoutPrivate *data;
268 CpmlPair org;
269 cairo_matrix_t map;
271 widget = (GtkWidget *) layout;
273 if (!gtk_widget_get_realized(widget))
274 return;
276 area = (AdgGtkArea *) layout;
277 data = layout->data;
278 org.x = gtk_adjustment_get_value(data->hadjustment);
279 org.y = gtk_adjustment_get_value(data->vadjustment);
281 cairo_matrix_init_translate(&map, data->viewport.org.x - org.x,
282 data->viewport.org.y - org.y);
283 adg_gtk_area_transform_render_map(area, &map, ADG_TRANSFORM_BEFORE);
285 gtk_widget_queue_draw(widget);
286 _adg_update_adjustments(layout);
289 static void
290 _adg_set_adjustment(AdgGtkLayout *layout,
291 GtkAdjustment **dst, GtkAdjustment *src)
293 GCallback value_changed;
295 if (*dst == src)
296 return;
298 value_changed = G_CALLBACK(_adg_value_changed);
300 if (*dst != NULL) {
301 /* Release the old adjustment */
302 g_signal_handlers_disconnect_by_func(*dst, (gpointer) value_changed, layout);
303 g_object_unref(*dst);
306 g_signal_connect_swapped(src, "value-changed", value_changed, layout);
307 g_object_ref_sink(src);
309 *dst = src;
312 static void
313 _adg_dispose(GObject *object)
315 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
317 if (data->hadjustment != NULL) {
318 g_object_unref(data->hadjustment);
319 data->hadjustment = NULL;
322 if (data->vadjustment != NULL) {
323 g_object_unref(data->vadjustment);
324 data->vadjustment = NULL;
327 if (_ADG_OLD_OBJECT_CLASS->dispose != NULL)
328 _ADG_OLD_OBJECT_CLASS->dispose(object);
331 static void
332 _adg_get_property(GObject *object, guint prop_id,
333 GValue *value, GParamSpec *pspec)
335 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
337 switch (prop_id) {
338 case PROP_HADJUSTMENT:
339 g_value_set_object(value, data->hadjustment);
340 break;
341 case PROP_VADJUSTMENT:
342 g_value_set_object(value, data->vadjustment);
343 break;
344 #ifdef GTK3_ENABLED
345 case PROP_HSCROLL_POLICY:
346 g_value_set_enum(value, data->hpolicy);
347 break;
348 case PROP_VSCROLL_POLICY:
349 g_value_set_enum(value, data->vpolicy);
350 break;
351 #endif
352 default:
353 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
354 break;
358 static void
359 _adg_set_property(GObject *object, guint prop_id,
360 const GValue *value, GParamSpec *pspec)
362 AdgGtkLayout *layout;
363 AdgGtkLayoutPrivate *data;
364 GtkAdjustment *adjustment;
366 layout = (AdgGtkLayout *) object;
367 data = layout->data;
369 switch (prop_id) {
370 case PROP_HADJUSTMENT:
371 adjustment = g_value_get_object(value);
372 if (adjustment == NULL)
373 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
375 _adg_set_adjustment(layout, &data->hadjustment, adjustment);
376 break;
377 case PROP_VADJUSTMENT:
378 adjustment = g_value_get_object(value);
379 if (adjustment == NULL)
380 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
382 _adg_set_adjustment(layout, &data->vadjustment, adjustment);
383 break;
384 #ifdef GTK3_ENABLED
385 case PROP_HSCROLL_POLICY:
386 data->hpolicy = g_value_get_enum(value);
387 break;
388 case PROP_VSCROLL_POLICY:
389 data->vpolicy = g_value_get_enum(value);
390 break;
391 #endif
392 default:
393 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
394 break;
398 static void
399 adg_gtk_layout_class_init(AdgGtkLayoutClass *klass)
401 GObjectClass *gobject_class;
402 GtkWidgetClass *widget_class;
403 AdgGtkAreaClass *area_class;
405 gobject_class = (GObjectClass *) klass;
406 widget_class = (GtkWidgetClass *) klass;
407 area_class = (AdgGtkAreaClass *) klass;
409 g_type_class_add_private(klass, sizeof(AdgGtkLayoutPrivate));
411 gobject_class->dispose = _adg_dispose;
412 gobject_class->get_property = _adg_get_property;
413 gobject_class->set_property = _adg_set_property;
415 widget_class->parent_set = _adg_parent_set;
416 widget_class->size_allocate = _adg_size_allocate;
418 area_class->canvas_changed = _adg_canvas_changed;
419 area_class->extents_changed = _adg_extents_changed;
421 #ifdef GTK2_ENABLED
422 g_object_class_install_property(gobject_class, PROP_HADJUSTMENT,
423 g_param_spec_object("hadjustment",
424 P_("Horizontal adjustment"),
425 P_("The GtkAdjustment that determines the values of the horizontal position for this viewport"),
426 GTK_TYPE_ADJUSTMENT,
427 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
429 g_object_class_install_property(gobject_class, PROP_VADJUSTMENT,
430 g_param_spec_object("vadjustment",
431 P_("Vertical adjustment"),
432 P_("The GtkAdjustment that determines the values of the vertical position for this viewport"),
433 GTK_TYPE_ADJUSTMENT,
434 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
437 * AdgGtkLayout::set-scroll-adjustments:
438 * @layout: an #AdgGtkLayout
439 * @hadjustment: the horizontal #GtkAdjustment
440 * @vadjustment: the vertical #GtkAdjustment
442 * Emitted when the adjustments of the scroll bars must be changed.
444 * Since: 1.0
446 widget_class->set_scroll_adjustments_signal =
447 g_signal_new_class_handler("set-scroll-adjustments", ADG_GTK_TYPE_LAYOUT,
448 G_SIGNAL_RUN_LAST,
449 G_CALLBACK(_adg_set_scroll_adjustments),
450 NULL, NULL,
451 adg_marshal_VOID__OBJECT_OBJECT,
452 G_TYPE_NONE, 2,
453 GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
454 #endif
456 #ifdef GTK3_ENABLED
457 g_object_class_override_property(gobject_class, PROP_HADJUSTMENT,
458 "hadjustment");
459 g_object_class_override_property(gobject_class, PROP_HSCROLL_POLICY,
460 "hscroll-policy");
461 g_object_class_override_property(gobject_class, PROP_VADJUSTMENT,
462 "vadjustment");
463 g_object_class_override_property(gobject_class, PROP_VSCROLL_POLICY,
464 "vscroll-policy");
465 #endif
468 static void
469 adg_gtk_layout_init(AdgGtkLayout *layout)
471 AdgGtkLayoutPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(layout,
472 ADG_GTK_TYPE_LAYOUT,
473 AdgGtkLayoutPrivate);
475 data->hadjustment = NULL;
476 data->vadjustment = NULL;
477 data->policy_stored = FALSE;
478 data->viewport.is_defined = FALSE;
480 layout->data = data;
485 * adg_gtk_layout_new:
487 * Creates a new empty #AdgGtkLayout. The widget is useful only after
488 * an #AdgCanvas has been added either using the #AdgGtkArea:canvas
489 * property or with adg_gtk_area_set_canvas().
491 * Returns: (transfer full): the newly created widget.
493 * Since: 1.0
495 GtkWidget *
496 adg_gtk_layout_new(void)
498 return g_object_new(ADG_GTK_TYPE_LAYOUT, NULL);
502 * adg_gtk_layout_new_with_canvas:
503 * @canvas: the #AdgCanvas shown by this widget
505 * Creates a new #AdgGtkLayout and sets the #AdgGtkArea:canvas
506 * property to @canvas.
508 * Returns: (transfer full): the newly created widget.
510 * Since: 1.0
512 GtkWidget *
513 adg_gtk_layout_new_with_canvas(AdgCanvas *canvas)
515 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
517 return g_object_new(ADG_GTK_TYPE_LAYOUT, "canvas", canvas, NULL);
521 * adg_gtk_layout_set_hadjustment:
522 * @layout: an #AdgGtkLayout
523 * @hadjustment: the new adjustment
525 * Sets the new horizontal adjustment for @layout to @hadjustment.
526 * The old adjustment, if present, is unreferenced.
528 * This is basically the same as manually setting the
529 * #GtkScrollable:hadjustment property with g_object_set().
531 * Since: 1.0
533 void
534 adg_gtk_layout_set_hadjustment(AdgGtkLayout *layout,
535 GtkAdjustment *hadjustment)
537 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
538 g_object_set(layout, "hadjustment", hadjustment, NULL);
542 * adg_gtk_layout_get_hadjustment:
543 * @layout: an #AdgGtkLayout
545 * Retrieves the current horizontal adjustment of @layout.
547 * The returned alignment is owned by @layout and should
548 * not be modified or freed.
550 * Returns: (transfer none): the alignment of @layout.
552 * Since: 1.0
554 GtkAdjustment *
555 adg_gtk_layout_get_hadjustment(AdgGtkLayout *layout)
557 AdgGtkLayoutPrivate *data;
559 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
561 data = layout->data;
563 return data->hadjustment;
567 * adg_gtk_layout_set_vadjustment:
568 * @layout: an #AdgGtkLayout
569 * @vadjustment: the new adjustment
571 * Sets the new vertical adjustment for @layout to @vadjustment.
572 * The old adjustment, if present, is unreferenced.
574 * This is basically the same as manually setting the
575 * #GtkScrollable:vadjustment property with g_object_set().
577 * Since: 1.0
579 void
580 adg_gtk_layout_set_vadjustment(AdgGtkLayout *layout,
581 GtkAdjustment *vadjustment)
583 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
584 g_object_set(layout, "vadjustment", vadjustment, NULL);
588 * adg_gtk_layout_get_vadjustment:
589 * @layout: an #AdgGtkLayout
591 * Retrieves the current vertical adjustment of @layout.
593 * The returned alignment is owned by @layout and should
594 * not be modified or freed.
596 * Returns: (transfer none): the alignment of @layout.
598 * Since: 1.0
600 GtkAdjustment *
601 adg_gtk_layout_get_vadjustment(AdgGtkLayout *layout)
603 AdgGtkLayoutPrivate *data;
605 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
607 data = layout->data;
609 return data->vadjustment;