4af29b052ada7c4490b9af924d89196368cac165
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_WITH_CODE(AdgGtkLayout
, adg_gtk_layout
, ADG_GTK_TYPE_AREA
,
61 G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE
, NULL
))
72 static void _adg_dispose (GObject
*object
);
73 static void _adg_get_property (GObject
*object
,
77 static void _adg_set_property (GObject
*object
,
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
;
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
,
122 g_object_class_override_property(gobject_class
, PROP_HSCROLL_POLICY
,
124 g_object_class_override_property(gobject_class
, PROP_VADJUSTMENT
,
126 g_object_class_override_property(gobject_class
, PROP_VSCROLL_POLICY
,
131 adg_gtk_layout_init(AdgGtkLayout
*layout
)
133 AdgGtkLayoutPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(layout
,
135 AdgGtkLayoutPrivate
);
137 data
->hadjustment
= NULL
;
138 data
->vadjustment
= NULL
;
139 data
->policy_stored
= FALSE
;
140 data
->viewport
.is_defined
= FALSE
;
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
);
165 _adg_get_property(GObject
*object
, guint prop_id
,
166 GValue
*value
, GParamSpec
*pspec
)
168 AdgGtkLayoutPrivate
*data
= ((AdgGtkLayout
*) object
)->data
;
171 case PROP_HADJUSTMENT
:
172 g_value_set_object(value
, data
->hadjustment
);
174 case PROP_HSCROLL_POLICY
:
175 g_value_set_enum(value
, data
->hpolicy
);
177 case PROP_VADJUSTMENT
:
178 g_value_set_object(value
, data
->vadjustment
);
180 case PROP_VSCROLL_POLICY
:
181 g_value_set_enum(value
, data
->vpolicy
);
184 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
;
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
);
208 case PROP_HSCROLL_POLICY
:
209 data
->hpolicy
= g_value_get_enum(value
);
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
);
218 case PROP_VSCROLL_POLICY
:
219 data
->vpolicy
= g_value_get_enum(value
);
222 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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.
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
252 * Returns: (transfer full): the newly created widget.
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().
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.
299 adg_gtk_layout_get_hadjustment(AdgGtkLayout
*layout
)
301 AdgGtkLayoutPrivate
*data
;
303 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
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().
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.
345 adg_gtk_layout_get_vadjustment(AdgGtkLayout
*layout
)
347 AdgGtkLayoutPrivate
*data
;
349 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
353 return data
->vadjustment
;
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
);
369 _adg_size_allocate(GtkWidget
*widget
, GtkAllocation
*allocation
)
371 AdgGtkLayout
*layout
;
372 AdgGtkLayoutPrivate
*data
;
374 layout
= (AdgGtkLayout
*) widget
;
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
);
392 _adg_canvas_changed(AdgGtkArea
*area
, AdgCanvas
*old_canvas
)
394 AdgGtkLayout
*layout
;
395 AdgGtkLayoutPrivate
*data
;
397 layout
= (AdgGtkLayout
*) area
;
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
);
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
);
421 _adg_set_parent_size(AdgGtkLayout
*layout
)
424 AdgGtkLayoutPrivate
*data
;
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
))
436 parent
= gtk_widget_get_parent(widget
);
437 if (!GTK_IS_WIDGET(parent
))
440 sheet
= adg_gtk_area_get_extents((AdgGtkArea
*) layout
);
441 if (sheet
== NULL
|| !sheet
->is_defined
)
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
;
459 _adg_set_adjustment(AdgGtkLayout
*layout
,
460 GtkAdjustment
**dst
, GtkAdjustment
*src
)
462 GCallback value_changed
;
467 value_changed
= G_CALLBACK(_adg_value_changed
);
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
);
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).
497 _adg_update_adjustments(AdgGtkLayout
*layout
)
501 const CpmlExtents
*sheet
, *viewport
;
502 AdgGtkLayoutPrivate
*data
;
503 GtkAdjustment
*hadj
, *vadj
;
506 area
= (AdgGtkArea
*) layout
;
507 canvas
= adg_gtk_area_get_canvas(area
);
511 sheet
= adg_gtk_area_get_extents(area
);
512 if (sheet
== NULL
|| !sheet
->is_defined
)
516 hadj
= data
->hadjustment
;
517 vadj
= data
->vadjustment
;
518 viewport
= &data
->viewport
;
520 cpml_extents_add(&surface
, viewport
);
522 if (data
->policy_stored
) {
523 /* Restore the original policy for the scrollbars */
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
;
537 "lower", surface
.org
.x
,
538 "upper", surface
.org
.x
+ surface
.size
.x
,
539 "page-size", viewport
->size
.x
,
540 "value", viewport
->org
.x
,
543 "lower", surface
.org
.y
,
544 "upper", surface
.org
.y
+ surface
.size
.y
,
545 "page-size", viewport
->size
.y
,
546 "value", viewport
->org
.y
,
551 _adg_value_changed(AdgGtkLayout
*layout
)
555 AdgGtkLayoutPrivate
*data
;
559 widget
= (GtkWidget
*) layout
;
561 if (!gtk_widget_get_realized(widget
))
564 area
= (AdgGtkArea
*) layout
;
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
);