Code

Fix LPE for groups bounding box calculation by using the SPItem->getBounds method.
[inkscape.git] / src / sp-lpe-item.cpp
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;
98     
99     klass->update_patheffect = NULL;
102 static void
103 sp_lpe_item_init(SPLPEItem *lpeitem)
105     lpeitem->path_effect_ref  = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
106     new (&lpeitem->lpe_modified_connection) sigc::connection();
109 static void
110 sp_lpe_item_finalize(GObject *object)
112     if (((GObjectClass *) (parent_class))->finalize) {
113         (* ((GObjectClass *) (parent_class))->finalize)(object);
114     }
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)
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");
128     
129     if (((SPObjectClass *) parent_class)->build) {
130         ((SPObjectClass *) parent_class)->build(object, document, repr);
131     }
134 /**
135  * Drops any allocated memory.
136  */
137 static void
138 sp_lpe_item_release(SPObject *object)
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);
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)
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     }
188 /**
189  * Receives update notifications.
190  */
191 static void
192 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
194     if (((SPObjectClass *) parent_class)->update) {
195         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
196     }
199 /**
200  * Sets modified flag for all sub-item views.
201  */
202 static void
203 sp_lpe_item_modified (SPObject *object, unsigned int flags)
205         if (((SPObjectClass *) (parent_class))->modified) {
206           (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
207         }
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)
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;
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     }
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;
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         lpeobj->lpe->doBeforeEffect(lpeitem);
261         lpeobj->lpe->doEffect(curve);
262     }
264     SPObject *parent = lpeitem->parent;
265     if (parent && SP_IS_LPE_ITEM(parent))
266         sp_lpe_item_perform_path_effect(SP_LPE_ITEM(parent), curve);
269 /**
270  * Calls any registered handlers for the update_patheffect action
271  */
272 void
273 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool write)
275 #ifdef SHAPE_VERBOSE
276     g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
277 #endif
278     g_return_if_fail (lpeitem != NULL);
279     g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
281     if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect) {
282         SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect (lpeitem, write);
283     }
286 /**
287  * Gets called when (re)attached to another lpeobject.
288  */
289 static void
290 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
292     if (old_ref) {
293         sp_signal_disconnect_by_data(old_ref, lpeitem);
294     }
295     if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
296     {
297         lpeitem->lpe_modified_connection.disconnect();
298         lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
299         lpeobject_ref_modified(ref, 0, lpeitem);
300     }
303 /**
304  * Gets called when lpeobject repr contents change: i.e. parameter change.
305  */
306 static void
307 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
309     sp_lpe_item_update_patheffect (lpeitem, true);
312 static void
313 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
315     if (SP_IS_GROUP(lpeitem)) {
316         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
317         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
318             SPObject *subitem = static_cast<SPObject *>(iter->data);
319             if (SP_IS_LPE_ITEM(subitem)) {
320                 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
321             }
322         }
323     }
324     else if (SP_IS_PATH(lpeitem)) {
325         Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
326         if ( !pathrepr->attribute("inkscape:original-d") ) {
327             pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
328         }
329     }
332 static void
333 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
335     if (SP_IS_GROUP(lpeitem)) {
336         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
337         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
338             SPObject *subitem = static_cast<SPObject *>(iter->data);
339             if (SP_IS_LPE_ITEM(subitem)) {
340                 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
341             }
342         }
343     }
344     else if (SP_IS_PATH(lpeitem)) {
345         Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
346         if (!sp_lpe_item_has_path_effect_recursive(lpeitem) 
347                 && repr->attribute("inkscape:original-d")) {
348             repr->setAttribute("d", repr->attribute("inkscape:original-d"));
349             repr->setAttribute("inkscape:original-d", NULL);
350         }
351         else {
352             sp_lpe_item_update_patheffect(lpeitem, true);
353         }
354     }
357 void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
359     if (!value) {
360         sp_lpe_item_remove_path_effect(lpeitem, false);
361     } else {
362         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", value);
363         
364         // Ask the path effect to reset itself if it doesn't have parameters yet
365         if (lpeitem->path_effect_ref && reset) {
366             LivePathEffectObject *lpeobj = lpeitem->path_effect_ref->lpeobject;
367             if (lpeobj && lpeobj->lpe) {
368                 // has to be called when all the subitems have their lpes applied
369                 lpeobj->lpe->resetDefaults(lpeitem);
370             }
371         }
372         
373         // make sure there is an original-d for paths!!!
374         sp_lpe_item_create_original_path_recursive(lpeitem);
375     }
378 void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
380     const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
381     gchar *hrefstr = g_strdup_printf("#%s", repr_id);
382     sp_lpe_item_set_path_effect(lpeitem, hrefstr, false);
383     g_free(hrefstr);
386 void sp_lpe_item_remove_path_effect(SPLPEItem *lpeitem, bool keep_paths)
388     Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
389     repr->setAttribute("inkscape:path-effect", NULL);
390     
391     if (!keep_paths) {
392         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
393     }
396 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
398     return lpeitem->path_effect_ref && lpeitem->path_effect_ref->lpeobject;
401 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
403     SPObject *parent = lpeitem->parent;
404     if (parent && SP_IS_LPE_ITEM(parent)) {
405         return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
406     }
407     else {
408         return sp_lpe_item_has_path_effect(lpeitem);
409     }
412 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
414     LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem);
415     if (lpeobj && lpeobj->lpe) {
416         lpeobj->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
417     }
420 static void
421 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
423     if (((SPObjectClass *) (parent_class))->child_added)
424         (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
425         
426     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
427         SPObject *ochild = sp_object_get_child_by_repr(object, child);
428         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
429             sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
430         }
431     }
434 static void
435 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
437     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
438         SPObject *ochild = sp_object_get_child_by_repr(object, child);
439         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
440             sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
441         }
442     }
444     if (((SPObjectClass *) (parent_class))->remove_child)
445         (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
448 /*
449   Local Variables:
450   mode:c++
451   c-file-style:"stroustrup"
452   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
453   indent-tabs-mode:nil
454   fill-column:99
455   End:
456 */
457 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :