From 903d68d4df1de1ae21e124d6e9c12770085ea087 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Tue, 25 Feb 2014 17:08:30 +0100 Subject: Rewrite of symbol creation code. Partial fix for 1201325. Symbols cannot have 'tranform' attribute. The old code copied the group 'transform' attribute to the symbol. The new code creates an intermediate group with the required 'transform' if it is not a simple translation. A simple translation is removed and applied instead to the new element. Multiple objects can now be turned into a symbol without first grouping the objects. (At the cost that multiple groups can no longer be turned into multiple symbols at one go.) This better matches marker and pattern creation. (bzr r13057) --- src/extension/internal/cairo-renderer.cpp | 6 +- src/selection-chemistry.cpp | 223 +++++++++++++++++++----------- src/selection-chemistry.h | 3 +- src/verbs.cpp | 2 +- 4 files changed, 149 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 6fbc85c05..aa47d8169 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -77,8 +77,8 @@ #include #endif -//#define TRACE(_args) g_printf _args -#define TRACE(_args) +#define TRACE(_args) g_printf _args +//#define TRACE(_args) //#define TEST(_args) _args #define TEST(_args) @@ -628,7 +628,7 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool page ctx->_width = d.width(); ctx->_height = d.height(); - TRACE(("setupDocument: %f x %f\n", ctx->_width, ctx->_height)); + TRACE(("Cairo Renderer: setupDocument: %f x %f\n", ctx->_width, ctx->_height)); bool ret = ctx->setupSurface(ctx->_width, ctx->_height); diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index ecf814f60..c3d37af72 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2934,96 +2934,145 @@ void sp_selection_to_guides(SPDesktop *desktop) } /* - * Convert to , leaving all elements referencing group unchanged. + * Convert objects to . How that happens depends on what is selected: + * + * 1) A random selection of objects will be embedded into a single element. + * + * 2) Except, a single will have its content directly embedded into a ; the 'id' and + * 'style' of the are transferred to the . + * + * 3) Except, a single with a transform that isn't a translation will keep the group when + * embedded into a (with 'id' and 'style' transferred to ). This is because a + * cannot have a transform. (If the transform is a pure translation, the translation + * is moved to the referencing element that is created.) + * + * Possible improvements: + * + * Move objects inside symbol so bbox corner at 0,0 (see marker/pattern) + * + * For SVG2, set 'refX' 'refY' to object center (with compensating shift in + * transformation). */ -void sp_selection_symbols(SPDesktop *desktop, bool /*apply*/ ) +void sp_selection_symbol(SPDesktop *desktop, bool /*apply*/ ) { if (desktop == NULL) { return; } SPDocument *doc = sp_desktop_document(desktop); - doc->ensureUpToDate(); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); Inkscape::Selection *selection = sp_desktop_selection(desktop); // Check if something is selected. if (selection->isEmpty()) { - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select groups to convert to symbols.")); + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select objects to convert to symbol.")); return; } - GSList *items = g_slist_copy(const_cast(selection->list())); - bool hasWorked = false; + doc->ensureUpToDate(); - for ( GSList const *iter=items; iter != NULL ; iter = iter->next ) { - SPObject *object = reinterpret_cast( iter->data ); + GSList *items = g_slist_copy(const_cast(selection->list())); - // Require that we really have a group. - if( SP_IS_GROUP( object ) ) { - sp_selection_symbol( doc, object ); - hasWorked = true; - } + // Keep track of parent, this is where will be inserted. + Inkscape::XML::Node *the_first_repr = reinterpret_cast( items->data )->getRepr(); + Inkscape::XML::Node *the_parent_repr = the_first_repr->parent(); + + // Find out if we have a single group + bool single_group = false; + SPObject *the_group = NULL; + Geom::Affine transform; + if( g_slist_length( items ) == 1 ) { + SPObject *object = reinterpret_cast( items->data ); + if( SP_IS_GROUP( object ) ) { + single_group = true; + the_group = object; + + if( !sp_svg_transform_read( object->getAttribute("transform"), &transform )) + transform = Geom::identity(); + + if( transform.isTranslation() ) { + + // Create new list from group children. + g_slist_free(items); + items = object->childList(false); + + // Hack: Temporarily set clone compensation to unmoved, so that we can move clone-originals + // without disturbing clones. + // See ActorAlign::on_button_click() in src/ui/dialog/align-and-distribute.cpp + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); + prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); + + // Remove transform on group, updating clones. + SP_ITEM(object)->doWriteTransform(object->getRepr(), Geom::identity()); + + // restore compensation setting + prefs->setInt("/options/clonecompensation/value", saved_compensation); + } + } } - g_slist_free(items); + // Create new + Inkscape::XML::Node *defsrepr = doc->getDefs()->getRepr(); + Inkscape::XML::Node *symbol_repr = xml_doc->createElement("svg:symbol"); + defsrepr->appendChild(symbol_repr); - if( !hasWorked ) { - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No groups converted to symbols.")); - return; - } + // For a single group, copy relevant attributes. + if( single_group ) { - selection->clear(); - // Group just disappears, nothing to select. + symbol_repr->setAttribute("style", the_group->getAttribute("style")); + symbol_repr->setAttribute("class", the_group->getAttribute("class")); + symbol_repr->setAttribute("id", the_group->getAttribute("id") ); - DocumentUndo::done(doc, SP_VERB_EDIT_SYMBOL, _("Group to symbol")); -} + // This should eventually be replaced by 'refX' and 'refY' once SVG WG approves it. + // It is done here for round-tripping + symbol_repr->setAttribute("inkscape:transform-center-x", + the_group->getAttribute("inkscape:transform-center-x")); + symbol_repr->setAttribute("inkscape:transform-center-y", + the_group->getAttribute("inkscape:transform-center-y")); -void sp_selection_symbol(SPDocument *doc, SPObject *group) -{ - Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + the_group->setAttribute("style", NULL); + std::string id = symbol_repr->attribute("id"); + id += "_transform"; + the_group->setAttribute("id", id.c_str()); - Inkscape::XML::Node *symbol = xml_doc->createElement("svg:symbol"); - symbol->setAttribute("style", group->getAttribute("style")); - symbol->setAttribute("title", group->getAttribute("title")); - symbol->setAttribute("transform", group->getAttribute("transform")); - - Glib::ustring id = group->getAttribute("id"); + } - // Now we need to copy all children of group - GSList* children = group->childList(false); - children = g_slist_reverse(children); - for (GSList* i = children; i != NULL; i = i->next ) { - SPObject* child = SP_OBJECT(i->data); - Inkscape::XML::Node *dup = child->getRepr()->duplicate(xml_doc); - symbol->appendChild(dup); - child->deleteObject(true); + // Move selected items to new + for (GSList *i = items; i != NULL; i = i->next) { + Inkscape::XML::Node *repr = SP_OBJECT(i->data)->getRepr(); + repr->parent()->removeChild(repr); + symbol_repr->addChild(repr,NULL); } - // Need to delete ; all elements that referenced should - // auto-magically reference . - doc->getDefs()->getRepr()->appendChild(symbol); - // Mysterious, must set symbol ID before deleting group or all items - // get turned into groups. (Linked to unlinking clones?) - symbol->setAttribute("id",id.c_str()); + if( single_group && transform.isTranslation() ) { + the_group->deleteObject(true); + } - // Create a clone of the symbol to replace the deleted group. + // Create pointing to new symbol (to replace the moved objects). Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); - clone->setAttribute("x", "0", false); - clone->setAttribute("y", "0", false); - gchar *href_str = g_strdup_printf("#%s", id.c_str()); + + const gchar *symbol_id = symbol_repr->attribute("id"); + gchar *href_str = g_strdup_printf("#%s", symbol_id); clone->setAttribute("xlink:href", href_str, false); g_free(href_str); - clone->setAttribute("inkscape:transform-center-x", group->getAttribute("inkscape:transform-center-x"), false); - clone->setAttribute("inkscape:transform-center-y", group->getAttribute("inkscape:transform-center-y"), false); - group->parent->getRepr()->appendChild(clone); + the_parent_repr->appendChild(clone); - group->deleteObject(true); + if( single_group && transform.isTranslation() ) { + if( !transform.isIdentity() ) + clone->setAttribute("transform", sp_svg_transform_write( transform )); + } - g_slist_free(children); + // Change selection to new element. + selection->set(clone); + + // Clean up + Inkscape::GC::release(symbol_repr); + g_slist_free(items); - Inkscape::GC::release(symbol); + DocumentUndo::done(doc, SP_VERB_EDIT_SYMBOL, _("Group to symbol")); } /* @@ -3049,48 +3098,64 @@ void sp_selection_unsymbol(SPDesktop *desktop) SPObject* symbol = selection->single(); // Make sure we have only one object in selection. - // Require that we really have a that references a . + // Require that we really have a . if( symbol == NULL || !SP_IS_SYMBOL( symbol )) { - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select only one symbol to convert to group.")); + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select only one symbol in Symbol dialog to convert to group.")); return; } doc->ensureUpToDate(); + // Create new and insert in current layer Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); - group->setAttribute("style", symbol->getAttribute("style")); - group->setAttribute("title", symbol->getAttribute("title")); - group->setAttribute("transform", symbol->getAttribute("transform")); - - Glib::ustring id = symbol->getAttribute("id"); + desktop->currentLayer()->getRepr()->appendChild(group); - // Now we need to copy all children of symbol + // Move all children of symbol to group GSList* children = symbol->childList(false); - children = g_slist_reverse(children); + + // Converting a group to a symbol inserts a group for non-translational transform. + // In converting a symbol back to a group we strip out the inserted group (or any other + // group that only adds a transform to the symbol content). + if( g_slist_length( children ) == 1 ) { + SPObject *object = reinterpret_cast( children->data ); + if( SP_IS_GROUP( object ) ) { + if( object->getAttribute("style") == NULL || + object->getAttribute("class") == NULL ) { + + group->setAttribute("transform", object->getAttribute("transform")); + g_slist_free(children); + children = object->childList(false); + } + } + } + for (GSList* i = children; i != NULL; i = i->next ) { - SPObject* child = SP_OBJECT(i->data); - Inkscape::XML::Node *dup = child->getRepr()->duplicate(xml_doc); - group->appendChild(dup); - child->deleteObject(true); + Inkscape::XML::Node *repr = SP_OBJECT(i->data)->getRepr(); + repr->parent()->removeChild(repr); + group->addChild(repr,NULL); } - // So we insert inside the current layer - SPObject *parent = desktop->currentLayer(); + // Copy relevant attributes + group->setAttribute("style", symbol->getAttribute("style")); + group->setAttribute("class", symbol->getAttribute("class")); + group->setAttribute("inkscape:transform-center-x", + symbol->getAttribute("inkscape:transform-center-x")); + group->setAttribute("inkscape:transform-center-y", + symbol->getAttribute("inkscape:transform-center-y")); + - // Need to delete ; all other elements that referenced should - // auto-magically reference . - symbol->setAttribute("id","todelete"); - group->setAttribute("id",id.c_str()); // After we delete symbol with same id. + // Need to delete ; all elements that referenced should + // auto-magically reference (if deleted after setting 'id'). + Glib::ustring id = symbol->getAttribute("id"); + group->setAttribute("id",id.c_str()); symbol->deleteObject(true); - parent->getRepr()->appendChild(group); + // Change selection to new element. SPItem *group_item = static_cast(sp_desktop_document(desktop)->getObjectByRepr(group)); - Inkscape::GC::release(group); - selection->clear(); selection->set(group_item); - // Need to signal Symbol dialog to update - + // Clean up + Inkscape::GC::release(group); g_slist_free(children); DocumentUndo::done(doc, SP_VERB_EDIT_UNSYMBOL, _("Group from symbol")); diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index e86000f70..d76a67a9d 100644 --- a/src/selection-chemistry.h +++ b/src/selection-chemistry.h @@ -69,8 +69,7 @@ void sp_selection_clone_original_path_lpe(SPDesktop *desktop); void sp_selection_to_marker(SPDesktop *desktop, bool apply = true); void sp_selection_to_guides(SPDesktop *desktop); -void sp_selection_symbols(SPDesktop *desktop, bool apply = true); -void sp_selection_symbol(SPDocument *doc, SPObject *group); +void sp_selection_symbol(SPDesktop *desktop, bool apply = true); void sp_selection_unsymbol(SPDesktop *desktop); void sp_selection_tile(SPDesktop *desktop, bool apply = true); diff --git a/src/verbs.cpp b/src/verbs.cpp index 8b333383f..653d5f892 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -996,7 +996,7 @@ void EditVerb::perform(SPAction *action, void *data) sp_selection_untile(dt); break; case SP_VERB_EDIT_SYMBOL: - sp_selection_symbols(dt); + sp_selection_symbol(dt); break; case SP_VERB_EDIT_UNSYMBOL: sp_selection_unsymbol(dt); -- cgit v1.2.3