Tested build process under ss-dev of OpenIndiana
[adg.git] / src / adg-gtk / adg-gtk-layout.c
blob33aef4da895cb2529db4dcf8a64349aa70dce07f
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011 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.
29 **/
31 /**
32 * AdgGtkLayout:
34 * All fields are private and should not be used directly.
35 * Use its public methods instead.
36 **/
39 #include "adg-gtk-internal.h"
40 #include "adg-gtk-area.h"
41 #include "adg-gtk-layout.h"
42 #include "adg-gtk-layout-private.h"
43 #include "adg-gtk-marshal.h"
45 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_gtk_layout_parent_class)
46 #define _ADG_OLD_WIDGET_CLASS ((GtkWidgetClass *) adg_gtk_layout_parent_class)
47 #define _ADG_OLD_AREA_CLASS ((AdgGtkAreaClass *) adg_gtk_layout_parent_class)
50 G_DEFINE_TYPE(AdgGtkLayout, adg_gtk_layout, ADG_GTK_TYPE_AREA)
52 enum {
53 PROP_0,
54 PROP_HADJUSTMENT,
55 PROP_VADJUSTMENT
59 static void _adg_dispose (GObject *object);
60 static void _adg_get_property (GObject *object,
61 guint prop_id,
62 GValue *value,
63 GParamSpec *pspec);
64 static void _adg_set_property (GObject *object,
65 guint prop_id,
66 const GValue *value,
67 GParamSpec *pspec);
68 static void _adg_set_scroll_adjustments (GtkWidget *widget,
69 GtkAdjustment *hadjustment,
70 GtkAdjustment *vadjustment);
71 static void _adg_parent_set (GtkWidget *widget,
72 GtkWidget *old_parent);
73 static void _adg_size_request (GtkWidget *widget,
74 GtkRequisition *requisition);
75 static void _adg_size_allocate (GtkWidget *widget,
76 GtkAllocation *allocation);
77 static void _adg_canvas_changed (AdgGtkArea *area,
78 AdgCanvas *old_canvas);
79 static void _adg_extents_changed (AdgGtkArea *area,
80 const CpmlExtents *old_extents);
81 static void _adg_set_parent_size (AdgGtkLayout *layout);
82 static void _adg_set_adjustment (AdgGtkLayout *layout,
83 GtkAdjustment **dst,
84 GtkAdjustment *src);
85 static void _adg_update_adjustments (AdgGtkLayout *layout);
86 static void _adg_value_changed (AdgGtkLayout *layout);
89 static void
90 adg_gtk_layout_class_init(AdgGtkLayoutClass *klass)
92 GObjectClass *gobject_class;
93 GtkWidgetClass *widget_class;
94 AdgGtkAreaClass *area_class;
95 GParamSpec *param;
96 GClosure *closure;
97 GType param_types[2];
99 gobject_class = (GObjectClass *) klass;
100 widget_class = (GtkWidgetClass *) klass;
101 area_class = (AdgGtkAreaClass *) klass;
103 g_type_class_add_private(klass, sizeof(AdgGtkLayoutPrivate));
105 gobject_class->dispose = _adg_dispose;
106 gobject_class->get_property = _adg_get_property;
107 gobject_class->set_property = _adg_set_property;
109 widget_class->parent_set = _adg_parent_set;
110 widget_class->size_request = _adg_size_request;
111 widget_class->size_allocate = _adg_size_allocate;
113 area_class->canvas_changed = _adg_canvas_changed;
114 area_class->extents_changed = _adg_extents_changed;
116 param = g_param_spec_object("hadjustment",
117 P_("Horizontal adjustment"),
118 P_("The GtkAdjustment that determines the values of the horizontal position for this viewport"),
119 GTK_TYPE_ADJUSTMENT,
120 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
121 g_object_class_install_property(gobject_class, PROP_HADJUSTMENT, param);
123 param = g_param_spec_object("vadjustment",
124 P_("Vertical adjustment"),
125 P_("The GtkAdjustment that determines the values of the vertical position for this viewport"),
126 GTK_TYPE_ADJUSTMENT,
127 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
128 g_object_class_install_property(gobject_class, PROP_VADJUSTMENT, param);
131 * AdgGtkLayout::set-scroll-adjustments:
132 * @layout: an #AdgGtkLayout
133 * @old_canvas: the old #AdgCanvas object
135 * Emitted when the #AdgGtkLayout scroll adjustments have been set.
137 closure = g_cclosure_new(G_CALLBACK(_adg_set_scroll_adjustments), NULL, NULL);
138 param_types[0] = GTK_TYPE_ADJUSTMENT;
139 param_types[1] = GTK_TYPE_ADJUSTMENT;
140 widget_class->set_scroll_adjustments_signal =
141 g_signal_newv("set-scroll-adjustments", ADG_GTK_TYPE_LAYOUT,
142 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
143 adg_gtk_marshal_VOID__OBJECT_OBJECT,
144 G_TYPE_NONE, 2, param_types);
147 static void
148 adg_gtk_layout_init(AdgGtkLayout *layout)
150 AdgGtkLayoutPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(layout,
151 ADG_GTK_TYPE_LAYOUT,
152 AdgGtkLayoutPrivate);
154 data->hadjustment = NULL;
155 data->vadjustment = NULL;
156 data->policy_stored = FALSE;
157 data->viewport.is_defined = FALSE;
159 layout->data = data;
162 static void
163 _adg_dispose(GObject *object)
165 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
167 if (data->hadjustment != NULL) {
168 g_object_unref(data->hadjustment);
169 data->hadjustment = NULL;
172 if (data->vadjustment != NULL) {
173 g_object_unref(data->vadjustment);
174 data->vadjustment = NULL;
177 if (_ADG_OLD_OBJECT_CLASS->dispose != NULL)
178 _ADG_OLD_OBJECT_CLASS->dispose(object);
181 static void
182 _adg_get_property(GObject *object, guint prop_id,
183 GValue *value, GParamSpec *pspec)
185 AdgGtkLayoutPrivate *data = ((AdgGtkLayout *) object)->data;
187 switch (prop_id) {
188 case PROP_HADJUSTMENT:
189 g_value_set_object(value, data->hadjustment);
190 break;
191 case PROP_VADJUSTMENT:
192 g_value_set_object(value, data->vadjustment);
193 break;
194 default:
195 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
196 break;
200 static void
201 _adg_set_property(GObject *object, guint prop_id,
202 const GValue *value, GParamSpec *pspec)
204 AdgGtkLayout *layout;
205 AdgGtkLayoutPrivate *data;
206 GtkAdjustment *adjustment;
208 layout = (AdgGtkLayout *) object;
209 data = layout->data;
211 switch (prop_id) {
212 case PROP_HADJUSTMENT:
213 adjustment = g_value_get_object(value);
214 if (adjustment == NULL)
215 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
217 _adg_set_adjustment(layout, &data->hadjustment, adjustment);
218 break;
219 case PROP_VADJUSTMENT:
220 adjustment = g_value_get_object(value);
221 if (adjustment == NULL)
222 adjustment = (GtkAdjustment *) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
224 _adg_set_adjustment(layout, &data->vadjustment, adjustment);
225 break;
226 default:
227 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
228 break;
234 * adg_gtk_layout_new:
236 * Creates a new empty #AdgGtkLayout. The widget is useful only after
237 * an #AdgCanvas has been added either using the #AdgGtkLayout:canvas
238 * property or with adg_gtk_layout_set_canvas().
240 * Returns: the newly created widget
242 GtkWidget *
243 adg_gtk_layout_new(void)
245 return g_object_new(ADG_GTK_TYPE_LAYOUT, NULL);
249 * adg_gtk_layout_new_with_canvas:
250 * @canvas: the #AdgCanvas shown by this widget
252 * Creates a new #AdgGtkLayout and sets the #AdgGtkLayout:canvas property
253 * to @canvas.
255 * Returns: the newly created widget
257 GtkWidget *
258 adg_gtk_layout_new_with_canvas(AdgCanvas *canvas)
260 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
262 return g_object_new(ADG_GTK_TYPE_LAYOUT, "canvas", canvas, NULL);
266 * adg_gtk_layout_set_hadjustment:
267 * @layout: an #AdgGtkLayout
268 * @hadjustment: the new adjustment
270 * Sets the new horizontal adjustment for @layout to @hadjustment.
271 * The old adjustment, if present, is unreferenced.
273 * This is basically the same as manually setting the
274 * #AdgGtkLayout:hadjustment property with g_object_set().
276 void
277 adg_gtk_layout_set_hadjustment(AdgGtkLayout *layout,
278 GtkAdjustment *hadjustment)
280 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
281 g_object_set(layout, "hadjustment", hadjustment, NULL);
285 * adg_gtk_layout_get_hadjustment:
286 * @layout: an #AdgGtkLayout
288 * Retrieves the current horizontal adjustment of @layout.
290 GtkAdjustment *
291 adg_gtk_layout_get_hadjustment(AdgGtkLayout *layout)
293 AdgGtkLayoutPrivate *data;
295 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
297 data = layout->data;
299 return data->hadjustment;
303 * adg_gtk_layout_set_vadjustment:
304 * @layout: an #AdgGtkLayout
305 * @vadjustment: the new adjustment
307 * Sets the new vertical adjustment for @layout to @vadjustment.
308 * The old adjustment, if present, is unreferenced.
310 * This is basically the same as manually setting the
311 * #AdgGtkLayout:vadjustment property with g_object_set().
313 void
314 adg_gtk_layout_set_vadjustment(AdgGtkLayout *layout,
315 GtkAdjustment *vadjustment)
317 g_return_if_fail(ADG_GTK_IS_LAYOUT(layout));
318 g_object_set(layout, "vadjustment", vadjustment, NULL);
322 * adg_gtk_layout_get_vadjustment:
323 * @layout: an #AdgGtkLayout
325 * Retrieves the current vertical adjustment of @layout.
327 GtkAdjustment *
328 adg_gtk_layout_get_vadjustment(AdgGtkLayout *layout)
330 AdgGtkLayoutPrivate *data;
332 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout), NULL);
334 data = layout->data;
336 return data->vadjustment;
340 static void
341 _adg_set_scroll_adjustments(GtkWidget *widget,
342 GtkAdjustment *hadjustment,
343 GtkAdjustment *vadjustment)
345 g_object_set(widget,
346 "hadjustment", hadjustment,
347 "vadjustment", vadjustment,
348 NULL);
351 static void
352 _adg_parent_set(GtkWidget *widget, GtkWidget *old_parent)
354 AdgGtkLayout *layout = (AdgGtkLayout *) widget;
356 if (_ADG_OLD_WIDGET_CLASS->parent_set != NULL)
357 _ADG_OLD_WIDGET_CLASS->parent_set(widget, old_parent);
359 _adg_set_parent_size(layout);
362 static void
363 _adg_size_request(GtkWidget *widget, GtkRequisition *requisition)
365 AdgGtkLayout *layout;
366 AdgGtkLayoutPrivate *data;
368 layout = (AdgGtkLayout *) widget;
369 data = layout->data;
371 if (_ADG_OLD_WIDGET_CLASS->size_request != NULL)
372 _ADG_OLD_WIDGET_CLASS->size_request(widget, requisition);
375 static void
376 _adg_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
378 AdgGtkLayout *layout;
379 AdgGtkLayoutPrivate *data;
381 layout = (AdgGtkLayout *) widget;
382 data = layout->data;
384 if (_ADG_OLD_WIDGET_CLASS->size_allocate != NULL)
385 _ADG_OLD_WIDGET_CLASS->size_allocate(widget, allocation);
387 /* Resize the viewport on a new allocation.
388 * TODO: plan other policies instead of forcibly set only the
389 * size field on the viewport struct, such as modifying the
390 * org to keep the sheet centered in the allocation space. */
391 data->viewport.size.x = allocation->width;
392 data->viewport.size.y = allocation->height;
393 data->viewport.is_defined = TRUE;
395 _adg_update_adjustments(layout);
398 static void
399 _adg_canvas_changed(AdgGtkArea *area, AdgCanvas *old_canvas)
401 AdgGtkLayout *layout;
402 AdgGtkLayoutPrivate *data;
404 layout = (AdgGtkLayout *) area;
405 data = layout->data;
407 if (_ADG_OLD_AREA_CLASS->canvas_changed != NULL)
408 _ADG_OLD_AREA_CLASS->canvas_changed(area, old_canvas);
410 /* By convention, expect the origin of a new canvas to be
411 * on the top/left corner of the allocation area */
412 data->viewport.org.x = 0;
413 data->viewport.org.y = 0;
415 _adg_set_parent_size(layout);
418 static void
419 _adg_extents_changed(AdgGtkArea *area, const CpmlExtents *old_extents)
421 if (_ADG_OLD_AREA_CLASS->extents_changed != NULL)
422 _ADG_OLD_AREA_CLASS->extents_changed(area, old_extents);
424 _adg_update_adjustments((AdgGtkLayout *) area);
427 static void
428 _adg_set_parent_size(AdgGtkLayout *layout)
430 AdgGtkLayoutPrivate *data;
431 GtkWidget *parent;
432 const CpmlExtents *sheet;
433 GtkScrolledWindow *scrolled_window;
435 /* When the widget is realized it is too late to suggest a size */
436 if (GTK_WIDGET_REALIZED(layout))
437 return;
439 data = layout->data;
440 parent = gtk_widget_get_parent((GtkWidget *) layout);
441 if (!GTK_IS_WIDGET(parent))
442 return;
444 sheet = adg_gtk_area_get_extents((AdgGtkArea *) layout);
445 if (sheet == NULL || !sheet->is_defined)
446 return;
448 gtk_widget_set_size_request(parent, sheet->size.x + 2, sheet->size.y + 2);
450 if (GTK_IS_SCROLLED_WINDOW(parent) && !data->policy_stored) {
451 scrolled_window = (GtkScrolledWindow *) parent;
453 gtk_scrolled_window_get_policy(scrolled_window,
454 &data->hpolicy, &data->vpolicy);
455 gtk_scrolled_window_set_policy(scrolled_window,
456 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
458 data->policy_stored = TRUE;
462 static void
463 _adg_set_adjustment(AdgGtkLayout *layout,
464 GtkAdjustment **dst, GtkAdjustment *src)
466 GCallback value_changed;
468 if (*dst == src)
469 return;
471 value_changed = G_CALLBACK(_adg_value_changed);
473 if (*dst != NULL) {
474 /* Release the old adjustment */
475 g_signal_handlers_disconnect_by_func(*dst, (gpointer) value_changed, layout);
476 g_object_unref(*dst);
479 g_signal_connect_swapped(src, "value-changed", value_changed, layout);
480 g_object_ref_sink(src);
482 *dst = src;
486 * _adg_update_adjustments:
487 * @layout: an #AdgGtkLayout
489 * Updates the scrollbars according to the new extents of the canvas
490 * of @area and to the current viewport.
492 * The algorithm uses three local #CpmlExtents variables: the
493 * <var>viewport</var> (what physically shown by the graphic device),
494 * the <var>sheet</var> (the extents of the drawing, margins
495 * included) and <var>surface</var> (a helper variable that is the
496 * union of the previous two extents).
498 static void
499 _adg_update_adjustments(AdgGtkLayout *layout)
501 AdgGtkArea *area;
502 AdgCanvas *canvas;
503 const CpmlExtents *sheet, *viewport;
504 AdgGtkLayoutPrivate *data;
505 GtkAdjustment *hadj, *vadj;
506 CpmlExtents surface;
508 area = (AdgGtkArea *) layout;
509 canvas = adg_gtk_area_get_canvas(area);
510 if (canvas == NULL)
511 return;
513 sheet = adg_gtk_area_get_extents(area);
514 if (sheet == NULL || !sheet->is_defined)
515 return;
517 data = layout->data;
518 hadj = data->hadjustment;
519 vadj = data->vadjustment;
520 viewport = &data->viewport;
521 surface = *sheet;
522 cpml_extents_add(&surface, viewport);
524 if (data->policy_stored) {
525 /* Restore the original policy for the scrollbars */
526 GtkWidget *parent;
527 GtkScrolledWindow *scrolled_window;
529 parent = gtk_widget_get_parent((GtkWidget *) layout);
530 scrolled_window = (GtkScrolledWindow *) parent;
532 gtk_scrolled_window_set_policy(scrolled_window,
533 data->hpolicy, data->vpolicy);
535 data->policy_stored = TRUE;
538 g_object_set(hadj,
539 "lower", surface.org.x,
540 "upper", surface.org.x + surface.size.x,
541 "page-size", viewport->size.x,
542 "value", viewport->org.x,
543 NULL);
544 g_object_set(vadj,
545 "lower", surface.org.y,
546 "upper", surface.org.y + surface.size.y,
547 "page-size", viewport->size.y,
548 "value", viewport->org.y,
549 NULL);
552 static void
553 _adg_value_changed(AdgGtkLayout *layout)
555 AdgGtkArea *area;
556 AdgGtkLayoutPrivate *data;
557 AdgPair org;
558 AdgMatrix map;
560 if (!GTK_WIDGET_REALIZED(layout))
561 return;
563 area = (AdgGtkArea *) layout;
564 data = layout->data;
565 org.x = gtk_adjustment_get_value(data->hadjustment);
566 org.y = gtk_adjustment_get_value(data->vadjustment);
568 cairo_matrix_init_translate(&map, data->viewport.org.x - org.x,
569 data->viewport.org.y - org.y);
570 adg_gtk_area_transform_render_map(area, &map, ADG_TRANSFORM_BEFORE);
572 gtk_widget_queue_draw((GtkWidget *) layout);
573 _adg_update_adjustments(layout);