X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fsp-item-group.cpp;h=30d08558465b4283ed19be3f9f68eff251da1bbe;hb=ec12cb48c1da4c3029b9e8c2eea263499d319599;hp=410a7b37e4c898dc0ba1cce4378e1c2498ff6a39;hpb=4358ff6156766a315e38e72a5c3c83d6d5f7486b;p=inkscape.git diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 410a7b37e..30d085584 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -1,5 +1,3 @@ -#define __SP_GROUP_C__ - /* * SVG implementation * @@ -7,6 +5,8 @@ * Lauris Kaplinski * bulia byak * Johan Engelen + * Jon A. Cruz + * Abhishek Sharma * * Copyright (C) 1999-2006 authors * Copyright (C) 2000-2001 Ximian, Inc. @@ -19,8 +19,11 @@ #endif #include +#include +#include #include "display/nr-arena-group.h" +#include "display/curve.h" #include "libnr/nr-matrix-ops.h" #include "libnr/nr-matrix-fns.h" #include "xml/repr.h" @@ -31,12 +34,23 @@ #include "sp-item-transform.h" #include "sp-root.h" #include "sp-use.h" -#include "prefs-utils.h" +#include "sp-offset.h" #include "sp-clippath.h" #include "sp-mask.h" #include "sp-path.h" #include "box3d.h" -#include "box3d-side.h" +#include "persp3d.h" +#include "inkscape.h" +#include "desktop-handles.h" +#include "selection.h" +#include "live_effects/effect.h" +#include "live_effects/lpeobject.h" +#include "live_effects/lpeobject-reference.h" +#include "sp-title.h" +#include "sp-desc.h" +#include "sp-switch.h" + +using Inkscape::DocumentUndo; static void sp_group_class_init (SPGroupClass *klass); static void sp_group_init (SPGroup *group); @@ -49,115 +63,122 @@ static void sp_group_remove_child (SPObject * object, Inkscape::XML::Node * chil static void sp_group_order_changed (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * old_ref, Inkscape::XML::Node * new_ref); static void sp_group_update (SPObject *object, SPCtx *ctx, guint flags); static void sp_group_modified (SPObject *object, guint flags); -static Inkscape::XML::Node *sp_group_write (SPObject *object, Inkscape::XML::Node *repr, guint flags); +static Inkscape::XML::Node *sp_group_write (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); static void sp_group_set(SPObject *object, unsigned key, char const *value); -static void sp_group_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags); +static void sp_group_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags); static void sp_group_print (SPItem * item, SPPrintContext *ctx); static gchar * sp_group_description (SPItem * item); static NRArenaItem *sp_group_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags); static void sp_group_hide (SPItem * item, unsigned int key); -static void sp_group_snappoints (SPItem const *item, SnapPointsIter p); +static void sp_group_snappoints (SPItem const *item, std::vector &p, Inkscape::SnapPreferences const *snapprefs); + +static void sp_group_update_patheffect(SPLPEItem *lpeitem, bool write); +static void sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write); -static SPItemClass * parent_class; +static SPLPEItemClass * parent_class; GType sp_group_get_type (void) { - static GType group_type = 0; - if (!group_type) { - GTypeInfo group_info = { - sizeof (SPGroupClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) sp_group_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (SPGroup), - 16, /* n_preallocs */ - (GInstanceInitFunc) sp_group_init, - NULL, /* value_table */ - }; - group_type = g_type_register_static (SP_TYPE_ITEM, "SPGroup", &group_info, (GTypeFlags)0); - } - return group_type; + static GType group_type = 0; + if (!group_type) { + GTypeInfo group_info = { + sizeof (SPGroupClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) sp_group_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (SPGroup), + 16, /* n_preallocs */ + (GInstanceInitFunc) sp_group_init, + NULL, /* value_table */ + }; + group_type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPGroup", &group_info, (GTypeFlags)0); + } + return group_type; } static void sp_group_class_init (SPGroupClass *klass) { - GObjectClass * object_class; - SPObjectClass * sp_object_class; - SPItemClass * item_class; + GObjectClass * object_class; + SPObjectClass * sp_object_class; + SPItemClass * item_class; + SPLPEItemClass * lpe_item_class; - object_class = (GObjectClass *) klass; - sp_object_class = (SPObjectClass *) klass; - item_class = (SPItemClass *) klass; + object_class = (GObjectClass *) klass; + sp_object_class = (SPObjectClass *) klass; + item_class = (SPItemClass *) klass; + lpe_item_class = (SPLPEItemClass *) klass; - parent_class = (SPItemClass *)g_type_class_ref (SP_TYPE_ITEM); + parent_class = (SPLPEItemClass *)g_type_class_ref (SP_TYPE_LPE_ITEM); - object_class->dispose = sp_group_dispose; + object_class->dispose = sp_group_dispose; - sp_object_class->child_added = sp_group_child_added; - sp_object_class->remove_child = sp_group_remove_child; - sp_object_class->order_changed = sp_group_order_changed; - sp_object_class->update = sp_group_update; - sp_object_class->modified = sp_group_modified; - sp_object_class->set = sp_group_set; - sp_object_class->write = sp_group_write; - sp_object_class->release = sp_group_release; - sp_object_class->build = sp_group_build; + sp_object_class->child_added = sp_group_child_added; + sp_object_class->remove_child = sp_group_remove_child; + sp_object_class->order_changed = sp_group_order_changed; + sp_object_class->update = sp_group_update; + sp_object_class->modified = sp_group_modified; + sp_object_class->set = sp_group_set; + sp_object_class->write = sp_group_write; + sp_object_class->release = sp_group_release; + sp_object_class->build = sp_group_build; - item_class->bbox = sp_group_bbox; - item_class->print = sp_group_print; - item_class->description = sp_group_description; - item_class->show = sp_group_show; - item_class->hide = sp_group_hide; - item_class->snappoints = sp_group_snappoints; + item_class->bbox = sp_group_bbox; + item_class->print = sp_group_print; + item_class->description = sp_group_description; + item_class->show = sp_group_show; + item_class->hide = sp_group_hide; + item_class->snappoints = sp_group_snappoints; + + lpe_item_class->update_patheffect = sp_group_update_patheffect; } static void sp_group_init (SPGroup *group) { - group->_layer_mode = SPGroup::GROUP; + group->_layer_mode = SPGroup::GROUP; group->group = new CGroup(group); - new (&group->_display_modes) std::map(); + new (&group->_display_modes) std::map(); } static void sp_group_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) { - sp_object_read_attr(object, "inkscape:groupmode"); + object->readAttr( "inkscape:groupmode" ); - if (((SPObjectClass *)parent_class)->build) { - ((SPObjectClass *)parent_class)->build(object, document, repr); - } + if (((SPObjectClass *)parent_class)->build) { + ((SPObjectClass *)parent_class)->build(object, document, repr); + } } static void sp_group_release(SPObject *object) { - if ( SP_GROUP(object)->_layer_mode == SPGroup::LAYER ) { - sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "layer", object); - } - if (((SPObjectClass *)parent_class)->release) { - ((SPObjectClass *)parent_class)->release(object); - } + if ( SP_GROUP(object)->_layer_mode == SPGroup::LAYER ) { + SP_OBJECT_DOCUMENT(object)->removeResource("layer", object); + } + if (((SPObjectClass *)parent_class)->release) { + ((SPObjectClass *)parent_class)->release(object); + } } static void sp_group_dispose(GObject *object) { - SP_GROUP(object)->_display_modes.~map(); + SP_GROUP(object)->_display_modes.~map(); delete SP_GROUP(object)->group; } static void sp_group_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref) { - SPItem *item; + SPItem *item; - item = SP_ITEM (object); + item = SP_ITEM (object); - if (((SPObjectClass *) (parent_class))->child_added) - (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref); + if (((SPObjectClass *) (parent_class))->child_added) + (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref); SP_GROUP(object)->group->onChildAdded(child); } @@ -167,8 +188,8 @@ sp_group_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XM static void sp_group_remove_child (SPObject * object, Inkscape::XML::Node * child) { - if (((SPObjectClass *) (parent_class))->remove_child) - (* ((SPObjectClass *) (parent_class))->remove_child) (object, child); + if (((SPObjectClass *) (parent_class))->remove_child) + (* ((SPObjectClass *) (parent_class))->remove_child) (object, child); SP_GROUP(object)->group->onChildRemoved(child); } @@ -176,8 +197,8 @@ sp_group_remove_child (SPObject * object, Inkscape::XML::Node * child) static void sp_group_order_changed (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref) { - if (((SPObjectClass *) (parent_class))->order_changed) - (* ((SPObjectClass *) (parent_class))->order_changed) (object, child, old_ref, new_ref); + if (((SPObjectClass *) (parent_class))->order_changed) + (* ((SPObjectClass *) (parent_class))->order_changed) (object, child, old_ref, new_ref); SP_GROUP(object)->group->onOrderChanged(child, old_ref, new_ref); } @@ -194,60 +215,70 @@ sp_group_update (SPObject *object, SPCtx *ctx, unsigned int flags) static void sp_group_modified (SPObject *object, guint flags) { + if (((SPObjectClass *) (parent_class))->modified) + ((SPObjectClass *) (parent_class))->modified (object, flags); + SP_GROUP(object)->group->onModified(flags); } -static Inkscape::XML::Node * -sp_group_write (SPObject *object, Inkscape::XML::Node *repr, guint flags) +static Inkscape::XML::Node * sp_group_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { - SPGroup *group; - SPObject *child; - Inkscape::XML::Node *crepr; + SPGroup *group = SP_GROUP(object); - group = SP_GROUP (object); - - if (flags & SP_OBJECT_WRITE_BUILD) { - GSList *l; - if (!repr) { - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object)); - repr = xml_doc->createElement("svg:g"); + if (flags & SP_OBJECT_WRITE_BUILD) { + GSList *l; + if (!repr) { + if (SP_IS_SWITCH(object)) { + repr = xml_doc->createElement("svg:switch"); + } else { + repr = xml_doc->createElement("svg:g"); + } + } + l = NULL; + for (SPObject *child = object->firstChild(); child; child = child->getNext() ) { + if ( !SP_IS_TITLE(child) && !SP_IS_DESC(child) ) { + Inkscape::XML::Node *crepr = child->updateRepr(xml_doc, NULL, flags); + if (crepr) { + l = g_slist_prepend (l, crepr); } - l = NULL; - for (child = sp_object_first_child(object); child != NULL; child = SP_OBJECT_NEXT(child) ) { - crepr = child->updateRepr(NULL, flags); - if (crepr) l = g_slist_prepend (l, crepr); - } - while (l) { - repr->addChild((Inkscape::XML::Node *) l->data, NULL); - Inkscape::GC::release((Inkscape::XML::Node *) l->data); - l = g_slist_remove (l, l->data); - } - } else { - for (child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { - child->updateRepr(flags); - } - } + } + } + while (l) { + repr->addChild((Inkscape::XML::Node *) l->data, NULL); + Inkscape::GC::release((Inkscape::XML::Node *) l->data); + l = g_slist_remove (l, l->data); + } + } else { + for (SPObject *child = object->firstChild() ; child ; child = child->getNext() ) { + if ( !SP_IS_TITLE(child) && !SP_IS_DESC(child) ) { + child->updateRepr(flags); + } + } + } - if ( flags & SP_OBJECT_WRITE_EXT ) { - const char *value; - if ( group->_layer_mode == SPGroup::LAYER ) { - value = "layer"; - } else if ( flags & SP_OBJECT_WRITE_ALL ) { - value = "group"; - } else { - value = NULL; - } - repr->setAttribute("inkscape:groupmode", value); - } + if ( flags & SP_OBJECT_WRITE_EXT ) { + const char *value; + if ( group->_layer_mode == SPGroup::LAYER ) { + value = "layer"; + } else if ( group->_layer_mode == SPGroup::MASK_HELPER ) { + value = "maskhelper"; + } else if ( flags & SP_OBJECT_WRITE_ALL ) { + value = "group"; + } else { + value = NULL; + } + repr->setAttribute("inkscape:groupmode", value); + } - if (((SPObjectClass *) (parent_class))->write) - ((SPObjectClass *) (parent_class))->write (object, repr, flags); + if (((SPObjectClass *) (parent_class))->write) { + ((SPObjectClass *) (parent_class))->write (object, xml_doc, repr, flags); + } - return repr; + return repr; } static void -sp_group_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags) +sp_group_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags) { SP_GROUP(item)->group->calculateBBox(bbox, transform, flags); } @@ -264,22 +295,24 @@ static gchar * sp_group_description (SPItem * item) } static void sp_group_set(SPObject *object, unsigned key, char const *value) { - SPGroup *group=SP_GROUP(object); - - switch (key) { - case SP_ATTR_INKSCAPE_GROUPMODE: { - if ( value && !strcmp(value, "layer") ) { - group->setLayerMode(SPGroup::LAYER); - } else { - group->setLayerMode(SPGroup::GROUP); - } - } break; - default: { - if (((SPObjectClass *) (parent_class))->set) { - (* ((SPObjectClass *) (parent_class))->set)(object, key, value); - } - } - } + SPGroup *group = SP_GROUP(object); + + switch (key) { + case SP_ATTR_INKSCAPE_GROUPMODE: + if ( value && !strcmp(value, "layer") ) { + group->setLayerMode(SPGroup::LAYER); + } else if ( value && !strcmp(value, "maskhelper") ) { + group->setLayerMode(SPGroup::MASK_HELPER); + } else { + group->setLayerMode(SPGroup::GROUP); + } + break; + default: { + if (((SPObjectClass *) (parent_class))->set) { + (* ((SPObjectClass *) (parent_class))->set)(object, key, value); + } + } + } } static NRArenaItem * @@ -294,255 +327,260 @@ sp_group_hide (SPItem *item, unsigned int key) SP_GROUP(item)->group->hide(key); } -static void sp_group_snappoints (SPItem const *item, SnapPointsIter p) +static void sp_group_snappoints(SPItem const *item, std::vector &p, Inkscape::SnapPreferences const *snapprefs) { - for (SPObject const *o = sp_object_first_child(SP_OBJECT(item)); - o != NULL; - o = SP_OBJECT_NEXT(o)) - { - if (SP_IS_ITEM(o)) { - sp_item_snappoints(SP_ITEM(o), false, p); - } - } + for ( SPObject const *o = item->firstChild(); o; o = o->getNext() ) + { + if (SP_IS_ITEM(o)) { + SP_ITEM(o)->getSnappoints(p, snapprefs); + } + } } void sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done) { - g_return_if_fail (group != NULL); - g_return_if_fail (SP_IS_GROUP (group)); + g_return_if_fail (group != NULL); + g_return_if_fail (SP_IS_GROUP (group)); + + SPDocument *doc = SP_OBJECT_DOCUMENT (group); + SPObject *root = doc->getRoot(); + SPObject *defs = SP_OBJECT (SP_ROOT (root)->defs); - SPDocument *doc = SP_OBJECT_DOCUMENT (group); - SPObject *root = SP_DOCUMENT_ROOT (doc); - SPObject *defs = SP_OBJECT (SP_ROOT (root)->defs); + SPItem *gitem = SP_ITEM (group); + Inkscape::XML::Node *grepr = SP_OBJECT_REPR (gitem); - SPItem *gitem = SP_ITEM (group); - Inkscape::XML::Node *grepr = SP_OBJECT_REPR (gitem); + g_return_if_fail (!strcmp (grepr->name(), "svg:g") || !strcmp (grepr->name(), "svg:a") || !strcmp (grepr->name(), "svg:switch")); - g_return_if_fail (!strcmp (grepr->name(), "svg:g") || !strcmp (grepr->name(), "svg:a") || !strcmp (grepr->name(), "svg:switch")); + // this converts the gradient/pattern fill/stroke on the group, if any, to userSpaceOnUse + gitem->adjust_paint_recursive (Geom::identity(), Geom::identity(), false); - // this converts the gradient/pattern fill/stroke on the group, if any, to userSpaceOnUse - sp_item_adjust_paint_recursive (gitem, NR::identity(), NR::identity(), false); + SPItem *pitem = SP_ITEM (SP_OBJECT_PARENT (gitem)); + Inkscape::XML::Node *prepr = SP_OBJECT_REPR (pitem); + + if (SP_IS_BOX3D(gitem)) { + group = box3d_convert_to_group(SP_BOX3D(gitem)); + gitem = SP_ITEM(group); + } + + sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(group), false); + + /* Step 1 - generate lists of children objects */ + GSList *items = NULL; + GSList *objects = NULL; + for (SPObject *child = group->firstChild() ; child; child = child->getNext() ) { + + if (SP_IS_ITEM (child)) { + + SPItem *citem = SP_ITEM (child); + + /* Merging of style */ + // this converts the gradient/pattern fill/stroke, if any, to userSpaceOnUse; we need to do + // it here _before_ the new transform is set, so as to use the pre-transform bbox + citem->adjust_paint_recursive (Geom::identity(), Geom::identity(), false); + + sp_style_merge_from_dying_parent(SP_OBJECT_STYLE(child), SP_OBJECT_STYLE(gitem)); + /* + * fixme: We currently make no allowance for the case where child is cloned + * and the group has any style settings. + * + * (This should never occur with documents created solely with the current + * version of inkscape without using the XML editor: we usually apply group + * style changes to children rather than to the group itself.) + * + * If the group has no style settings, then + * sp_style_merge_from_dying_parent should be a no-op. Otherwise (i.e. if + * we change the child's style to compensate for its parent going away) + * then those changes will typically be reflected in any clones of child, + * whereas we'd prefer for Ungroup not to affect the visual appearance. + * + * The only way of preserving styling appearance in general is for child to + * be put into a new group -- a somewhat surprising response to an Ungroup + * command. We could add a new groupmode:transparent that would mostly + * hide the existence of such groups from the user (i.e. editing behaves as + * if the transparent group's children weren't in a group), though that's + * extra complication & maintenance burden and this case is rare. + */ + + child->updateRepr(); + + Inkscape::XML::Node *nrepr = SP_OBJECT_REPR (child)->duplicate(prepr->document()); + + // Merging transform + Geom::Matrix ctrans; + Geom::Matrix const g(gitem->transform); + if (SP_IS_USE(citem) && sp_use_get_original (SP_USE(citem)) && + SP_OBJECT_PARENT (sp_use_get_original (SP_USE(citem))) == SP_OBJECT(group)) { + // make sure a clone's effective transform is the same as was under group + ctrans = g.inverse() * citem->transform * g; + } else { + // We should not apply the group's transformation to both a linked offset AND to its source + if (SP_IS_OFFSET(citem)) { // Do we have an offset at hand (whether it's dynamic or linked)? + SPItem *source = sp_offset_get_source(SP_OFFSET(citem)); + // When dealing with a chain of linked offsets, the transformation of an offset will be + // tied to the transformation of the top-most source, not to any of the intermediate + // offsets. So let's find the top-most source + while (source != NULL && SP_IS_OFFSET(source)) { + source = sp_offset_get_source(SP_OFFSET(source)); + } + if (source != NULL && // If true then we must be dealing with a linked offset ... + SP_OBJECT(group)->isAncestorOf(SP_OBJECT(source)) == false) { // ... of which the source is not in the same group + ctrans = citem->transform * g; // then we should apply the transformation of the group to the offset + } else { + ctrans = citem->transform; + } + } else { + ctrans = citem->transform * g; + } + } - SPItem *pitem = SP_ITEM (SP_OBJECT_PARENT (gitem)); - Inkscape::XML::Node *prepr = SP_OBJECT_REPR (pitem); + // FIXME: constructing a transform that would fully preserve the appearance of a + // textpath if it is ungrouped with its path seems to be impossible in general + // case. E.g. if the group was squeezed, to keep the ungrouped textpath squeezed + // as well, we'll need to relink it to some "virtual" path which is inversely + // stretched relative to the actual path, and then squeeze the textpath back so it + // would both fit the actual path _and_ be squeezed as before. It's a bummer. + + // This is just a way to temporarily remember the transform in repr. When repr is + // reattached outside of the group, the transform will be written more properly + // (i.e. optimized into the object if the corresponding preference is set) + gchar *affinestr=sp_svg_transform_write(ctrans); + nrepr->setAttribute("transform", affinestr); + g_free(affinestr); + + items = g_slist_prepend (items, nrepr); + + } else { + Inkscape::XML::Node *nrepr = SP_OBJECT_REPR (child)->duplicate(prepr->document()); + objects = g_slist_prepend (objects, nrepr); + } + } - /* When ungrouping a 3D box, we must convert the sides to ordinary paths */ - if (SP_IS_BOX3D (gitem)) { - g_print ("============== Ungrouping a 3D box ===============\n"); + /* Step 2 - clear group */ + // remember the position of the group + gint pos = SP_OBJECT_REPR(group)->position(); + + // the group is leaving forever, no heir, clones should take note; its children however are going to reemerge + SP_OBJECT (group)->deleteObject(true, false); + + /* Step 3 - add nonitems */ + if (objects) { + Inkscape::XML::Node *last_def = SP_OBJECT_REPR(defs)->lastChild(); + while (objects) { + Inkscape::XML::Node *repr = (Inkscape::XML::Node *) objects->data; + if (!sp_repr_is_meta_element(repr)) + SP_OBJECT_REPR(defs)->addChild(repr, last_def); + Inkscape::GC::release(repr); + objects = g_slist_remove (objects, objects->data); } + } - /* Step 1 - generate lists of children objects */ - GSList *items = NULL; - GSList *objects = NULL; - for (SPObject *child = sp_object_first_child(SP_OBJECT(group)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { - - if (SP_IS_ITEM (child)) { - - SPItem *citem = SP_ITEM (child); - - if (SP_IS_BOX3D_SIDE(child)) { - Inkscape::XML::Node *repr = SP_OBJECT_REPR(child); - // FIXME: This doesn't remove the attribute "inkscape:box3dsidetype". Why? - repr->setAttribute("inkscape:box3dsidetype", NULL); - repr->setAttribute("sodipodi:type", NULL); - } - - /* Merging of style */ - // this converts the gradient/pattern fill/stroke, if any, to userSpaceOnUse; we need to do - // it here _before_ the new transform is set, so as to use the pre-transform bbox - sp_item_adjust_paint_recursive (citem, NR::identity(), NR::identity(), false); - - sp_style_merge_from_dying_parent(SP_OBJECT_STYLE(child), SP_OBJECT_STYLE(gitem)); - /* - * fixme: We currently make no allowance for the case where child is cloned - * and the group has any style settings. - * - * (This should never occur with documents created solely with the current - * version of inkscape without using the XML editor: we usually apply group - * style changes to children rather than to the group itself.) - * - * If the group has no style settings, then - * sp_style_merge_from_dying_parent should be a no-op. Otherwise (i.e. if - * we change the child's style to compensate for its parent going away) - * then those changes will typically be reflected in any clones of child, - * whereas we'd prefer for Ungroup not to affect the visual appearance. - * - * The only way of preserving styling appearance in general is for child to - * be put into a new group -- a somewhat surprising response to an Ungroup - * command. We could add a new groupmode:transparent that would mostly - * hide the existence of such groups from the user (i.e. editing behaves as - * if the transparent group's children weren't in a group), though that's - * extra complication & maintenance burden and this case is rare. - */ - - child->updateRepr(); - - Inkscape::XML::Node *nrepr = SP_OBJECT_REPR (child)->duplicate(prepr->document()); - - // Merging transform - NR::Matrix ctrans; - NR::Matrix const g(gitem->transform); - if (SP_IS_USE(citem) && sp_use_get_original (SP_USE(citem)) && - SP_OBJECT_PARENT (sp_use_get_original (SP_USE(citem))) == SP_OBJECT(group)) { - // make sure a clone's effective transform is the same as was under group - ctrans = g.inverse() * citem->transform * g; - } else { - ctrans = citem->transform * g; - } - - // FIXME: constructing a transform that would fully preserve the appearance of a - // textpath if it is ungrouped with its path seems to be impossible in general - // case. E.g. if the group was squeezed, to keep the ungrouped textpath squeezed - // as well, we'll need to relink it to some "virtual" path which is inversely - // stretched relative to the actual path, and then squeeze the textpath back so it - // would both fit the actual path _and_ be squeezed as before. It's a bummer. - - // This is just a way to temporarily remember the transform in repr. When repr is - // reattached outside of the group, the transform will be written more properly - // (i.e. optimized into the object if the corresponding preference is set) - gchar *affinestr=sp_svg_transform_write(ctrans); - nrepr->setAttribute("transform", affinestr); - g_free(affinestr); - - items = g_slist_prepend (items, nrepr); - - } else { - Inkscape::XML::Node *nrepr = SP_OBJECT_REPR (child)->duplicate(prepr->document()); - objects = g_slist_prepend (objects, nrepr); - } - } + /* Step 4 - add items */ + while (items) { + Inkscape::XML::Node *repr = (Inkscape::XML::Node *) items->data; + // add item + prepr->appendChild(repr); + // restore position; since the items list was prepended (i.e. reverse), we now add + // all children at the same pos, which inverts the order once again + repr->setPosition(pos > 0 ? pos : 0); - /* Step 2 - clear group */ - // remember the position of the group - gint pos = SP_OBJECT_REPR(group)->position(); - - // the group is leaving forever, no heir, clones should take note; its children however are going to reemerge - SP_OBJECT (group)->deleteObject(true, false); - - /* Step 3 - add nonitems */ - if (objects) { - Inkscape::XML::Node *last_def = SP_OBJECT_REPR(defs)->lastChild(); - while (objects) { - SP_OBJECT_REPR(defs)->addChild((Inkscape::XML::Node *) objects->data, last_def); - Inkscape::GC::release((Inkscape::XML::Node *) objects->data); - objects = g_slist_remove (objects, objects->data); - } - } + // fill in the children list if non-null + SPItem *item = (SPItem *) doc->getObjectByRepr(repr); - /* Step 4 - add items */ - while (items) { - Inkscape::XML::Node *repr = (Inkscape::XML::Node *) items->data; - // add item - prepr->appendChild(repr); - // restore position; since the items list was prepended (i.e. reverse), we now add - // all children at the same pos, which inverts the order once again - repr->setPosition(pos > 0 ? pos : 0); - - // fill in the children list if non-null - SPItem *item = (SPItem *) doc->getObjectByRepr(repr); - - sp_item_write_transform(item, repr, item->transform, NULL, false); - - Inkscape::GC::release(repr); - if (children && SP_IS_ITEM (item)) - *children = g_slist_prepend (*children, item); - - items = g_slist_remove (items, items->data); - } + item->doWriteTransform(repr, item->transform, NULL, false); - if (do_done) - sp_document_done (doc, SP_VERB_NONE, _("Ungroup")); + Inkscape::GC::release(repr); + if (children && SP_IS_ITEM (item)) + *children = g_slist_prepend (*children, item); + + items = g_slist_remove (items, items->data); + } + + if (do_done) { + DocumentUndo::done(doc, SP_VERB_NONE, _("Ungroup")); + } } /* * some API for list aspect of SPGroup */ -GSList * -sp_item_group_item_list (SPGroup * group) +GSList *sp_item_group_item_list(SPGroup * group) { - GSList *s; - SPObject *o; + g_return_val_if_fail(group != NULL, NULL); + g_return_val_if_fail(SP_IS_GROUP(group), NULL); - g_return_val_if_fail (group != NULL, NULL); - g_return_val_if_fail (SP_IS_GROUP (group), NULL); + GSList *s = NULL; - s = NULL; - - for ( o = sp_object_first_child(SP_OBJECT(group)) ; o != NULL ; o = SP_OBJECT_NEXT(o) ) { - if (SP_IS_ITEM (o)) { - s = g_slist_prepend (s, o); - } - } + for (SPObject *o = group->firstChild() ; o ; o = o->getNext() ) { + if ( SP_IS_ITEM(o) ) { + s = g_slist_prepend(s, o); + } + } - return g_slist_reverse (s); + return g_slist_reverse (s); } -SPObject * -sp_item_group_get_child_by_name (SPGroup *group, SPObject *ref, const gchar *name) +SPObject *sp_item_group_get_child_by_name(SPGroup *group, SPObject *ref, const gchar *name) { - SPObject *child; - child = (ref) ? SP_OBJECT_NEXT(ref) : sp_object_first_child(SP_OBJECT(group)); - while ( child && strcmp (SP_OBJECT_REPR(child)->name(), name) ) { - child = SP_OBJECT_NEXT(child); - } - return child; + SPObject *child = (ref) ? ref->getNext() : group->firstChild(); + while ( child && strcmp(child->getRepr()->name(), name) ) { + child = child->getNext(); + } + return child; } void SPGroup::setLayerMode(LayerMode mode) { - if ( _layer_mode != mode ) { - if ( mode == LAYER ) { - sp_document_add_resource(SP_OBJECT_DOCUMENT(this), "layer", this); - } else { - sp_document_remove_resource(SP_OBJECT_DOCUMENT(this), "layer", this); - } - _layer_mode = mode; - _updateLayerMode(); - } + if ( _layer_mode != mode ) { + if ( mode == LAYER ) { + SP_OBJECT_DOCUMENT(this)->addResource("layer", this); + } else if ( _layer_mode == LAYER ) { + SP_OBJECT_DOCUMENT(this)->removeResource("layer", this); + } + _layer_mode = mode; + _updateLayerMode(); + } } SPGroup::LayerMode SPGroup::layerDisplayMode(unsigned int dkey) const { - std::map::const_iterator iter; - iter = _display_modes.find(dkey); - if ( iter != _display_modes.end() ) { - return (*iter).second; - } else { - return GROUP; - } + std::map::const_iterator iter; + iter = _display_modes.find(dkey); + if ( iter != _display_modes.end() ) { + return (*iter).second; + } else { + return GROUP; + } } void SPGroup::setLayerDisplayMode(unsigned int dkey, SPGroup::LayerMode mode) { - if ( layerDisplayMode(dkey) != mode ) { - _display_modes[dkey] = mode; - _updateLayerMode(dkey); - } + if ( layerDisplayMode(dkey) != mode ) { + _display_modes[dkey] = mode; + _updateLayerMode(dkey); + } } void SPGroup::_updateLayerMode(unsigned int display_key) { - SPItemView *view; - for ( view = this->display ; view ; view = view->next ) { - if ( !display_key || view->key == display_key ) { - NRArenaGroup *arena_group=NR_ARENA_GROUP(view->arenaitem); - if (arena_group) { - nr_arena_group_set_transparent(arena_group, effectiveLayerMode(view->key) == SPGroup::LAYER); - } - } - } + SPItemView *view; + for ( view = this->display ; view ; view = view->next ) { + if ( !display_key || view->key == display_key ) { + NRArenaGroup *arena_group=NR_ARENA_GROUP(view->arenaitem); + if (arena_group) { + nr_arena_group_set_transparent(arena_group, effectiveLayerMode(view->key) == SPGroup::LAYER); + } + } + } } -void SPGroup::translateChildItems(NR::translate const &tr) +void SPGroup::translateChildItems(Geom::Translate const &tr) { - if (this->hasChildren()) - { - SPObject *o = NULL; - for (o = sp_object_first_child(SP_OBJECT(this)) ; o != NULL ; o = SP_OBJECT_NEXT(o) ) { - if (SP_IS_ITEM (o)) { - sp_item_move_rel(static_cast(o), tr); - } - } - } + if ( hasChildren() ) { + for (SPObject *o = firstChild() ; o ; o = o->getNext() ) { + if ( SP_IS_ITEM(o) ) { + sp_item_move_rel(reinterpret_cast(o), tr); + } + } + } } CGroup::CGroup(SPGroup *group) { @@ -563,30 +601,28 @@ void CGroup::onChildAdded(Inkscape::XML::Node *child) { NRArenaItem *ac; for (v = _group->display; v != NULL; v = v->next) { - ac = sp_item_invoke_show (SP_ITEM (ochild), NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); + ac = SP_ITEM (ochild)->invoke_show (NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); if (ac) { nr_arena_item_append_child (v->arenaitem, ac); - nr_arena_item_unref (ac); } } } } else { // general case - SPObject *ochild = sp_object_get_child_by_repr(_group, child); + SPObject *ochild = _group->get_child_by_repr(child); if ( ochild && SP_IS_ITEM(ochild) ) { /* TODO: this should be moved into SPItem somehow */ SPItemView *v; NRArenaItem *ac; - unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild)); + unsigned position = SP_ITEM(ochild)->pos_in_parent(); for (v = _group->display; v != NULL; v = v->next) { - ac = sp_item_invoke_show (SP_ITEM (ochild), NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); + ac = SP_ITEM (ochild)->invoke_show (NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); if (ac) { nr_arena_item_add_child (v->arenaitem, ac, NULL); nr_arena_item_set_order (ac, position); - nr_arena_item_unref (ac); } } } @@ -599,7 +635,7 @@ void CGroup::onChildRemoved(Inkscape::XML::Node */*child*/) { _group->requestModified(SP_OBJECT_MODIFIED_FLAG); } -void CGroup::onUpdate(SPCtx *ctx, unsigned int flags) { +void CGroup::onUpdate(SPCtx *ctx, unsigned int flags) { SPItemCtx *ictx, cctx; ictx = (SPItemCtx *) ctx; @@ -614,7 +650,7 @@ void CGroup::onUpdate(SPCtx *ctx, unsigned int flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { SPObject *object = SP_OBJECT(_group); for (SPItemView *v = SP_ITEM(_group)->display; v != NULL; v = v->next) { - nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object)); + nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object)); } } @@ -622,50 +658,14 @@ void CGroup::onUpdate(SPCtx *ctx, unsigned int flags) { while (l) { SPObject *child = SP_OBJECT (l->data); l = g_slist_remove (l, child); - //g_print ("sp-item-group: onUpdate working on child with flags "); if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { - /*** - g_print (" On parent object: "); - if (flags & SP_OBJECT_MODIFIED_FLAG) { - g_print("SP_OBJECT_MODIFIED_FLAG "); - } - if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) { - g_print("SP_OBJECT_CHILD_MODIFIED_FLAG "); - } - g_print ("\n"); - g_print (" On child object: "); - if (child->uflags & SP_OBJECT_MODIFIED_FLAG) { - g_print("SP_OBJECT_MODIFIED_FLAG "); - } - if (child->uflags & SP_OBJECT_CHILD_MODIFIED_FLAG) { - g_print("SP_OBJECT_CHILD_MODIFIED_FLAG "); - } - ***/ if (SP_IS_ITEM (child)) { SPItem const &chi = *SP_ITEM(child); cctx.i2doc = chi.transform * ictx->i2doc; cctx.i2vp = chi.transform * ictx->i2vp; - /** - g_print ("case 1\n"); - g_print ("\n On parent object: flags=%d ", flags); - g_print ("\n On child object: uflags=%d ", child->uflags); - if (flags & SP_OBJECT_MODIFIED_FLAG) g_print("SP_OBJECT_MODIFIED_FLAG "); - if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) g_print("SP_OBJECT_CHILD_MODIFIED_FLAG "); - g_print ("\n"); - if (child->uflags & SP_OBJECT_MODIFIED_FLAG) { - g_print("SP_OBJECT_MODIFIED_FLAG "); - } - if (child->uflags & SP_OBJECT_CHILD_MODIFIED_FLAG) { - g_print("SP_OBJECT_CHILD_MODIFIED_FLAG "); - } - g_print ("\n"); - **/ - //g_print ("Caution! The changed code applies! Does this change any behaviour?\n"); child->updateDisplay((SPCtx *)&cctx, flags); - //child->updateDisplay((SPCtx *)&cctx, child->uflags); } else { child->updateDisplay(ctx, flags); - //g_print ("case 2\n"); } } g_object_unref (G_OBJECT (child)); @@ -681,7 +681,7 @@ void CGroup::onModified(guint flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { SPObject *object = SP_OBJECT(_group); for (SPItemView *v = SP_ITEM(_group)->display; v != NULL; v = v->next) { - nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object)); + nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object)); } } @@ -696,17 +696,22 @@ void CGroup::onModified(guint flags) { } } -void CGroup::calculateBBox(NRRect *bbox, NR::Matrix const &transform, unsigned const flags) { +void CGroup::calculateBBox(NRRect *bbox, Geom::Matrix const &transform, unsigned const flags) { + + Geom::OptRect dummy_bbox; + GSList *l = _group->childList(false, SPObject::ActionBBox); while (l) { SPObject *o = SP_OBJECT (l->data); - if (SP_IS_ITEM(o)) { + if (SP_IS_ITEM(o) && !SP_ITEM(o)->isHidden()) { SPItem *child = SP_ITEM(o); - NR::Matrix const ct(child->transform * transform); - sp_item_invoke_bbox_full(child, bbox, ct, flags, FALSE); - } + Geom::Matrix const ct(to_2geom(child->transform) * transform); + child->invoke_bbox_full( dummy_bbox, ct, flags, FALSE); + } l = g_slist_remove (l, o); } + + *bbox = NRRect(dummy_bbox); } void CGroup::onPrint(SPPrintContext *ctx) { @@ -714,20 +719,20 @@ void CGroup::onPrint(SPPrintContext *ctx) { while (l) { SPObject *o = SP_OBJECT (l->data); if (SP_IS_ITEM(o)) { - sp_item_invoke_print (SP_ITEM (o), ctx); - } + SP_ITEM(o)->invoke_print (ctx); + } l = g_slist_remove (l, o); } } gint CGroup::getItemCount() { gint len = 0; - for (SPObject *o = sp_object_first_child(SP_OBJECT(_group)) ; o != NULL ; o = SP_OBJECT_NEXT(o) ) { + for (SPObject *o = _group->firstChild() ; o ; o = o->getNext() ) { if (SP_IS_ITEM(o)) { len++; } } - + return len; } @@ -763,11 +768,10 @@ void CGroup::_showChildren (NRArena *arena, NRArenaItem *ai, unsigned int key, u SPObject *o = SP_OBJECT (l->data); if (SP_IS_ITEM (o)) { child = SP_ITEM (o); - ac = sp_item_invoke_show (child, arena, key, flags); + ac = child->invoke_show (arena, key, flags); if (ac) { nr_arena_item_add_child (ai, ac, ar); ar = ac; - nr_arena_item_unref (ac); } } l = g_slist_remove (l, o); @@ -782,7 +786,7 @@ void CGroup::hide (unsigned int key) { SPObject *o = SP_OBJECT (l->data); if (SP_IS_ITEM (o)) { child = SP_ITEM (o); - sp_item_invoke_hide (child, key); + child->invoke_hide (key); } l = g_slist_remove (l, o); } @@ -793,11 +797,11 @@ void CGroup::hide (unsigned int key) { void CGroup::onOrderChanged (Inkscape::XML::Node *child, Inkscape::XML::Node *, Inkscape::XML::Node *) { - SPObject *ochild = sp_object_get_child_by_repr(_group, child); + SPObject *ochild = _group->get_child_by_repr(child); if ( ochild && SP_IS_ITEM(ochild) ) { /* TODO: this should be moved into SPItem somehow */ SPItemView *v; - unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild)); + unsigned position = SP_ITEM(ochild)->pos_in_parent(); for ( v = SP_ITEM (ochild)->display ; v != NULL ; v = v->next ) { nr_arena_item_set_order (v->arenaitem, position); } @@ -806,6 +810,74 @@ void CGroup::onOrderChanged (Inkscape::XML::Node *child, Inkscape::XML::Node *, _group->requestModified(SP_OBJECT_MODIFIED_FLAG); } +static void +sp_group_update_patheffect (SPLPEItem *lpeitem, bool write) +{ +#ifdef GROUP_VERBOSE + g_message("sp_group_update_patheffect: %p\n", lpeitem); +#endif + g_return_if_fail (lpeitem != NULL); + g_return_if_fail (SP_IS_GROUP (lpeitem)); + + GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); + for ( GSList const *iter = item_list; iter; iter = iter->next ) { + SPObject *subitem = static_cast(iter->data); + if (SP_IS_LPE_ITEM(subitem)) { + if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (subitem))->update_patheffect) { + SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (subitem))->update_patheffect (SP_LPE_ITEM(subitem), write); + } + } + } + + if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) { + for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + if (lpeobj && lpeobj->get_lpe()) { + lpeobj->get_lpe()->doBeforeEffect(lpeitem); + } + } + + sp_group_perform_patheffect(SP_GROUP(lpeitem), SP_GROUP(lpeitem), write); + } +} + +static void +sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write) +{ + GSList const *item_list = sp_item_group_item_list(SP_GROUP(group)); + for ( GSList const *iter = item_list; iter; iter = iter->next ) { + SPObject *subitem = static_cast(iter->data); + if (SP_IS_GROUP(subitem)) { + sp_group_perform_patheffect(SP_GROUP(subitem), topgroup, write); + } else if (SP_IS_SHAPE(subitem)) { + SPCurve * c = NULL; + if (SP_IS_PATH(subitem)) { + c = sp_path_get_original_curve(SP_PATH(subitem)); + } else { + c = SP_SHAPE(subitem)->getCurve(); + } + // only run LPEs when the shape has a curve defined + if (c) { + sp_lpe_item_perform_path_effect(SP_LPE_ITEM(topgroup), c); + SP_SHAPE(subitem)->setCurve(c, TRUE); + + if (write) { + Inkscape::XML::Node *repr = SP_OBJECT_REPR(subitem); + gchar *str = sp_svg_write_path(c->get_pathvector()); + repr->setAttribute("d", str); +#ifdef GROUP_VERBOSE +g_message("sp_group_perform_patheffect writes 'd' attribute"); +#endif + g_free(str); + } + + c->unref(); + } + } + } +} + /* Local Variables: mode:c++