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 "streq.h"
27 #include "macros.h"
28 #include "attributes.h"
29 #include "sp-lpe-item.h"
30 #include "xml/repr.h"
31 #include "uri.h"
33 /* LPEItem base class */
35 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
36 static void sp_lpe_item_init(SPLPEItem *lpe_item);
37 static void sp_lpe_item_finalize(GObject *object);
39 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
40 static void sp_lpe_item_release(SPObject *object);
41 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
42 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
43 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
44 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
46 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
47 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
49 static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
50 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
52 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
53 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
55 static SPItemClass *parent_class;
57 GType
58 sp_lpe_item_get_type()
59 {
60 static GType lpe_item_type = 0;
62 if (!lpe_item_type) {
63 GTypeInfo lpe_item_info = {
64 sizeof(SPLPEItemClass),
65 NULL, NULL,
66 (GClassInitFunc) sp_lpe_item_class_init,
67 NULL, NULL,
68 sizeof(SPLPEItem),
69 16,
70 (GInstanceInitFunc) sp_lpe_item_init,
71 NULL, /* value_table */
72 };
73 lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
74 }
75 return lpe_item_type;
76 }
78 static void
79 sp_lpe_item_class_init(SPLPEItemClass *klass)
80 {
81 GObjectClass *gobject_class;
82 SPObjectClass *sp_object_class;
84 gobject_class = (GObjectClass *) klass;
85 sp_object_class = (SPObjectClass *) klass;
86 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
88 gobject_class->finalize = sp_lpe_item_finalize;
90 sp_object_class->build = sp_lpe_item_build;
91 sp_object_class->release = sp_lpe_item_release;
92 sp_object_class->set = sp_lpe_item_set;
93 sp_object_class->update = sp_lpe_item_update;
94 sp_object_class->modified = sp_lpe_item_modified;
95 sp_object_class->write = sp_lpe_item_write;
96 sp_object_class->child_added = sp_lpe_item_child_added;
97 sp_object_class->remove_child = sp_lpe_item_remove_child;
99 klass->update_patheffect = NULL;
100 }
102 static void
103 sp_lpe_item_init(SPLPEItem *lpeitem)
104 {
105 lpeitem->path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
106 new (&lpeitem->lpe_modified_connection) sigc::connection();
107 }
109 static void
110 sp_lpe_item_finalize(GObject *object)
111 {
112 if (((GObjectClass *) (parent_class))->finalize) {
113 (* ((GObjectClass *) (parent_class))->finalize)(object);
114 }
115 }
117 /**
118 * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables. For this to get called,
119 * our name must be associated with a repr via "sp_object_type_register". Best done through
120 * sp-object-repr.cpp's repr_name_entries array.
121 */
122 static void
123 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
124 {
125 SP_LPE_ITEM(object)->path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
127 sp_object_read_attr(object, "inkscape:path-effect");
129 if (((SPObjectClass *) parent_class)->build) {
130 ((SPObjectClass *) parent_class)->build(object, document, repr);
131 }
132 }
134 /**
135 * Drops any allocated memory.
136 */
137 static void
138 sp_lpe_item_release(SPObject *object)
139 {
140 SPLPEItem *lpeitem;
141 lpeitem = (SPLPEItem *) object;
143 lpeitem->path_effect_ref->detach();
145 lpeitem->lpe_modified_connection.disconnect();
146 lpeitem->lpe_modified_connection.~connection();
148 if (((SPObjectClass *) parent_class)->release)
149 ((SPObjectClass *) parent_class)->release(object);
150 }
152 /**
153 * Sets a specific value in the SPLPEItem.
154 */
155 static void
156 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
157 {
158 SPLPEItem *lpeitem = (SPLPEItem *) object;
160 switch (key) {
161 case SP_ATTR_INKSCAPE_PATH_EFFECT:
162 if ( value && lpeitem->path_effect_ref->lpeobject_href
163 && streq(value, lpeitem->path_effect_ref->lpeobject_href) ) {
164 /* No change, do nothing. */
165 } else {
166 if (value) {
167 // Now do the attaching, which emits the changed signal.
168 try {
169 lpeitem->path_effect_ref->link((gchar*)value);
170 } catch (Inkscape::BadURIException &e) {
171 g_warning("%s", e.what());
172 lpeitem->path_effect_ref->detach();
173 }
174 } else {
175 // Detach, which emits the changed signal.
176 lpeitem->path_effect_ref->detach();
177 }
178 }
179 break;
180 default:
181 if (((SPObjectClass *) parent_class)->set) {
182 ((SPObjectClass *) parent_class)->set(object, key, value);
183 }
184 break;
185 }
186 }
188 /**
189 * Receives update notifications.
190 */
191 static void
192 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
193 {
194 if (((SPObjectClass *) parent_class)->update) {
195 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
196 }
197 }
199 /**
200 * Sets modified flag for all sub-item views.
201 */
202 static void
203 sp_lpe_item_modified (SPObject *object, unsigned int flags)
204 {
205 if (((SPObjectClass *) (parent_class))->modified) {
206 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
207 }
208 }
210 /**
211 * Writes its settings to an incoming repr object, if any.
212 */
213 static Inkscape::XML::Node *
214 sp_lpe_item_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
215 {
216 SPLPEItem *lpeitem = (SPLPEItem *) object;
218 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
219 repr->setAttribute("inkscape:path-effect", lpeitem->path_effect_ref->lpeobject_href);
220 } else {
221 repr->setAttribute("inkscape:path-effect", NULL);
222 }
224 if (((SPObjectClass *)(parent_class))->write) {
225 ((SPObjectClass *)(parent_class))->write(object, repr, flags);
226 }
228 return repr;
229 }
232 LivePathEffectObject *
233 sp_lpe_item_get_livepatheffectobject(SPLPEItem *lpeitem) {
234 if (!lpeitem) return NULL;
236 if (sp_lpe_item_has_path_effect(lpeitem)) {
237 return lpeitem->path_effect_ref->lpeobject;
238 } else {
239 return NULL;
240 }
241 }
243 Inkscape::LivePathEffect::Effect *
244 sp_lpe_item_get_livepatheffect(SPLPEItem *lpeitem) {
245 if (!lpeitem) return NULL;
247 LivePathEffectObject * lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
248 if (lpeobj)
249 return lpeobj->lpe;
250 else
251 return NULL;
252 }
254 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
255 if (!lpeitem) return;
256 if (!curve) return;
258 if (sp_lpe_item_has_path_effect(lpeitem)) {
259 LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
260 if (lpeobj->lpe->isVisible()) {
261 lpeobj->lpe->doBeforeEffect(lpeitem);
262 lpeobj->lpe->doEffect(curve);
263 }
264 }
266 SPObject *parent = lpeitem->parent;
267 if (parent && SP_IS_LPE_ITEM(parent))
268 sp_lpe_item_perform_path_effect(SP_LPE_ITEM(parent), curve);
269 }
271 /**
272 * Calls any registered handlers for the update_patheffect action
273 */
274 void
275 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool write)
276 {
277 #ifdef SHAPE_VERBOSE
278 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
279 #endif
280 g_return_if_fail (lpeitem != NULL);
281 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
283 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect) {
284 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect (lpeitem, write);
285 }
286 }
288 /**
289 * Gets called when (re)attached to another lpeobject.
290 */
291 static void
292 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
293 {
294 if (old_ref) {
295 sp_signal_disconnect_by_data(old_ref, lpeitem);
296 }
297 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
298 {
299 lpeitem->lpe_modified_connection.disconnect();
300 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
301 lpeobject_ref_modified(ref, 0, lpeitem);
302 }
303 }
305 /**
306 * Gets called when lpeobject repr contents change: i.e. parameter change.
307 */
308 static void
309 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
310 {
311 sp_lpe_item_update_patheffect (lpeitem, true);
312 }
314 static void
315 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
316 {
317 if (SP_IS_GROUP(lpeitem)) {
318 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
319 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
320 SPObject *subitem = static_cast<SPObject *>(iter->data);
321 if (SP_IS_LPE_ITEM(subitem)) {
322 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
323 }
324 }
325 }
326 else if (SP_IS_PATH(lpeitem)) {
327 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
328 if ( !pathrepr->attribute("inkscape:original-d") ) {
329 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
330 }
331 }
332 }
334 static void
335 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
336 {
337 if (SP_IS_GROUP(lpeitem)) {
338 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
339 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
340 SPObject *subitem = static_cast<SPObject *>(iter->data);
341 if (SP_IS_LPE_ITEM(subitem)) {
342 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
343 }
344 }
345 }
346 else if (SP_IS_PATH(lpeitem)) {
347 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
348 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
349 && repr->attribute("inkscape:original-d")) {
350 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
351 repr->setAttribute("inkscape:original-d", NULL);
352 }
353 else {
354 sp_lpe_item_update_patheffect(lpeitem, true);
355 }
356 }
357 }
359 void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
360 {
361 if (!value) {
362 sp_lpe_item_remove_path_effect(lpeitem, false);
363 } else {
364 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", value);
366 // Ask the path effect to reset itself if it doesn't have parameters yet
367 if (lpeitem->path_effect_ref) {
368 LivePathEffectObject *lpeobj = lpeitem->path_effect_ref->lpeobject;
369 if (lpeobj && lpeobj->lpe) {
370 if(reset) {
371 // has to be called when all the subitems have their lpes applied
372 lpeobj->lpe->resetDefaults(lpeitem);
373 }
374 // perform this once when the effect is applied
375 lpeobj->lpe->doOnApply(SP_LPE_ITEM(lpeitem));
376 }
377 }
379 // make sure there is an original-d for paths!!!
380 sp_lpe_item_create_original_path_recursive(lpeitem);
381 }
382 }
384 void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
385 {
386 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
387 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
388 sp_lpe_item_set_path_effect(lpeitem, hrefstr, false);
389 g_free(hrefstr);
390 }
392 void sp_lpe_item_remove_path_effect(SPLPEItem *lpeitem, bool keep_paths)
393 {
394 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
395 repr->setAttribute("inkscape:path-effect", NULL);
397 if (!keep_paths) {
398 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
399 }
400 }
402 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
403 {
404 return lpeitem->path_effect_ref && lpeitem->path_effect_ref->lpeobject;
405 }
407 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
408 {
409 SPObject *parent = lpeitem->parent;
410 if (parent && SP_IS_LPE_ITEM(parent)) {
411 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
412 }
413 else {
414 return sp_lpe_item_has_path_effect(lpeitem);
415 }
416 }
418 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
419 {
420 LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
421 if (lpeobj && lpeobj->lpe) {
422 lpeobj->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
423 }
424 }
426 static void
427 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
428 {
429 if (((SPObjectClass *) (parent_class))->child_added)
430 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
432 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
433 SPObject *ochild = sp_object_get_child_by_repr(object, child);
434 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
435 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
436 }
437 }
438 }
440 static void
441 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
442 {
443 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
444 SPObject *ochild = sp_object_get_child_by_repr(object, child);
445 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
446 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
447 }
448 }
450 if (((SPObjectClass *) (parent_class))->remove_child)
451 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
452 }
454 /*
455 Local Variables:
456 mode:c++
457 c-file-style:"stroustrup"
458 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
459 indent-tabs-mode:nil
460 fill-column:99
461 End:
462 */
463 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :