1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 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(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
))
95 _adg_set_parent_size(AdgGtkLayout
*layout
)
98 AdgGtkLayoutPrivate
*data
;
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
))
110 parent
= gtk_widget_get_parent(widget
);
111 if (!GTK_IS_WIDGET(parent
))
114 sheet
= adg_gtk_area_get_extents((AdgGtkArea
*) layout
);
115 if (sheet
== NULL
|| !sheet
->is_defined
)
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
;
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).
157 _adg_update_adjustments(AdgGtkLayout
*layout
)
161 const CpmlExtents
*sheet
, *viewport
;
162 AdgGtkLayoutPrivate
*data
;
163 GtkAdjustment
*hadj
, *vadj
;
166 area
= (AdgGtkArea
*) layout
;
167 canvas
= adg_gtk_area_get_canvas(area
);
171 sheet
= adg_gtk_area_get_extents(area
);
172 if (sheet
== NULL
|| !sheet
->is_defined
)
176 hadj
= data
->hadjustment
;
177 vadj
= data
->vadjustment
;
178 viewport
= &data
->viewport
;
180 cpml_extents_add(&surface
, viewport
);
182 if (data
->policy_stored
) {
183 /* Restore the original policy for the scrollbars */
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
;
197 "lower", surface
.org
.x
,
198 "upper", surface
.org
.x
+ surface
.size
.x
,
199 "page-size", viewport
->size
.x
,
200 "value", viewport
->org
.x
,
203 "lower", surface
.org
.y
,
204 "upper", surface
.org
.y
+ surface
.size
.y
,
205 "page-size", viewport
->size
.y
,
206 "value", viewport
->org
.y
,
211 _adg_size_allocate(GtkWidget
*widget
, GtkAllocation
*allocation
)
213 AdgGtkLayout
*layout
;
214 AdgGtkLayoutPrivate
*data
;
216 layout
= (AdgGtkLayout
*) widget
;
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
);
234 _adg_canvas_changed(AdgGtkArea
*area
, AdgCanvas
*old_canvas
)
236 AdgGtkLayout
*layout
;
237 AdgGtkLayoutPrivate
*data
;
239 layout
= (AdgGtkLayout
*) area
;
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
);
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
);
263 _adg_value_changed(AdgGtkLayout
*layout
)
267 AdgGtkLayoutPrivate
*data
;
271 widget
= (GtkWidget
*) layout
;
273 if (!gtk_widget_get_realized(widget
))
276 area
= (AdgGtkArea
*) layout
;
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
);
290 _adg_set_adjustment(AdgGtkLayout
*layout
,
291 GtkAdjustment
**dst
, GtkAdjustment
*src
)
293 GCallback value_changed
;
298 value_changed
= G_CALLBACK(_adg_value_changed
);
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
);
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
);
332 _adg_get_property(GObject
*object
, guint prop_id
,
333 GValue
*value
, GParamSpec
*pspec
)
335 AdgGtkLayoutPrivate
*data
= ((AdgGtkLayout
*) object
)->data
;
338 case PROP_HADJUSTMENT
:
339 g_value_set_object(value
, data
->hadjustment
);
341 case PROP_VADJUSTMENT
:
342 g_value_set_object(value
, data
->vadjustment
);
345 case PROP_HSCROLL_POLICY
:
346 g_value_set_enum(value
, data
->hpolicy
);
348 case PROP_VSCROLL_POLICY
:
349 g_value_set_enum(value
, data
->vpolicy
);
353 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
;
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
);
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
);
385 case PROP_HSCROLL_POLICY
:
386 data
->hpolicy
= g_value_get_enum(value
);
388 case PROP_VSCROLL_POLICY
:
389 data
->vpolicy
= g_value_get_enum(value
);
393 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
;
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"),
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"),
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.
446 widget_class
->set_scroll_adjustments_signal
=
447 g_signal_new_class_handler("set-scroll-adjustments", ADG_GTK_TYPE_LAYOUT
,
449 G_CALLBACK(_adg_set_scroll_adjustments
),
451 adg_marshal_VOID__OBJECT_OBJECT
,
453 GTK_TYPE_ADJUSTMENT
, GTK_TYPE_ADJUSTMENT
);
457 g_object_class_override_property(gobject_class
, PROP_HADJUSTMENT
,
459 g_object_class_override_property(gobject_class
, PROP_HSCROLL_POLICY
,
461 g_object_class_override_property(gobject_class
, PROP_VADJUSTMENT
,
463 g_object_class_override_property(gobject_class
, PROP_VSCROLL_POLICY
,
469 adg_gtk_layout_init(AdgGtkLayout
*layout
)
471 AdgGtkLayoutPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(layout
,
473 AdgGtkLayoutPrivate
);
475 data
->hadjustment
= NULL
;
476 data
->vadjustment
= NULL
;
477 data
->policy_stored
= FALSE
;
478 data
->viewport
.is_defined
= FALSE
;
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.
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.
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().
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.
555 adg_gtk_layout_get_hadjustment(AdgGtkLayout
*layout
)
557 AdgGtkLayoutPrivate
*data
;
559 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
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().
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.
601 adg_gtk_layout_get_vadjustment(AdgGtkLayout
*layout
)
603 AdgGtkLayoutPrivate
*data
;
605 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
609 return data
->vadjustment
;