doc: corrected NEWS.xml typo
[adg.git] / src / adg / adg-gtk-layout-gtk2.c
blob53f6076c3b90541a2b99edfbeff90908fc7efde6
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(AdgGtkLayout, adg_gtk_layout, ADG_GTK_TYPE_AREA)
62 enum {
63 PROP_0,
64 PROP_HADJUSTMENT,
65 PROP_VADJUSTMENT
69 static void _adg_dispose (GObject *object);
70 static void _adg_get_property (GObject *object,
71 guint prop_id,
72 GValue *value,
73 GParamSpec *pspec);
74 static void _adg_set_property (GObject *object,
75 guint prop_id,
76 const GValue *value,
77 GParamSpec *pspec);
78 static void _adg_set_scroll_adjustments (GtkWidget *widget,
79 GtkAdjustment *hadjustment,
80 GtkAdjustment *vadjustment);
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;
103 GParamSpec *param;
104 GClosure *closure;
105 GType param_types[2];
107 gobject_class = (GObjectClass *) klass;
108 widget_class = (GtkWidgetClass *) klass;
109 area_class = (AdgGtkAreaClass *) klass;
111 g_type_class_add_private(klass, sizeof(AdgGtkLayoutPrivate));
113 gobject_class->dispose = _adg_dispose;
114 gobject_class->get_property = _adg_get_property;
115 gobject_class->set_property = _adg_set_property;
117 widget_class->parent_set = _adg_parent_set;
118 widget_class->size_allocate = _adg_size_allocate;
120 area_class->canvas_changed = _adg_canvas_changed;
121 area_class->extents_changed = _adg_extents_changed;
123 param = g_param_spec_object("hadjustment",
124 P_("Horizontal adjustment"),
125 P_("The GtkAdjustment that determines the values of the horizontal position for this viewport"),
126 GTK_TYPE_ADJUSTMENT,
127 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
128 g_object_class_install_property(gobject_class, PROP_HADJUSTMENT, param);
130 param = g_param_spec_object("vadjustment",
131 P_("Vertical adjustment"),
132 P_("The GtkAdjustment that determines the values of the vertical position for this viewport"),
133 GTK_TYPE_ADJUSTMENT,
134 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
135 g_object_class_install_property(gobject_class, PROP_VADJUSTMENT, param);
138 * AdgGtkLayout::set-scroll-adjustments:
139 * @layout: an #AdgGtkLayout
140 * @old_canvas: the old #AdgCanvas object
142 * Emitted when the #AdgGtkLayout scroll adjustments have been set.
144 * Since: 1.0
146 closure = g_cclosure_new(G_CALLBACK(_adg_set_scroll_adjustments), NULL, NULL);
147 param_types[0] = GTK_TYPE_ADJUSTMENT;
148 param_types[1] = GTK_TYPE_ADJUSTMENT;
149 widget_class->set_scroll_adjustments_signal =
150 g_signal_newv("set-scroll-adjustments", ADG_GTK_TYPE_LAYOUT,
151 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
152 adg_marshal_VOID__OBJECT_OBJECT,
153 G_TYPE_NONE, 2, param_types);
156 static void
157 adg_gtk_layout_init(AdgGtkLayout *layout)
159 AdgGtkLayoutPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(layout,
160 ADG_GTK_TYPE_LAYOUT,
161 AdgGtkLayoutPrivate);
163 data->hadjustment = NULL;
164 data->vadjustment = NULL;
165 data->policy_stored = FALSE;
166 data->viewport.is_defined = FALSE;
168 layout->data = data;
171 static void
172 _adg_dispose(GObject *object)
174 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
176 if (data->hadjustment != NULL) {
177 g_object_unref(data->hadjustment);
178 data->hadjustment = NULL;
181 if (data->vadjustment != NULL) {
182 g_object_unref(data->vadjustment);
183 data->vadjustment = NULL;
186 if (_ADG_OLD_OBJECT_CLASS->dispose != NULL)
187 _ADG_OLD_OBJECT_CLASS->dispose(object);
190 static void
191 _adg_get_property(GObject *object, guint prop_id,
192 GValue *value, GParamSpec *pspec)
194 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
196 switch (prop_id) {
197 case PROP_HADJUSTMENT:
198 g_value_set_object(value, data->hadjustment);
199 break;
200 case PROP_VADJUSTMENT:
201 g_value_set_object(value, data->vadjustment);
202 break;
203 default:
204 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
205 break;
209 static void
210 _adg_set_property(GObject *object, guint prop_id,
211 const GValue *value, GParamSpec *pspec)
213 AdgGtkLayout *layout;
214 AdgGtkLayoutPrivate *data;
215 GtkAdjustment *adjustment;
217 layout = (AdgGtkLayout *) object;
218 data = layout->data;
220 switch (prop_id) {
221 case PROP_HADJUSTMENT:
222 adjustment = g_value_get_object(value);
223 if (adjustment == NULL)
224 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
226 _adg_set_adjustment(layout, &data->hadjustment, adjustment);
227 break;
228 case PROP_VADJUSTMENT:
229 adjustment = g_value_get_object(value);
230 if (adjustment == NULL)
231 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
233 _adg_set_adjustment(layout, &data->vadjustment, adjustment);
234 break;
235 default:
236 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
237 break;
243 * adg_gtk_layout_new:
245 * Creates a new empty #AdgGtkLayout. The widget is useful only after
246 * an #AdgCanvas has been added either using the #AdgGtkLayout:canvas
247 * property or with adg_gtk_layout_set_canvas().
249 * Returns: (transfer full): the newly created widget.
251 * Since: 1.0
253 GtkWidget *
254 adg_gtk_layout_new(void)
256 return g_object_new(ADG_GTK_TYPE_LAYOUT, NULL);
260 * adg_gtk_layout_new_with_canvas:
261 * @canvas: the #AdgCanvas shown by this widget
263 * Creates a new #AdgGtkLayout and sets the #AdgGtkLayout:canvas property
264 * to @canvas.
266 * Returns: (transfer full): the newly created widget.
268 * Since: 1.0
270 GtkWidget *
271 adg_gtk_layout_new_with_canvas(AdgCanvas *canvas)
273 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
275 return g_object_new(ADG_GTK_TYPE_LAYOUT, "canvas", canvas, NULL);
279 * adg_gtk_layout_set_hadjustment:
280 * @layout: an #AdgGtkLayout
281 * @hadjustment: the new adjustment
283 * Sets the new horizontal adjustment for @layout to @hadjustment.
284 * The old adjustment, if present, is unreferenced.
286 * This is basically the same as manually setting the
287 * #AdgGtkLayout:hadjustment property with g_object_set().
289 * Since: 1.0
291 void
292 adg_gtk_layout_set_hadjustment(AdgGtkLayout *layout,
293 GtkAdjustment *hadjustment)
295 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
296 g_object_set(layout, "hadjustment", hadjustment, NULL);
300 * adg_gtk_layout_get_hadjustment:
301 * @layout: an #AdgGtkLayout
303 * Retrieves the current horizontal adjustment of @layout.
305 * The returned alignment is owned by @layout and should
306 * not be modified or freed.
308 * Returns: (transfer none): the alignment of @layout.
310 * Since: 1.0
312 GtkAdjustment *
313 adg_gtk_layout_get_hadjustment(AdgGtkLayout *layout)
315 AdgGtkLayoutPrivate *data;
317 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
319 data = layout->data;
321 return data->hadjustment;
325 * adg_gtk_layout_set_vadjustment:
326 * @layout: an #AdgGtkLayout
327 * @vadjustment: the new adjustment
329 * Sets the new vertical adjustment for @layout to @vadjustment.
330 * The old adjustment, if present, is unreferenced.
332 * This is basically the same as manually setting the
333 * #AdgGtkLayout:vadjustment property with g_object_set().
335 * Since: 1.0
337 void
338 adg_gtk_layout_set_vadjustment(AdgGtkLayout *layout,
339 GtkAdjustment *vadjustment)
341 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
342 g_object_set(layout, "vadjustment", vadjustment, NULL);
346 * adg_gtk_layout_get_vadjustment:
347 * @layout: an #AdgGtkLayout
349 * Retrieves the current vertical adjustment of @layout.
351 * The returned alignment is owned by @layout and should
352 * not be modified or freed.
354 * Returns: (transfer none): the alignment of @layout.
356 * Since: 1.0
358 GtkAdjustment *
359 adg_gtk_layout_get_vadjustment(AdgGtkLayout *layout)
361 AdgGtkLayoutPrivate *data;
363 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
365 data = layout->data;
367 return data->vadjustment;
371 static void
372 _adg_set_scroll_adjustments(GtkWidget *widget,
373 GtkAdjustment *hadjustment,
374 GtkAdjustment *vadjustment)
376 g_object_set(widget,
377 "hadjustment", hadjustment,
378 "vadjustment", vadjustment,
379 NULL);
382 static void
383 _adg_parent_set(GtkWidget *widget, GtkWidget *old_parent)
385 AdgGtkLayout *layout = (AdgGtkLayout *) widget;
387 if (_ADG_OLD_WIDGET_CLASS->parent_set != NULL)
388 _ADG_OLD_WIDGET_CLASS->parent_set(widget, old_parent);
390 _adg_set_parent_size(layout);
393 static void
394 _adg_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
396 AdgGtkLayout *layout;
397 AdgGtkLayoutPrivate *data;
399 layout = (AdgGtkLayout *) widget;
400 data = layout->data;
402 if (_ADG_OLD_WIDGET_CLASS->size_allocate != NULL)
403 _ADG_OLD_WIDGET_CLASS->size_allocate(widget, allocation);
405 /* Resize the viewport on a new allocation.
406 * TODO: plan other policies instead of forcibly set only the
407 * size field on the viewport struct, such as modifying the
408 * org to keep the sheet centered in the allocation space. */
409 data->viewport.size.x = allocation->width;
410 data->viewport.size.y = allocation->height;
411 data->viewport.is_defined = TRUE;
413 _adg_update_adjustments(layout);
416 static void
417 _adg_canvas_changed(AdgGtkArea *area, AdgCanvas *old_canvas)
419 AdgGtkLayout *layout;
420 AdgGtkLayoutPrivate *data;
422 layout = (AdgGtkLayout *) area;
423 data = layout->data;
425 if (_ADG_OLD_AREA_CLASS->canvas_changed != NULL)
426 _ADG_OLD_AREA_CLASS->canvas_changed(area, old_canvas);
428 /* By convention, expect the origin of a new canvas to be
429 * on the top/left corner of the allocation area */
430 data->viewport.org.x = 0;
431 data->viewport.org.y = 0;
433 _adg_set_parent_size(layout);
436 static void
437 _adg_extents_changed(AdgGtkArea *area, const CpmlExtents *old_extents)
439 if (_ADG_OLD_AREA_CLASS->extents_changed != NULL)
440 _ADG_OLD_AREA_CLASS->extents_changed(area, old_extents);
442 _adg_update_adjustments((AdgGtkLayout *) area);
445 static void
446 _adg_set_parent_size(AdgGtkLayout *layout)
448 AdgGtkLayoutPrivate *data;
449 GtkWidget *parent;
450 const CpmlExtents *sheet;
451 GtkScrolledWindow *scrolled_window;
453 /* When the widget is realized it is too late to suggest a size */
454 if (GTK_WIDGET_REALIZED(layout))
455 return;
457 data = layout->data;
458 parent = gtk_widget_get_parent((GtkWidget *) layout);
459 if (!GTK_IS_WIDGET(parent))
460 return;
462 sheet = adg_gtk_area_get_extents((AdgGtkArea *) layout);
463 if (sheet == NULL || !sheet->is_defined)
464 return;
466 gtk_widget_set_size_request(parent, sheet->size.x + 2, sheet->size.y + 2);
468 if (GTK_IS_SCROLLED_WINDOW(parent) && !data->policy_stored) {
469 scrolled_window = (GtkScrolledWindow *) parent;
471 gtk_scrolled_window_get_policy(scrolled_window,
472 &data->hpolicy, &data->vpolicy);
473 gtk_scrolled_window_set_policy(scrolled_window,
474 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
476 data->policy_stored = TRUE;
480 static void
481 _adg_set_adjustment(AdgGtkLayout *layout,
482 GtkAdjustment **dst, GtkAdjustment *src)
484 GCallback value_changed;
486 if (*dst == src)
487 return;
489 value_changed = G_CALLBACK(_adg_value_changed);
491 if (*dst != NULL) {
492 /* Release the old adjustment */
493 g_signal_handlers_disconnect_by_func(*dst, (gpointer) value_changed, layout);
494 g_object_unref(*dst);
497 g_signal_connect_swapped(src, "value-changed", value_changed, layout);
498 g_object_ref_sink(src);
500 *dst = src;
504 * _adg_update_adjustments:
505 * @layout: an #AdgGtkLayout
507 * Updates the scrollbars according to the new extents of the canvas
508 * of @area and to the current viewport.
510 * The algorithm uses three local #CpmlExtents variables: the
511 * <var>viewport</var> (what physically shown by the graphic device),
512 * the <var>sheet</var> (the extents of the drawing, margins
513 * included) and <var>surface</var> (a helper variable that is the
514 * union of the previous two extents).
516 * Since: 1.0
518 static void
519 _adg_update_adjustments(AdgGtkLayout *layout)
521 AdgGtkArea *area;
522 AdgCanvas *canvas;
523 const CpmlExtents *sheet, *viewport;
524 AdgGtkLayoutPrivate *data;
525 GtkAdjustment *hadj, *vadj;
526 CpmlExtents surface;
528 area = (AdgGtkArea *) layout;
529 canvas = adg_gtk_area_get_canvas(area);
530 if (canvas == NULL)
531 return;
533 sheet = adg_gtk_area_get_extents(area);
534 if (sheet == NULL || !sheet->is_defined)
535 return;
537 data = layout->data;
538 hadj = data->hadjustment;
539 vadj = data->vadjustment;
540 viewport = &data->viewport;
541 surface = *sheet;
542 cpml_extents_add(&surface, viewport);
544 if (data->policy_stored) {
545 /* Restore the original policy for the scrollbars */
546 GtkWidget *parent;
547 GtkScrolledWindow *scrolled_window;
549 parent = gtk_widget_get_parent((GtkWidget *) layout);
550 scrolled_window = (GtkScrolledWindow *) parent;
552 gtk_scrolled_window_set_policy(scrolled_window,
553 data->hpolicy, data->vpolicy);
555 data->policy_stored = TRUE;
558 g_object_set(hadj,
559 "lower", surface.org.x,
560 "upper", surface.org.x + surface.size.x,
561 "page-size", viewport->size.x,
562 "value", viewport->org.x,
563 NULL);
564 g_object_set(vadj,
565 "lower", surface.org.y,
566 "upper", surface.org.y + surface.size.y,
567 "page-size", viewport->size.y,
568 "value", viewport->org.y,
569 NULL);
572 static void
573 _adg_value_changed(AdgGtkLayout *layout)
575 AdgGtkArea *area;
576 AdgGtkLayoutPrivate *data;
577 CpmlPair org;
578 cairo_matrix_t map;
580 if (!GTK_WIDGET_REALIZED(layout))
581 return;
583 area = (AdgGtkArea *) layout;
584 data = layout->data;
585 org.x = gtk_adjustment_get_value(data->hadjustment);
586 org.y = gtk_adjustment_get_value(data->vadjustment);
588 cairo_matrix_init_translate(&map, data->viewport.org.x - org.x,
589 data->viewport.org.y - org.y);
590 adg_gtk_area_transform_render_map(area, &map, ADG_TRANSFORM_BEFORE);
592 gtk_widget_queue_draw((GtkWidget *) layout);
593 _adg_update_adjustments(layout);