build: depends on cairo-gobject if introspection is enabled
[adg.git] / src / adg / adg-gtk-layout.c
blob4af29b052ada7c4490b9af924d89196368cac165
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.
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)
60 G_DEFINE_TYPE_WITH_CODE(AdgGtkLayout, adg_gtk_layout, ADG_GTK_TYPE_AREA,
61 G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, NULL))
63 enum {
64 PROP_0,
65 PROP_HADJUSTMENT,
66 PROP_HSCROLL_POLICY,
67 PROP_VADJUSTMENT,
68 PROP_VSCROLL_POLICY
72 static void _adg_dispose (GObject *object);
73 static void _adg_get_property (GObject *object,
74 guint prop_id,
75 GValue *value,
76 GParamSpec *pspec);
77 static void _adg_set_property (GObject *object,
78 guint prop_id,
79 const GValue *value,
80 GParamSpec *pspec);
81 static void _adg_parent_set (GtkWidget *widget,
82 GtkWidget *old_parent);
83 static void _adg_size_allocate (GtkWidget *widget,
84 GtkAllocation *allocation);
85 static void _adg_canvas_changed (AdgGtkArea *area,
86 AdgCanvas *old_canvas);
87 static void _adg_extents_changed (AdgGtkArea *area,
88 const CpmlExtents *old_extents);
89 static void _adg_set_parent_size (AdgGtkLayout *layout);
90 static void _adg_set_adjustment (AdgGtkLayout *layout,
91 GtkAdjustment **dst,
92 GtkAdjustment *src);
93 static void _adg_update_adjustments (AdgGtkLayout *layout);
94 static void _adg_value_changed (AdgGtkLayout *layout);
97 static void
98 adg_gtk_layout_class_init(AdgGtkLayoutClass *klass)
100 GObjectClass *gobject_class;
101 GtkWidgetClass *widget_class;
102 AdgGtkAreaClass *area_class;
104 gobject_class = (GObjectClass *) klass;
105 widget_class = (GtkWidgetClass *) klass;
106 area_class = (AdgGtkAreaClass *) klass;
108 g_type_class_add_private(klass, sizeof(AdgGtkLayoutPrivate));
110 gobject_class->dispose = _adg_dispose;
111 gobject_class->get_property = _adg_get_property;
112 gobject_class->set_property = _adg_set_property;
114 widget_class->parent_set = _adg_parent_set;
115 widget_class->size_allocate = _adg_size_allocate;
117 area_class->canvas_changed = _adg_canvas_changed;
118 area_class->extents_changed = _adg_extents_changed;
120 g_object_class_override_property(gobject_class, PROP_HADJUSTMENT,
121 "hadjustment");
122 g_object_class_override_property(gobject_class, PROP_HSCROLL_POLICY,
123 "hscroll-policy");
124 g_object_class_override_property(gobject_class, PROP_VADJUSTMENT,
125 "vadjustment");
126 g_object_class_override_property(gobject_class, PROP_VSCROLL_POLICY,
127 "vscroll-policy");
130 static void
131 adg_gtk_layout_init(AdgGtkLayout *layout)
133 AdgGtkLayoutPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(layout,
134 ADG_GTK_TYPE_LAYOUT,
135 AdgGtkLayoutPrivate);
137 data->hadjustment = NULL;
138 data->vadjustment = NULL;
139 data->policy_stored = FALSE;
140 data->viewport.is_defined = FALSE;
142 layout->data = data;
145 static void
146 _adg_dispose(GObject *object)
148 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
150 if (data->hadjustment != NULL) {
151 g_object_unref(data->hadjustment);
152 data->hadjustment = NULL;
155 if (data->vadjustment != NULL) {
156 g_object_unref(data->vadjustment);
157 data->vadjustment = NULL;
160 if (_ADG_OLD_OBJECT_CLASS->dispose != NULL)
161 _ADG_OLD_OBJECT_CLASS->dispose(object);
164 static void
165 _adg_get_property(GObject *object, guint prop_id,
166 GValue *value, GParamSpec *pspec)
168 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
170 switch (prop_id) {
171 case PROP_HADJUSTMENT:
172 g_value_set_object(value, data->hadjustment);
173 break;
174 case PROP_HSCROLL_POLICY:
175 g_value_set_enum(value, data->hpolicy);
176 break;
177 case PROP_VADJUSTMENT:
178 g_value_set_object(value, data->vadjustment);
179 break;
180 case PROP_VSCROLL_POLICY:
181 g_value_set_enum(value, data->vpolicy);
182 break;
183 default:
184 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
185 break;
189 static void
190 _adg_set_property(GObject *object, guint prop_id,
191 const GValue *value, GParamSpec *pspec)
193 AdgGtkLayout *layout;
194 AdgGtkLayoutPrivate *data;
195 GtkAdjustment *adjustment;
197 layout = (AdgGtkLayout *) object;
198 data = layout->data;
200 switch (prop_id) {
201 case PROP_HADJUSTMENT:
202 adjustment = g_value_get_object(value);
203 if (adjustment == NULL)
204 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
206 _adg_set_adjustment(layout, &data->hadjustment, adjustment);
207 break;
208 case PROP_HSCROLL_POLICY:
209 data->hpolicy = g_value_get_enum(value);
210 break;
211 case PROP_VADJUSTMENT:
212 adjustment = g_value_get_object(value);
213 if (adjustment == NULL)
214 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
216 _adg_set_adjustment(layout, &data->vadjustment, adjustment);
217 break;
218 case PROP_VSCROLL_POLICY:
219 data->vpolicy = g_value_get_enum(value);
220 break;
221 default:
222 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
223 break;
229 * adg_gtk_layout_new:
231 * Creates a new empty #AdgGtkLayout. The widget is useful only after
232 * an #AdgCanvas has been added either using the #AdgGtkLayout:canvas
233 * property or with adg_gtk_layout_set_canvas().
235 * Returns: (transfer full): the newly created widget.
237 * Since: 1.0
239 GtkWidget *
240 adg_gtk_layout_new(void)
242 return g_object_new(ADG_GTK_TYPE_LAYOUT, NULL);
246 * adg_gtk_layout_new_with_canvas:
247 * @canvas: the #AdgCanvas shown by this widget
249 * Creates a new #AdgGtkLayout and sets the #AdgGtkLayout:canvas property
250 * to @canvas.
252 * Returns: (transfer full): the newly created widget.
254 * Since: 1.0
256 GtkWidget *
257 adg_gtk_layout_new_with_canvas(AdgCanvas *canvas)
259 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
261 return g_object_new(ADG_GTK_TYPE_LAYOUT, "canvas", canvas, NULL);
265 * adg_gtk_layout_set_hadjustment:
266 * @layout: an #AdgGtkLayout
267 * @hadjustment: the new adjustment
269 * Sets the new horizontal adjustment for @layout to @hadjustment.
270 * The old adjustment, if present, is unreferenced.
272 * This is basically the same as manually setting the
273 * #AdgGtkLayout:hadjustment property with g_object_set().
275 * Since: 1.0
277 void
278 adg_gtk_layout_set_hadjustment(AdgGtkLayout *layout,
279 GtkAdjustment *hadjustment)
281 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
282 g_object_set(layout, "hadjustment", hadjustment, NULL);
286 * adg_gtk_layout_get_hadjustment:
287 * @layout: an #AdgGtkLayout
289 * Retrieves the current horizontal adjustment of @layout.
291 * The returned alignment is owned by @layout and should
292 * not be modified or freed.
294 * Returns: (transfer none): the alignment of @layout.
296 * Since: 1.0
298 GtkAdjustment *
299 adg_gtk_layout_get_hadjustment(AdgGtkLayout *layout)
301 AdgGtkLayoutPrivate *data;
303 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
305 data = layout->data;
307 return data->hadjustment;
311 * adg_gtk_layout_set_vadjustment:
312 * @layout: an #AdgGtkLayout
313 * @vadjustment: the new adjustment
315 * Sets the new vertical adjustment for @layout to @vadjustment.
316 * The old adjustment, if present, is unreferenced.
318 * This is basically the same as manually setting the
319 * #AdgGtkLayout:vadjustment property with g_object_set().
321 * Since: 1.0
323 void
324 adg_gtk_layout_set_vadjustment(AdgGtkLayout *layout,
325 GtkAdjustment *vadjustment)
327 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
328 g_object_set(layout, "vadjustment", vadjustment, NULL);
332 * adg_gtk_layout_get_vadjustment:
333 * @layout: an #AdgGtkLayout
335 * Retrieves the current vertical adjustment of @layout.
337 * The returned alignment is owned by @layout and should
338 * not be modified or freed.
340 * Returns: (transfer none): the alignment of @layout.
342 * Since: 1.0
344 GtkAdjustment *
345 adg_gtk_layout_get_vadjustment(AdgGtkLayout *layout)
347 AdgGtkLayoutPrivate *data;
349 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
351 data = layout->data;
353 return data->vadjustment;
357 static void
358 _adg_parent_set(GtkWidget *widget, GtkWidget *old_parent)
360 AdgGtkLayout *layout = (AdgGtkLayout *) widget;
362 if (_ADG_OLD_WIDGET_CLASS->parent_set != NULL)
363 _ADG_OLD_WIDGET_CLASS->parent_set(widget, old_parent);
365 _adg_set_parent_size(layout);
368 static void
369 _adg_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
371 AdgGtkLayout *layout;
372 AdgGtkLayoutPrivate *data;
374 layout = (AdgGtkLayout *) widget;
375 data = layout->data;
377 if (_ADG_OLD_WIDGET_CLASS->size_allocate != NULL)
378 _ADG_OLD_WIDGET_CLASS->size_allocate(widget, allocation);
380 /* Resize the viewport on a new allocation.
381 * TODO: plan other policies instead of forcibly set only the
382 * size field on the viewport struct, such as modifying the
383 * org to keep the sheet centered in the allocation space. */
384 data->viewport.size.x = allocation->width;
385 data->viewport.size.y = allocation->height;
386 data->viewport.is_defined = TRUE;
388 _adg_update_adjustments(layout);
391 static void
392 _adg_canvas_changed(AdgGtkArea *area, AdgCanvas *old_canvas)
394 AdgGtkLayout *layout;
395 AdgGtkLayoutPrivate *data;
397 layout = (AdgGtkLayout *) area;
398 data = layout->data;
400 if (_ADG_OLD_AREA_CLASS->canvas_changed != NULL)
401 _ADG_OLD_AREA_CLASS->canvas_changed(area, old_canvas);
403 /* By convention, expect the origin of a new canvas to be
404 * on the top/left corner of the allocation area */
405 data->viewport.org.x = 0;
406 data->viewport.org.y = 0;
408 _adg_set_parent_size(layout);
411 static void
412 _adg_extents_changed(AdgGtkArea *area, const CpmlExtents *old_extents)
414 if (_ADG_OLD_AREA_CLASS->extents_changed != NULL)
415 _ADG_OLD_AREA_CLASS->extents_changed(area, old_extents);
417 _adg_update_adjustments((AdgGtkLayout *) area);
420 static void
421 _adg_set_parent_size(AdgGtkLayout *layout)
423 GtkWidget *widget;
424 AdgGtkLayoutPrivate *data;
425 GtkWidget *parent;
426 const CpmlExtents *sheet;
427 GtkScrolledWindow *scrolled_window;
429 widget = (GtkWidget *) layout;
431 /* When the widget is realized it is too late to suggest a size */
432 if (gtk_widget_get_realized(widget))
433 return;
435 data = layout->data;
436 parent = gtk_widget_get_parent(widget);
437 if (!GTK_IS_WIDGET(parent))
438 return;
440 sheet = adg_gtk_area_get_extents((AdgGtkArea *) layout);
441 if (sheet == NULL || !sheet->is_defined)
442 return;
444 gtk_widget_set_size_request(parent, sheet->size.x + 2, sheet->size.y + 2);
446 if (GTK_IS_SCROLLED_WINDOW(parent) && !data->policy_stored) {
447 scrolled_window = (GtkScrolledWindow *) parent;
449 gtk_scrolled_window_get_policy(scrolled_window,
450 &data->hpolicy, &data->vpolicy);
451 gtk_scrolled_window_set_policy(scrolled_window,
452 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
454 data->policy_stored = TRUE;
458 static void
459 _adg_set_adjustment(AdgGtkLayout *layout,
460 GtkAdjustment **dst, GtkAdjustment *src)
462 GCallback value_changed;
464 if (*dst == src)
465 return;
467 value_changed = G_CALLBACK(_adg_value_changed);
469 if (*dst != NULL) {
470 /* Release the old adjustment */
471 g_signal_handlers_disconnect_by_func(*dst, (gpointer) value_changed, layout);
472 g_object_unref(*dst);
475 g_signal_connect_swapped(src, "value-changed", value_changed, layout);
476 g_object_ref_sink(src);
478 *dst = src;
482 * _adg_update_adjustments:
483 * @layout: an #AdgGtkLayout
485 * Updates the scrollbars according to the new extents of the canvas
486 * of @area and to the current viewport.
488 * The algorithm uses three local #CpmlExtents variables: the
489 * <var>viewport</var> (what physically shown by the graphic device),
490 * the <var>sheet</var> (the extents of the drawing, margins
491 * included) and <var>surface</var> (a helper variable that is the
492 * union of the previous two extents).
494 * Since: 1.0
496 static void
497 _adg_update_adjustments(AdgGtkLayout *layout)
499 AdgGtkArea *area;
500 AdgCanvas *canvas;
501 const CpmlExtents *sheet, *viewport;
502 AdgGtkLayoutPrivate *data;
503 GtkAdjustment *hadj, *vadj;
504 CpmlExtents surface;
506 area = (AdgGtkArea *) layout;
507 canvas = adg_gtk_area_get_canvas(area);
508 if (canvas == NULL)
509 return;
511 sheet = adg_gtk_area_get_extents(area);
512 if (sheet == NULL || !sheet->is_defined)
513 return;
515 data = layout->data;
516 hadj = data->hadjustment;
517 vadj = data->vadjustment;
518 viewport = &data->viewport;
519 surface = *sheet;
520 cpml_extents_add(&surface, viewport);
522 if (data->policy_stored) {
523 /* Restore the original policy for the scrollbars */
524 GtkWidget *parent;
525 GtkScrolledWindow *scrolled_window;
527 parent = gtk_widget_get_parent((GtkWidget *) layout);
528 scrolled_window = (GtkScrolledWindow *) parent;
530 gtk_scrolled_window_set_policy(scrolled_window,
531 data->hpolicy, data->vpolicy);
533 data->policy_stored = TRUE;
536 g_object_set(hadj,
537 "lower", surface.org.x,
538 "upper", surface.org.x + surface.size.x,
539 "page-size", viewport->size.x,
540 "value", viewport->org.x,
541 NULL);
542 g_object_set(vadj,
543 "lower", surface.org.y,
544 "upper", surface.org.y + surface.size.y,
545 "page-size", viewport->size.y,
546 "value", viewport->org.y,
547 NULL);
550 static void
551 _adg_value_changed(AdgGtkLayout *layout)
553 GtkWidget *widget;
554 AdgGtkArea *area;
555 AdgGtkLayoutPrivate *data;
556 CpmlPair org;
557 cairo_matrix_t map;
559 widget = (GtkWidget *) layout;
561 if (!gtk_widget_get_realized(widget))
562 return;
564 area = (AdgGtkArea *) layout;
565 data = layout->data;
566 org.x = gtk_adjustment_get_value(data->hadjustment);
567 org.y = gtk_adjustment_get_value(data->vadjustment);
569 cairo_matrix_init_translate(&map, data->viewport.org.x - org.x,
570 data->viewport.org.y - org.y);
571 adg_gtk_area_transform_render_map(area, &map, ADG_TRANSFORM_BEFORE);
573 gtk_widget_queue_draw(widget);
574 _adg_update_adjustments(layout);