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.
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.
34 * All fields are private and should not be used directly.
35 * Use its public methods instead.
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
)
59 static void _adg_dispose (GObject
*object
);
60 static void _adg_get_property (GObject
*object
,
64 static void _adg_set_property (GObject
*object
,
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
,
85 static void _adg_update_adjustments (AdgGtkLayout
*layout
);
86 static void _adg_value_changed (AdgGtkLayout
*layout
);
90 adg_gtk_layout_class_init(AdgGtkLayoutClass
*klass
)
92 GObjectClass
*gobject_class
;
93 GtkWidgetClass
*widget_class
;
94 AdgGtkAreaClass
*area_class
;
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"),
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"),
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
);
148 adg_gtk_layout_init(AdgGtkLayout
*layout
)
150 AdgGtkLayoutPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(layout
,
152 AdgGtkLayoutPrivate
);
154 data
->hadjustment
= NULL
;
155 data
->vadjustment
= NULL
;
156 data
->policy_stored
= FALSE
;
157 data
->viewport
.is_defined
= FALSE
;
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
);
182 _adg_get_property(GObject
*object
, guint prop_id
,
183 GValue
*value
, GParamSpec
*pspec
)
185 AdgGtkLayoutPrivate
*data
= ((AdgGtkLayout
*) object
)->data
;
188 case PROP_HADJUSTMENT
:
189 g_value_set_object(value
, data
->hadjustment
);
191 case PROP_VADJUSTMENT
:
192 g_value_set_object(value
, data
->vadjustment
);
195 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
;
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
);
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
);
227 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
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
255 * Returns: the newly created widget
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().
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.
291 adg_gtk_layout_get_hadjustment(AdgGtkLayout
*layout
)
293 AdgGtkLayoutPrivate
*data
;
295 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
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().
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.
328 adg_gtk_layout_get_vadjustment(AdgGtkLayout
*layout
)
330 AdgGtkLayoutPrivate
*data
;
332 g_return_val_if_fail(ADG_GTK_IS_LAYOUT(layout
), NULL
);
336 return data
->vadjustment
;
341 _adg_set_scroll_adjustments(GtkWidget
*widget
,
342 GtkAdjustment
*hadjustment
,
343 GtkAdjustment
*vadjustment
)
346 "hadjustment", hadjustment
,
347 "vadjustment", vadjustment
,
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
);
363 _adg_size_request(GtkWidget
*widget
, GtkRequisition
*requisition
)
365 AdgGtkLayout
*layout
;
366 AdgGtkLayoutPrivate
*data
;
368 layout
= (AdgGtkLayout
*) widget
;
371 if (_ADG_OLD_WIDGET_CLASS
->size_request
!= NULL
)
372 _ADG_OLD_WIDGET_CLASS
->size_request(widget
, requisition
);
376 _adg_size_allocate(GtkWidget
*widget
, GtkAllocation
*allocation
)
378 AdgGtkLayout
*layout
;
379 AdgGtkLayoutPrivate
*data
;
381 layout
= (AdgGtkLayout
*) widget
;
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
);
399 _adg_canvas_changed(AdgGtkArea
*area
, AdgCanvas
*old_canvas
)
401 AdgGtkLayout
*layout
;
402 AdgGtkLayoutPrivate
*data
;
404 layout
= (AdgGtkLayout
*) area
;
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
);
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
);
428 _adg_set_parent_size(AdgGtkLayout
*layout
)
430 AdgGtkLayoutPrivate
*data
;
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
))
440 parent
= gtk_widget_get_parent((GtkWidget
*) layout
);
441 if (!GTK_IS_WIDGET(parent
))
444 sheet
= adg_gtk_area_get_extents((AdgGtkArea
*) layout
);
445 if (sheet
== NULL
|| !sheet
->is_defined
)
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
;
463 _adg_set_adjustment(AdgGtkLayout
*layout
,
464 GtkAdjustment
**dst
, GtkAdjustment
*src
)
466 GCallback value_changed
;
471 value_changed
= G_CALLBACK(_adg_value_changed
);
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
);
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).
499 _adg_update_adjustments(AdgGtkLayout
*layout
)
503 const CpmlExtents
*sheet
, *viewport
;
504 AdgGtkLayoutPrivate
*data
;
505 GtkAdjustment
*hadj
, *vadj
;
508 area
= (AdgGtkArea
*) layout
;
509 canvas
= adg_gtk_area_get_canvas(area
);
513 sheet
= adg_gtk_area_get_extents(area
);
514 if (sheet
== NULL
|| !sheet
->is_defined
)
518 hadj
= data
->hadjustment
;
519 vadj
= data
->vadjustment
;
520 viewport
= &data
->viewport
;
522 cpml_extents_add(&surface
, viewport
);
524 if (data
->policy_stored
) {
525 /* Restore the original policy for the scrollbars */
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
;
539 "lower", surface
.org
.x
,
540 "upper", surface
.org
.x
+ surface
.size
.x
,
541 "page-size", viewport
->size
.x
,
542 "value", viewport
->org
.x
,
545 "lower", surface
.org
.y
,
546 "upper", surface
.org
.y
+ surface
.size
.y
,
547 "page-size", viewport
->size
.y
,
548 "value", viewport
->org
.y
,
553 _adg_value_changed(AdgGtkLayout
*layout
)
556 AdgGtkLayoutPrivate
*data
;
560 if (!GTK_WIDGET_REALIZED(layout
))
563 area
= (AdgGtkArea
*) layout
;
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
);