[AdgLDim] Avoid arrange() on the same data
[adg.git] / adg / adg-stroke.c
blobbe3df1568619878e570f54ed270f79ab8281eb51
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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-stroke
23 * @short_description: A stroked entity
25 * The #AdgStroke object is a stroked representation of an #AdgTrail model.
26 **/
28 /**
29 * AdgStroke:
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
33 **/
36 #include "adg-stroke.h"
37 #include "adg-stroke-private.h"
38 #include "adg-line-style.h"
39 #include "adg-intl.h"
41 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_stroke_parent_class)
44 enum {
45 PROP_0,
46 PROP_DRESS,
47 PROP_TRAIL
50 static void dispose (GObject *object);
51 static void get_property (GObject *object,
52 guint param_id,
53 GValue *value,
54 GParamSpec *pspec);
55 static void set_property (GObject *object,
56 guint param_id,
57 const GValue *value,
58 GParamSpec *pspec);
59 static void arrange (AdgEntity *entity);
60 static void render (AdgEntity *entity,
61 cairo_t *cr);
62 static gboolean set_trail (AdgStroke *stroke,
63 AdgTrail *trail);
64 static void unset_trail (AdgStroke *stroke);
67 G_DEFINE_TYPE(AdgStroke, adg_stroke, ADG_TYPE_ENTITY);
70 static void
71 adg_stroke_class_init(AdgStrokeClass *klass)
73 GObjectClass *gobject_class;
74 AdgEntityClass *entity_class;
75 GParamSpec *param;
77 gobject_class = (GObjectClass *) klass;
78 entity_class = (AdgEntityClass *) klass;
80 g_type_class_add_private(klass, sizeof(AdgStrokePrivate));
82 gobject_class->dispose = dispose;
83 gobject_class->get_property = get_property;
84 gobject_class->set_property = set_property;
86 entity_class->arrange = arrange;
87 entity_class->render = render;
89 param = adg_param_spec_dress("dress",
90 P_("Dress Style"),
91 P_("The dress style to use for stroking this entity"),
92 ADG_DRESS_LINE_REGULAR,
93 G_PARAM_READWRITE);
94 g_object_class_install_property(gobject_class, PROP_DRESS, param);
96 param = g_param_spec_object("trail",
97 P_("Trail"),
98 P_("The trail to be stroked"),
99 ADG_TYPE_TRAIL,
100 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
101 g_object_class_install_property(gobject_class, PROP_TRAIL, param);
104 static void
105 adg_stroke_init(AdgStroke *stroke)
107 AdgStrokePrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(stroke,
108 ADG_TYPE_STROKE,
109 AdgStrokePrivate);
111 data->dress = ADG_DRESS_LINE_REGULAR;
112 data->trail = NULL;
114 adg_entity_set_local_mode((AdgEntity *) stroke, ADG_TRANSFORM_BEFORE);
116 stroke->data = data;
119 static void
120 dispose(GObject *object)
122 AdgStroke *stroke = (AdgStroke *) object;
124 adg_stroke_set_trail(stroke, NULL);
126 if (PARENT_OBJECT_CLASS->dispose != NULL)
127 PARENT_OBJECT_CLASS->dispose(object);
130 static void
131 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
133 AdgStrokePrivate *data = ((AdgStroke *) object)->data;
135 switch (prop_id) {
136 case PROP_DRESS:
137 g_value_set_int(value, data->dress);
138 break;
139 case PROP_TRAIL:
140 g_value_set_object(value, &data->trail);
141 break;
142 default:
143 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
144 break;
148 static void
149 set_property(GObject *object, guint prop_id,
150 const GValue *value, GParamSpec *pspec)
152 AdgStroke *stroke;
153 AdgStrokePrivate *data;
155 stroke = (AdgStroke *) object;
156 data = stroke->data;
158 switch (prop_id) {
159 case PROP_DRESS:
160 adg_dress_set(&data->dress, g_value_get_int(value));
161 break;
162 case PROP_TRAIL:
163 set_trail(stroke, (AdgTrail *) g_value_get_object(value));
164 break;
165 default:
166 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
167 break;
173 * adg_stroke_new:
174 * @trail: the #AdgTrail to stroke
176 * Creates a new stroke entity.
178 * Returns: the newly created stroke entity
180 AdgStroke *
181 adg_stroke_new(AdgTrail *trail)
183 g_return_val_if_fail(ADG_IS_TRAIL(trail), NULL);
185 return g_object_new(ADG_TYPE_STROKE, "trail", trail, NULL);
189 * adg_stroke_get_dress:
190 * @stroke: an #AdgStroke
192 * Gets the line dress to be used in rendering @stroke.
194 * Returns: the current line dress
196 AdgDress
197 adg_stroke_get_dress(AdgStroke *stroke)
199 AdgStrokePrivate *data;
201 g_return_val_if_fail(ADG_IS_STROKE(stroke), ADG_DRESS_UNDEFINED);
203 data = stroke->data;
205 return data->dress;
209 * adg_stroke_set_dress:
210 * @stroke: an #AdgStroke
211 * @dress: the new #AdgDress to use
213 * Sets a new line dress for rendering @stroke. The new dress
214 * must be related to the original dress for this property:
215 * you cannot set a dress used for line styles to a dress
216 * managing fonts.
218 * The check is done by calling adg_dress_are_related() with
219 * @dress and the previous dress as arguments. Check out its
220 * documentation for details on what is a related dress.
222 void
223 adg_stroke_set_dress(AdgStroke *stroke, AdgDress dress)
225 AdgStrokePrivate *data;
227 g_return_if_fail(ADG_IS_STROKE(stroke));
229 data = stroke->data;
231 if (adg_dress_set(&data->dress, dress))
232 g_object_notify((GObject *) stroke, "dress");
236 * adg_stroke_get_trail:
237 * @stroke: an #AdgStroke
239 * Gets the #AdgTrail bound to this @stroke entity.
241 * Returns: the requested #AdgTrail or %NULL on errors
243 AdgTrail *
244 adg_stroke_get_trail(AdgStroke *stroke)
246 AdgStrokePrivate *data;
248 g_return_val_if_fail(ADG_IS_STROKE(stroke), NULL);
250 data = stroke->data;
252 return data->trail;
256 * adg_stroke_set_trail:
257 * @stroke: an #AdgStroke
258 * @trail: the new #AdgTrail to bind
260 * Sets @trail as the new trail to be stroked by @stroke.
262 void
263 adg_stroke_set_trail(AdgStroke *stroke, AdgTrail *trail)
265 g_return_if_fail(ADG_IS_STROKE(stroke));
267 if (set_trail(stroke, trail))
268 g_object_notify((GObject *) stroke, "trail");
272 static void
273 arrange(AdgEntity *entity)
277 static void
278 render(AdgEntity *entity, cairo_t *cr)
280 AdgStroke *stroke;
281 AdgStrokePrivate *data;
282 const cairo_path_t *cairo_path;
284 stroke = (AdgStroke *) entity;
285 data = stroke->data;
286 cairo_path = adg_trail_get_cairo_path(data->trail);
288 if (cairo_path != NULL) {
289 AdgLineStyle *line_style;
290 AdgMatrix ctm;
292 line_style = (AdgLineStyle *) adg_entity_style(entity, data->dress);
293 adg_entity_get_ctm(entity, &ctm);
295 cairo_save(cr);
296 cairo_set_matrix(cr, &ctm);
297 cairo_append_path(cr, cairo_path);
298 cairo_restore(cr);
300 adg_style_apply((AdgStyle *) line_style, cr);
301 cairo_stroke(cr);
305 static gboolean
306 set_trail(AdgStroke *stroke, AdgTrail *trail)
308 AdgEntity *entity;
309 AdgStrokePrivate *data;
311 entity = (AdgEntity *) stroke;
312 data = stroke->data;
314 if (trail == data->trail)
315 return FALSE;
317 if (data->trail != NULL) {
318 g_object_weak_unref((GObject *) data->trail,
319 (GWeakNotify) unset_trail, stroke);
320 adg_model_remove_dependency((AdgModel *) data->trail, entity);
323 data->trail = trail;
325 if (data->trail != NULL) {
326 g_object_weak_ref((GObject *) data->trail,
327 (GWeakNotify) unset_trail, stroke);
328 adg_model_add_dependency((AdgModel *) data->trail, entity);
331 return TRUE;
334 static void
335 unset_trail(AdgStroke *stroke)
337 AdgStrokePrivate *data = stroke->data;
339 if (data->trail != NULL) {
340 data->trail = NULL;
341 adg_entity_invalidate((AdgEntity *) stroke);