1 #define __SP_LPE_ITEM_CPP__
3 /** \file
4 * Base class for live path effect items
5 */
6 /*
7 * Authors:
8 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9 * Bastien Bouclet <bgkweb@gmail.com>
10 *
11 * Copyright (C) 2008 authors
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include "live_effects/effect.h"
21 #include "live_effects/lpeobject.h"
22 #include "live_effects/lpeobject-reference.h"
24 #include "sp-path.h"
25 #include "sp-item-group.h"
26 #include "macros.h"
27 #include "attributes.h"
28 #include "sp-lpe-item.h"
29 #include "xml/repr.h"
30 #include "uri.h"
32 /* LPEItem base class */
34 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
35 static void sp_lpe_item_init(SPLPEItem *lpe_item);
36 static void sp_lpe_item_finalize(GObject *object);
38 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
39 static void sp_lpe_item_release(SPObject *object);
40 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
41 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
42 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
43 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
45 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
46 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
48 static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
49 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
51 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
52 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
54 static SPItemClass *parent_class;
56 GType
57 sp_lpe_item_get_type()
58 {
59 static GType lpe_item_type = 0;
61 if (!lpe_item_type) {
62 GTypeInfo lpe_item_info = {
63 sizeof(SPLPEItemClass),
64 NULL, NULL,
65 (GClassInitFunc) sp_lpe_item_class_init,
66 NULL, NULL,
67 sizeof(SPLPEItem),
68 16,
69 (GInstanceInitFunc) sp_lpe_item_init,
70 NULL, /* value_table */
71 };
72 lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
73 }
74 return lpe_item_type;
75 }
77 static void
78 sp_lpe_item_class_init(SPLPEItemClass *klass)
79 {
80 GObjectClass *gobject_class;
81 SPObjectClass *sp_object_class;
83 gobject_class = (GObjectClass *) klass;
84 sp_object_class = (SPObjectClass *) klass;
85 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
87 gobject_class->finalize = sp_lpe_item_finalize;
89 sp_object_class->build = sp_lpe_item_build;
90 sp_object_class->release = sp_lpe_item_release;
91 sp_object_class->set = sp_lpe_item_set;
92 sp_object_class->update = sp_lpe_item_update;
93 sp_object_class->modified = sp_lpe_item_modified;
94 sp_object_class->write = sp_lpe_item_write;
95 sp_object_class->child_added = sp_lpe_item_child_added;
96 sp_object_class->remove_child = sp_lpe_item_remove_child;
98 klass->update_patheffect = NULL;
99 }
101 static void
102 sp_lpe_item_init(SPLPEItem *lpeitem)
103 {
104 lpeitem->path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
105 new (&lpeitem->lpe_modified_connection) sigc::connection();
106 }
108 static void
109 sp_lpe_item_finalize(GObject *object)
110 {
111 if (((GObjectClass *) (parent_class))->finalize) {
112 (* ((GObjectClass *) (parent_class))->finalize)(object);
113 }
114 }
116 /**
117 * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables. For this to get called,
118 * our name must be associated with a repr via "sp_object_type_register". Best done through
119 * sp-object-repr.cpp's repr_name_entries array.
120 */
121 static void
122 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
123 {
124 SP_LPE_ITEM(object)->path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
126 sp_object_read_attr(object, "inkscape:path-effect");
128 if (((SPObjectClass *) parent_class)->build) {
129 ((SPObjectClass *) parent_class)->build(object, document, repr);
130 }
131 }
133 /**
134 * Drops any allocated memory.
135 */
136 static void
137 sp_lpe_item_release(SPObject *object)
138 {
139 SPLPEItem *lpeitem;
140 lpeitem = (SPLPEItem *) object;
142 lpeitem->path_effect_ref->detach();
144 lpeitem->lpe_modified_connection.disconnect();
145 lpeitem->lpe_modified_connection.~connection();
147 if (((SPObjectClass *) parent_class)->release)
148 ((SPObjectClass *) parent_class)->release(object);
149 }
151 /**
152 * Sets a specific value in the SPLPEItem.
153 */
154 static void
155 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
156 {
157 SPLPEItem *lpeitem = (SPLPEItem *) object;
159 switch (key) {
160 case SP_ATTR_INKSCAPE_PATH_EFFECT:
161 if ( value && lpeitem->path_effect_ref->lpeobject_href
162 && ( strcmp(value, lpeitem->path_effect_ref->lpeobject_href) == 0 ) ) {
163 /* No change, do nothing. */
164 } else {
165 if (value) {
166 // Now do the attaching, which emits the changed signal.
167 try {
168 lpeitem->path_effect_ref->link((gchar*)value);
169 } catch (Inkscape::BadURIException &e) {
170 g_warning("%s", e.what());
171 lpeitem->path_effect_ref->detach();
172 }
173 } else {
174 // Detach, which emits the changed signal.
175 lpeitem->path_effect_ref->detach();
176 }
177 }
178 break;
179 default:
180 if (((SPObjectClass *) parent_class)->set) {
181 ((SPObjectClass *) parent_class)->set(object, key, value);
182 }
183 break;
184 }
185 }
187 /**
188 * Receives update notifications.
189 */
190 static void
191 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
192 {
193 if (((SPObjectClass *) parent_class)->update) {
194 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
195 }
196 }
198 /**
199 * Sets modified flag for all sub-item views.
200 */
201 static void
202 sp_lpe_item_modified (SPObject *object, unsigned int flags)
203 {
204 if (((SPObjectClass *) (parent_class))->modified) {
205 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
206 }
207 }
209 /**
210 * Writes its settings to an incoming repr object, if any.
211 */
212 static Inkscape::XML::Node *
213 sp_lpe_item_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
214 {
215 SPLPEItem *lpeitem = (SPLPEItem *) object;
217 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
218 repr->setAttribute("inkscape:path-effect", lpeitem->path_effect_ref->lpeobject_href);
219 } else {
220 repr->setAttribute("inkscape:path-effect", NULL);
221 }
223 if (((SPObjectClass *)(parent_class))->write) {
224 ((SPObjectClass *)(parent_class))->write(object, repr, flags);
225 }
227 return repr;
228 }
231 LivePathEffectObject *
232 sp_lpe_item_get_livepatheffectobject(SPLPEItem *lpeitem) {
233 if (!lpeitem) return NULL;
235 if (sp_lpe_item_has_path_effect(lpeitem)) {
236 return lpeitem->path_effect_ref->lpeobject;
237 } else {
238 return NULL;
239 }
240 }
242 Inkscape::LivePathEffect::Effect *
243 sp_lpe_item_get_livepatheffect(SPLPEItem *lpeitem) {
244 if (!lpeitem) return NULL;
246 LivePathEffectObject * lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
247 if (lpeobj)
248 return lpeobj->lpe;
249 else
250 return NULL;
251 }
253 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
254 if (!lpeitem) return;
255 if (!curve) return;
257 if (sp_lpe_item_has_path_effect(lpeitem)) {
258 LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
259 lpeobj->lpe->doEffect(curve);
260 }
262 SPObject *parent = lpeitem->parent;
263 if (parent && SP_IS_LPE_ITEM(parent))
264 sp_lpe_item_perform_path_effect(SP_LPE_ITEM(parent), curve);
265 }
267 /**
268 * Calls any registered handlers for the update_patheffect action
269 */
270 void
271 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool write)
272 {
273 #ifdef SHAPE_VERBOSE
274 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
275 #endif
276 g_return_if_fail (lpeitem != NULL);
277 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
279 if (sp_lpe_item_has_path_effect(lpeitem)) {
280 LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
281 lpeobj->lpe->doBeforeEffect(lpeitem);
282 }
284 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect) {
285 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect (lpeitem, write);
286 }
287 }
289 /**
290 * Gets called when (re)attached to another lpeobject.
291 */
292 static void
293 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
294 {
295 if (old_ref) {
296 sp_signal_disconnect_by_data(old_ref, lpeitem);
297 }
298 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
299 {
300 lpeitem->lpe_modified_connection.disconnect();
301 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
302 lpeobject_ref_modified(ref, 0, lpeitem);
303 }
304 }
306 /**
307 * Gets called when lpeobject repr contents change: i.e. parameter change.
308 */
309 static void
310 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
311 {
312 sp_lpe_item_update_patheffect (lpeitem, true);
313 }
315 static void
316 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
317 {
318 if (SP_IS_GROUP(lpeitem)) {
319 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
320 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
321 SPObject *subitem = static_cast<SPObject *>(iter->data);
322 if (SP_IS_LPE_ITEM(subitem)) {
323 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
324 }
325 }
326 }
327 else if (SP_IS_PATH(lpeitem)) {
328 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
329 if ( !pathrepr->attribute("inkscape:original-d") ) {
330 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
331 }
332 }
333 }
335 static void
336 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
337 {
338 if (SP_IS_GROUP(lpeitem)) {
339 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
340 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
341 SPObject *subitem = static_cast<SPObject *>(iter->data);
342 if (SP_IS_LPE_ITEM(subitem)) {
343 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
344 }
345 }
346 }
347 else if (SP_IS_PATH(lpeitem)) {
348 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
349 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
350 && repr->attribute("inkscape:original-d")) {
351 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
352 repr->setAttribute("inkscape:original-d", NULL);
353 }
354 else {
355 sp_lpe_item_update_patheffect(lpeitem, true);
356 }
357 }
358 }
360 void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, gchar *value)
361 {
362 if (!value) {
363 sp_lpe_item_remove_path_effect(lpeitem, false);
364 } else {
365 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", value);
367 // make sure there is an original-d for paths!!!
368 sp_lpe_item_create_original_path_recursive(lpeitem);
369 }
370 }
372 void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
373 {
374 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
375 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
376 sp_lpe_item_set_path_effect(lpeitem, hrefstr);
377 g_free(hrefstr);
378 }
380 void sp_lpe_item_remove_path_effect(SPLPEItem *lpeitem, bool keep_paths)
381 {
382 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
383 repr->setAttribute("inkscape:path-effect", NULL);
385 if (!keep_paths) {
386 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
387 }
388 }
390 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
391 {
392 return lpeitem->path_effect_ref && lpeitem->path_effect_ref->lpeobject;
393 }
395 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
396 {
397 SPObject *parent = lpeitem->parent;
398 if (parent && SP_IS_LPE_ITEM(parent)) {
399 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
400 }
401 else {
402 return sp_lpe_item_has_path_effect(lpeitem);
403 }
404 }
406 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
407 {
408 LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
409 if (lpeobj && lpeobj->lpe) {
410 lpeobj->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
411 }
412 }
414 static void
415 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
416 {
417 if (((SPObjectClass *) (parent_class))->child_added)
418 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
420 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
421 SPObject *ochild = sp_object_get_child_by_repr(object, child);
422 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
423 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
424 }
425 }
426 }
428 static void
429 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
430 {
431 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
432 SPObject *ochild = sp_object_get_child_by_repr(object, child);
433 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
434 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
435 }
436 }
438 if (((SPObjectClass *) (parent_class))->remove_child)
439 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
440 }
442 /*
443 Local Variables:
444 mode:c++
445 c-file-style:"stroustrup"
446 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
447 indent-tabs-mode:nil
448 fill-column:99
449 End:
450 */
451 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :