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.
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)
60 G_DEFINE_TYPE(AdgGtkLayout
, adg_gtk_layout
, ADG_GTK_TYPE_AREA
)
69 static void _adg_dispose (GObject
*object
);
70 static void _adg_get_property (GObject
*object
,
74 static void _adg_set_property (GObject
*object
,
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
,
93 static void _adg_update_adjustments (AdgGtkLayout
*layout
);
94 static void _adg_value_changed (AdgGtkLayout
*layout
);
98 adg_gtk_layout_class_init(AdgGtkLayoutClass
*klass
)
100 GObjectClass
*gobject_class
;
101 GtkWidgetClass
*widget_class
;
102 AdgGtkAreaClass
*area_class
;
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"),
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"),
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.
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
);
157 adg_gtk_layout_init(AdgGtkLayout
*layout
)
159 AdgGtkLayoutPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(layout
,
161 AdgGtkLayoutPrivate
);
163 data
->hadjustment
= NULL
;
164 data
->vadjustment
= NULL
;
165 data
->policy_stored
= FALSE
;
166 data
->viewport
.is_defined
= FALSE
;
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
);
191 _adg_get_property(GObject
*object
, guint prop_id
,
192 GValue
*value
, GParamSpec
*pspec
)
194 AdgGtkLayoutPrivate
*data
= ((AdgGtkLayout
*) object
)->data
;
197 case PROP_HADJUSTMENT
:
198 g_value_set_object(value
, data
->hadjustment
);
200 case PROP_VADJUSTMENT
:
201 g_value_set_object(value
, data
->vadjustment
);
204 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
;
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
);
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
);
236 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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.
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
266 * Returns: (transfer full): the newly created widget.
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().
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.
313 adg_gtk_layout_get_hadjustment(AdgGtkLayout
*layout
)
315 AdgGtkLayoutPrivate
*data
;
317 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
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().
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.
359 adg_gtk_layout_get_vadjustment(AdgGtkLayout
*layout
)
361 AdgGtkLayoutPrivate
*data
;
363 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
367 return data
->vadjustment
;
372 _adg_set_scroll_adjustments(GtkWidget
*widget
,
373 GtkAdjustment
*hadjustment
,
374 GtkAdjustment
*vadjustment
)
377 "hadjustment", hadjustment
,
378 "vadjustment", vadjustment
,
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
);
394 _adg_size_allocate(GtkWidget
*widget
, GtkAllocation
*allocation
)
396 AdgGtkLayout
*layout
;
397 AdgGtkLayoutPrivate
*data
;
399 layout
= (AdgGtkLayout
*) widget
;
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
);
417 _adg_canvas_changed(AdgGtkArea
*area
, AdgCanvas
*old_canvas
)
419 AdgGtkLayout
*layout
;
420 AdgGtkLayoutPrivate
*data
;
422 layout
= (AdgGtkLayout
*) area
;
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
);
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
);
446 _adg_set_parent_size(AdgGtkLayout
*layout
)
448 AdgGtkLayoutPrivate
*data
;
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
))
458 parent
= gtk_widget_get_parent((GtkWidget
*) layout
);
459 if (!GTK_IS_WIDGET(parent
))
462 sheet
= adg_gtk_area_get_extents((AdgGtkArea
*) layout
);
463 if (sheet
== NULL
|| !sheet
->is_defined
)
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
;
481 _adg_set_adjustment(AdgGtkLayout
*layout
,
482 GtkAdjustment
**dst
, GtkAdjustment
*src
)
484 GCallback value_changed
;
489 value_changed
= G_CALLBACK(_adg_value_changed
);
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
);
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).
519 _adg_update_adjustments(AdgGtkLayout
*layout
)
523 const CpmlExtents
*sheet
, *viewport
;
524 AdgGtkLayoutPrivate
*data
;
525 GtkAdjustment
*hadj
, *vadj
;
528 area
= (AdgGtkArea
*) layout
;
529 canvas
= adg_gtk_area_get_canvas(area
);
533 sheet
= adg_gtk_area_get_extents(area
);
534 if (sheet
== NULL
|| !sheet
->is_defined
)
538 hadj
= data
->hadjustment
;
539 vadj
= data
->vadjustment
;
540 viewport
= &data
->viewport
;
542 cpml_extents_add(&surface
, viewport
);
544 if (data
->policy_stored
) {
545 /* Restore the original policy for the scrollbars */
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
;
559 "lower", surface
.org
.x
,
560 "upper", surface
.org
.x
+ surface
.size
.x
,
561 "page-size", viewport
->size
.x
,
562 "value", viewport
->org
.x
,
565 "lower", surface
.org
.y
,
566 "upper", surface
.org
.y
+ surface
.size
.y
,
567 "page-size", viewport
->size
.y
,
568 "value", viewport
->org
.y
,
573 _adg_value_changed(AdgGtkLayout
*layout
)
576 AdgGtkLayoutPrivate
*data
;
580 if (!GTK_WIDGET_REALIZED(layout
))
583 area
= (AdgGtkArea
*) layout
;
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
);