build: depends on cairo-gobject if introspection is enabled
[adg.git] / src / adg / adg-alignment.c
bloba9ab51127ec8be81cfac216ec951959ab0f73ee8
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.
21 /**
22 * SECTION:adg-alignment
23 * @short_description: Base class for entity that can contain other entities
25 * The #AdgAlignment is an entity that can contains more sub-entities,
26 * much in the same way as the #AdgContainer does, but allowing the
27 * displacement of its content with an arbitrary fraction dependent
28 * on the content itself.
30 * This shift is computed by multiplying the #AdgAligment:factor
31 * property with the extents of the bare content, with "bare" meaning
32 * the children entities as they are rendered on the global matrix
33 * *without* rotation components.
35 * To specify the alignment fraction, use adg_alignment_set_factor() and
36 * related methods or directly set the #AdgAlignment:factor property.
37 * For example, to center the children either in x and y, you can call
38 * adg_alignment_set_factor_explicit(alignment, 0.5, 0.5). To align them
39 * on the right, specify a (0, 1) factor.
41 * Since: 1.0
42 **/
44 /**
45 * AdgAlignment:
47 * All fields are private and should not be used directly.
48 * Use its public methods instead.
50 * Since: 1.0
51 **/
54 #include "adg-internal.h"
55 #include "adg-entity.h"
56 #include "adg-container.h"
58 #include "adg-alignment.h"
59 #include "adg-alignment-private.h"
62 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_alignment_parent_class)
65 G_DEFINE_TYPE(AdgAlignment, adg_alignment, ADG_TYPE_CONTAINER)
67 enum {
68 PROP_0,
69 PROP_FACTOR
73 static void _adg_get_property (GObject *object,
74 guint param_id,
75 GValue *value,
76 GParamSpec *pspec);
77 static void _adg_set_property (GObject *object,
78 guint prop_id,
79 const GValue *value,
80 GParamSpec *pspec);
81 static void _adg_arrange (AdgEntity *entity);
82 static void _adg_render (AdgEntity *entity,
83 cairo_t *cr);
86 static void
87 adg_alignment_class_init(AdgAlignmentClass *klass)
89 GObjectClass *gobject_class;
90 AdgEntityClass *entity_class;
91 GParamSpec *param;
93 gobject_class = (GObjectClass *) klass;
94 entity_class = (AdgEntityClass *) klass;
96 g_type_class_add_private(klass, sizeof(AdgAlignmentPrivate));
98 gobject_class->get_property = _adg_get_property;
99 gobject_class->set_property = _adg_set_property;
101 entity_class->arrange = _adg_arrange;
102 entity_class->render = _adg_render;
104 param = g_param_spec_boxed("factor",
105 P_("Factor"),
106 P_("Portion of extents, either in x and y, the content will be displaced: a (0.5, 0.5) factor means the origin is the middle point of the extents"),
107 CPML_TYPE_PAIR,
108 G_PARAM_READWRITE);
109 g_object_class_install_property(gobject_class, PROP_FACTOR, param);
112 static void
113 adg_alignment_init(AdgAlignment *alignment)
115 AdgAlignmentPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(alignment,
116 ADG_TYPE_ALIGNMENT,
117 AdgAlignmentPrivate);
119 data->factor.x = 0;
120 data->factor.y = 0;
121 data->shift.x = 0;
122 data->shift.y = 0;
124 alignment->data = data;
127 static void
128 _adg_get_property(GObject *object, guint prop_id,
129 GValue *value, GParamSpec *pspec)
131 AdgAlignmentPrivate *data = ((AdgAlignment *) object)->data;
133 switch (prop_id) {
134 case PROP_FACTOR:
135 g_value_set_boxed(value, &data->factor);
136 break;
137 default:
138 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
139 break;
143 static void
144 _adg_set_property(GObject *object, guint prop_id,
145 const GValue *value, GParamSpec *pspec)
147 AdgAlignmentPrivate *data;
148 const CpmlPair *pair;
150 data = ((AdgAlignment *) object)->data;
152 switch (prop_id) {
153 case PROP_FACTOR:
154 pair = g_value_get_boxed(value);
155 if (! cpml_pair_equal(&data->factor, pair)) {
156 cpml_pair_copy(&data->factor, pair);
157 adg_entity_invalidate((AdgEntity *) object);
159 break;
160 default:
161 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
167 * adg_alignment_new:
168 * @factor: the alignment factor
170 * Creates a new alignment container with the specified factor.
172 * Returns: the newly created alignment or %NULL on errors
174 * Since: 1.0
176 AdgAlignment *
177 adg_alignment_new(const CpmlPair *factor)
179 return g_object_new(ADG_TYPE_ALIGNMENT, "factor", factor, NULL);
183 * adg_alignment_new_explicit:
184 * @x_factor: x component of the factor
185 * @y_factor: y component of the factor
187 * Convenient function that creates a new alignment accepting explicit
188 * factor values.
190 * Returns: the newly created alignment
192 * Since: 1.0
194 AdgAlignment *
195 adg_alignment_new_explicit(gdouble x_factor, gdouble y_factor)
197 CpmlPair factor;
199 factor.x = x_factor;
200 factor.y = y_factor;
202 return adg_alignment_new(&factor);
206 * adg_alignment_set_factor:
207 * @alignment: an #AdgAlignment container
208 * @factor: the new factor
210 * Sets a the #AdgAlignment:factor property to @factor on @alignment.
211 * The factor is applied to the @alignment extents to compute the
212 * displacement of the content, providing a way to for instance center
213 * the content either vertically or horizontally. A pair factor of
214 * (%0.5, %0) means the content will be centered horizontally in
215 * reference to the normal flow without @alignment.
217 * Since: 1.0
219 void
220 adg_alignment_set_factor(AdgAlignment *alignment, const CpmlPair *factor)
222 g_return_if_fail(ADG_IS_ALIGNMENT(alignment));
223 g_object_set(alignment, "factor", factor, NULL);
227 * adg_alignment_set_factor_explicit:
228 * @alignment: an #AdgAlignment container
229 * @x_factor: x component of the factor
230 * @y_factor: y component of the factor
232 * Convenient wrapper around adg_alignment_set_factor() that accepts
233 * explicit factors instead of an #CpmlPair value.
235 * Since: 1.0
237 void
238 adg_alignment_set_factor_explicit(AdgAlignment *alignment,
239 gdouble x_factor, gdouble y_factor)
241 CpmlPair factor;
243 factor.x = x_factor;
244 factor.y = y_factor;
246 adg_alignment_set_factor(alignment, &factor);
250 * adg_alignment_get_factor:
251 * @alignment: an #AdgAlignment container
253 * Gets the value of the #AdgAlignment:factor property. The returned
254 * pair is owned by @alignment and must not be modified or freed.
256 * Returns: the factor pair
258 * Since: 1.0
260 const CpmlPair *
261 adg_alignment_get_factor(AdgAlignment *alignment)
263 AdgAlignmentPrivate *data;
265 g_return_val_if_fail(ADG_IS_ALIGNMENT(alignment), NULL);
267 data = alignment->data;
269 return &data->factor;
273 static void
274 _adg_arrange(AdgEntity *entity)
276 AdgAlignmentPrivate *data;
277 const CpmlExtents *extents;
278 CpmlExtents new_extents;
279 cairo_matrix_t ctm, ctm_inverted, old_map;
281 if (_ADG_OLD_ENTITY_CLASS->arrange == NULL)
282 return;
284 data = ((AdgAlignment *) entity)->data;
285 extents = adg_entity_get_extents(entity);
286 data->shift.x = 0;
287 data->shift.y = 0;
289 /* The shift is performed only when relevant */
290 if (data->factor.x != 0 || data->factor.y != 0) {
291 /* Force the ctm to be the identity matrix */
292 adg_matrix_copy(&old_map, adg_entity_get_global_map(entity));
293 adg_matrix_copy(&ctm, adg_entity_get_global_map(entity));
294 adg_matrix_transform(&ctm, adg_entity_get_local_matrix(entity),
295 ADG_TRANSFORM_AFTER);
296 adg_matrix_copy(&ctm_inverted, &ctm);
297 cairo_matrix_invert(&ctm_inverted);
298 adg_entity_transform_global_map(entity, &ctm_inverted,
299 ADG_TRANSFORM_AFTER);
300 adg_entity_global_changed(entity);
302 /* Calculating the shift */
303 _ADG_OLD_ENTITY_CLASS->arrange(entity);
304 extents = adg_entity_get_extents(entity);
305 if (extents->is_defined) {
306 data->shift.x = -extents->size.x * data->factor.x;
307 data->shift.y = -extents->size.y * data->factor.y;
308 cpml_vector_transform(&data->shift, &ctm);
311 /* Restore the old global map */
312 adg_entity_set_global_map(entity, &old_map);
313 adg_entity_global_changed(entity);
316 /* Add the shift to the extents */
317 _ADG_OLD_ENTITY_CLASS->arrange(entity);
318 cpml_extents_copy(&new_extents, adg_entity_get_extents(entity));
319 new_extents.org.x += data->shift.x;
320 new_extents.org.y += data->shift.y;
321 adg_entity_set_extents(entity, &new_extents);
324 static void
325 _adg_render(AdgEntity *entity, cairo_t *cr)
327 AdgAlignmentPrivate *data;
329 if (_ADG_OLD_ENTITY_CLASS->render == NULL)
330 return;
332 data = ((AdgAlignment *) entity)->data;
334 cairo_translate(cr, data->shift.x, data->shift.y);
335 _ADG_OLD_ENTITY_CLASS->render(entity, cr);