X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fpath-chemistry.cpp;h=bec3c5cbff27f23c51bd27e83da0d13fad7c6c6c;hb=9dc68827cbd515262ecb8d5ae8547d9e82c72e00;hp=32e2a7beca74ce65f1ace23297ba3b9edadad1c7;hpb=7ed5165239990ad860859dd38806e0ac1736b8ee;p=inkscape.git diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 32e2a7bec..bec3c5cbf 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -1,13 +1,14 @@ -#define __SP_PATH_CHEMISTRY_C__ - /* * Here are handlers for modifying selections, specific to paths * * Authors: * Lauris Kaplinski * bulia byak + * Jasper van de Gronde + * Jon A. Cruz + * Abhishek Sharma * - * Copyright (C) 1999-2004 Authors + * Copyright (C) 1999-2008 Authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information @@ -16,6 +17,8 @@ #ifdef HAVE_CONFIG_H # include #endif +#include +#include #include "xml/repr.h" #include "svg/svg.h" #include "display/curve.h" @@ -24,138 +27,163 @@ #include "sp-path.h" #include "sp-text.h" #include "sp-flowtext.h" -#include "libnr/nr-path.h" #include "text-editing.h" #include "style.h" -#include "inkscape.h" #include "desktop.h" #include "document.h" #include "message-stack.h" #include "selection.h" #include "desktop-handles.h" #include "box3d.h" - +#include <2geom/pathvector.h> +#include "selection-chemistry.h" #include "path-chemistry.h" -/* Helper functions for sp_selected_path_to_curves */ -static void sp_selected_path_to_curves0(gboolean do_document_done, guint32 text_grouping_policy); -enum { - /* Not used yet. This is the placeholder of Lauris's idea. */ - SP_TOCURVE_INTERACTIVE = 1 << 0, - SP_TOCURVE_GROUPING_BY_WORD = 1 << 1, - SP_TOCURVE_GROUPING_BY_LINE = 1 << 2, - SP_TOCURVE_GROUPING_BY_WHOLE = 1 << 3 -}; +using Inkscape::DocumentUndo; void -sp_selected_path_combine(void) +sp_selected_path_combine(SPDesktop *desktop) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = sp_desktop_selection(desktop); - GSList *items = (GSList *) selection->itemList(); - - if (g_slist_length(items) < 2) { - sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select at least two objects to combine.")); + SPDocument *doc = sp_desktop_document(desktop); + + if (g_slist_length((GSList *) selection->itemList()) < 1) { + sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to combine.")); return; } - for (GSList *i = items; i != NULL; i = i->next) { - SPItem *item = (SPItem *) i->data; - if (!SP_IS_SHAPE(item) && !SP_IS_TEXT(item)) { - sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("At least one of the objects is not a path, cannot combine.")); - return; - } - } - - Inkscape::XML::Node *parent = SP_OBJECT_REPR((SPItem *) items->data)->parent(); - for (GSList *i = items; i != NULL; i = i->next) { - if ( SP_OBJECT_REPR((SPItem *) i->data)->parent() != parent ) { - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot combine objects from different groups or layers.")); - return; - } - } - desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Combining paths...")); // set "busy" cursor desktop->setWaitingCursor(); - sp_selected_path_to_curves0(FALSE, 0); + GSList *items = g_slist_copy((GSList *) selection->itemList()); - items = g_slist_copy((GSList *) selection->itemList()); - items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); - selection->clear(); + items = sp_degroup_list (items); // descend into any groups in selection - // remember the position of the topmost object - gint topmost = (SP_OBJECT_REPR((SPItem *) g_slist_last(items)->data))->position(); + GSList *to_paths = NULL; + for (GSList *i = items; i != NULL; i = i->next) { + SPItem *item = (SPItem *) i->data; + if (!SP_IS_PATH(item) && !SP_IS_GROUP(item)) + to_paths = g_slist_prepend(to_paths, item); + } + GSList *converted = NULL; + bool did = sp_item_list_to_curves(to_paths, &items, &converted); + g_slist_free(to_paths); + for (GSList *i = converted; i != NULL; i = i->next) + items = g_slist_prepend(items, doc->getObjectByRepr((Inkscape::XML::Node*)(i->data))); - // remember the id of the bottomost object - char const *id = SP_OBJECT_REPR((SPItem *) items->data)->attribute("id"); + items = sp_degroup_list (items); // converting to path may have added more groups, descend again - // FIXME: merge styles of combined objects instead of using the first one's style - gchar *style = g_strdup(SP_OBJECT_REPR((SPItem *) items->data)->attribute("style")); + items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); + items = g_slist_reverse(items); - GString *dstring = g_string_new(""); - for (GSList *i = items; i != NULL; i = i->next) { + // remember the position, id, transform and style of the topmost path, they will be assigned to the combined one + gint position = 0; + char const *id = NULL; + char const *transform = NULL; + gchar *style = NULL; + gchar *path_effect = NULL; - SPPath *path = (SPPath *) i->data; - SPCurve *c = sp_shape_get_curve(SP_SHAPE(path)); + SPCurve* curve = 0; + SPItem *first = NULL; + Inkscape::XML::Node *parent = NULL; - NArtBpath *abp = nr_artpath_affine(SP_CURVE_BPATH(c), SP_ITEM(path)->transform); - sp_curve_unref(c); - gchar *str = sp_svg_write_path(abp); - g_free(abp); + if (did) { + selection->clear(); + } - dstring = g_string_append(dstring, str); - g_free(str); + for (GSList *i = items; i != NULL; i = i->next) { // going from top to bottom - // if this is the bottommost object, - if (!strcmp(SP_OBJECT_REPR(path)->attribute("id"), id)) { - // delete it so that its clones don't get alerted; this object will be restored shortly, with the same id - SP_OBJECT(path)->deleteObject(false); + SPItem *item = (SPItem *) i->data; + if (!SP_IS_PATH(item)) + continue; + + if (!did) { + selection->clear(); + did = true; + } + + SPCurve *c = sp_path_get_curve_for_edit(SP_PATH(item)); + if (first == NULL) { // this is the topmost path + first = item; + parent = SP_OBJECT_REPR(first)->parent(); + position = SP_OBJECT_REPR(first)->position(); + id = SP_OBJECT_REPR(first)->attribute("id"); + transform = SP_OBJECT_REPR(first)->attribute("transform"); + // FIXME: merge styles of combined objects instead of using the first one's style + style = g_strdup(SP_OBJECT_REPR(first)->attribute("style")); + path_effect = g_strdup(SP_OBJECT_REPR(first)->attribute("inkscape:path-effect")); + //c->transform(item->transform); + curve = c; } else { - // delete the object for real, so that its clones can take appropriate action - SP_OBJECT(path)->deleteObject(); + c->transform(item->getRelativeTransform(SP_OBJECT(first))); + curve->append(c, false); + c->unref(); } - topmost--; + // unless this is the topmost object, + if (item != first) { + // reduce position only if the same parent + if (SP_OBJECT_REPR(item)->parent() == parent) + position--; + // delete the object for real, so that its clones can take appropriate action + SP_OBJECT(item)->deleteObject(); + } } g_slist_free(items); - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); - Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); + if (did) { + SP_OBJECT(first)->deleteObject(false); + // delete the topmost. - // restore id - repr->setAttribute("id", id); + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); - repr->setAttribute("style", style); - g_free(style); + // restore id, transform, path effect, and style + repr->setAttribute("id", id); + if (transform) { + repr->setAttribute("transform", transform); + } + repr->setAttribute("style", style); + g_free(style); - repr->setAttribute("d", dstring->str); - g_string_free(dstring, TRUE); + repr->setAttribute("inkscape:path-effect", path_effect); + g_free(path_effect); - // add the new group to the group members' common parent - parent->appendChild(repr); + // set path data corresponding to new curve + gchar *dstring = sp_svg_write_path(curve->get_pathvector()); + curve->unref(); + if (path_effect) { + repr->setAttribute("inkscape:original-d", dstring); + } else { + repr->setAttribute("d", dstring); + } + g_free(dstring); - // move to the position of the topmost, reduced by the number of deleted items - repr->setPosition(topmost > 0 ? topmost + 1 : 0); + // add the new group to the parent of the topmost + parent->appendChild(repr); - desktop->clearWaitingCursor(); + // move to the position of the topmost, reduced by the number of deleted items + repr->setPosition(position > 0 ? position : 0); + + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_COMBINE, + _("Combine")); + + selection->set(repr); - sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_COMBINE, - _("Combine")); + Inkscape::GC::release(repr); - selection->set(repr); + } else { + sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("No path(s) to combine in the selection.")); + } - Inkscape::GC::release(repr); + desktop->clearWaitingCursor(); } void -sp_selected_path_break_apart(void) +sp_selected_path_break_apart(SPDesktop *desktop) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { @@ -180,7 +208,7 @@ sp_selected_path_break_apart(void) SPPath *path = SP_PATH(item); - SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path)); + SPCurve *curve = sp_path_get_curve_for_edit(SP_PATH(path)); if (curve == NULL) continue; @@ -190,21 +218,24 @@ sp_selected_path_break_apart(void) gint pos = SP_OBJECT_REPR(item)->position(); char const *id = SP_OBJECT_REPR(item)->attribute("id"); - gchar *style = g_strdup(SP_OBJECT(item)->repr->attribute("style")); + // XML Tree being used directly here while it shouldn't be... + gchar *style = g_strdup(SP_OBJECT(item)->getRepr()->attribute("style")); + // XML Tree being used directly here while it shouldn't be... + gchar *path_effect = g_strdup(SP_OBJECT(item)->getRepr()->attribute("inkscape:path-effect")); - NArtBpath *abp = nr_artpath_affine(SP_CURVE_BPATH(curve), (SP_ITEM(path))->transform); + Geom::PathVector apv = curve->get_pathvector() * SP_ITEM(path)->transform; - sp_curve_unref(curve); + curve->unref(); // it's going to resurrect as one of the pieces, so we delete without advertisement SP_OBJECT(item)->deleteObject(false); - curve = sp_curve_new_from_bpath(abp); + curve = new SPCurve(apv); g_assert(curve != NULL); - GSList *list = sp_curve_split(curve); + GSList *list = curve->split(); - sp_curve_unref(curve); + curve->unref(); GSList *reprs = NULL; for (GSList *l = list; l != NULL; l = l->next) { @@ -213,8 +244,13 @@ sp_selected_path_break_apart(void) Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); repr->setAttribute("style", style); - gchar *str = sp_svg_write_path(SP_CURVE_BPATH(curve)); - repr->setAttribute("d", str); + repr->setAttribute("inkscape:path-effect", path_effect); + + gchar *str = sp_svg_write_path(curve->get_pathvector()); + if (path_effect) + repr->setAttribute("inkscape:original-d", str); + else + repr->setAttribute("d", str); g_free(str); // add the new repr to the parent @@ -237,32 +273,23 @@ sp_selected_path_break_apart(void) g_slist_free(reprs); g_slist_free(list); g_free(style); - + g_free(path_effect); } desktop->clearWaitingCursor(); if (did) { - sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_BREAK_APART, - _("Break apart")); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_BREAK_APART, + _("Break apart")); } else { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("No path(s) to break apart in the selection.")); - return; } } /* This function is an entry point from GUI */ void -sp_selected_path_to_curves(void) +sp_selected_path_to_curves(SPDesktop *desktop, bool interactive) { - sp_selected_path_to_curves0(TRUE, SP_TOCURVE_INTERACTIVE); -} - -static void -sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy*/) -{ - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { @@ -283,11 +310,69 @@ sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy selection->clear(); GSList *items = g_slist_copy(selected); + did = sp_item_list_to_curves(items, &selected, &to_select); + + g_slist_free (items); + selection->setReprList(to_select); + selection->addList(selected); + g_slist_free (to_select); + g_slist_free (selected); + + if (interactive) { + desktop->clearWaitingCursor(); + if (did) { + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_OBJECT_TO_CURVE, + _("Object to path")); + } else { + sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("No objects to convert to path in the selection.")); + return; + } + } +} + +/** Converts the selected items to LPEItems if they are not already so; e.g. SPRects) */ +void sp_selected_to_lpeitems(SPDesktop *desktop) +{ + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + if (selection->isEmpty()) { + return; + } + + bool did = false; + + GSList *selected = g_slist_copy((GSList *) selection->itemList()); + GSList *to_select = NULL; + selection->clear(); + GSList *items = g_slist_copy(selected); + + did = sp_item_list_to_curves(items, &selected, &to_select, true); + + g_slist_free (items); + selection->setReprList(to_select); + selection->addList(selected); + g_slist_free (to_select); + g_slist_free (selected); +} + +bool +sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select, bool skip_all_lpeitems) +{ + bool did = false; + for (; items != NULL; items = items->next) { SPItem *item = SP_ITEM(items->data); + SPDocument *document = item->document; + + if ( skip_all_lpeitems && + SP_IS_LPE_ITEM(item) && + !SP_IS_GROUP(item) ) // also convert objects in an SPGroup when skip_all_lpeitems is set. + { + continue; + } if (SP_IS_PATH(item) && !SP_PATH(item)->original_curve) { continue; // already a path, and no path effect @@ -295,12 +380,30 @@ sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy if (SP_IS_BOX3D(item)) { // convert 3D box to ordinary group of paths; replace the old element in 'selected' with the new group - GSList *sel_it = g_slist_find(selected, item); - sel_it->data = box3d_convert_to_group(SP_BOX3D(item)); - item = SP_ITEM(sel_it->data); + Inkscape::XML::Node *repr = SP_OBJECT_REPR(box3d_convert_to_group(SP_BOX3D(item))); + + if (repr) { + *to_select = g_slist_prepend (*to_select, repr); + did = true; + *selected = g_slist_remove (*selected, item); + } - did = true; - selected = g_slist_remove (selected, item); + continue; + } + + if (SP_IS_GROUP(item)) { + sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(item), true); + GSList *item_list = sp_item_group_item_list(SP_GROUP(item)); + + GSList *item_to_select = NULL; + GSList *item_selected = NULL; + + if (sp_item_list_to_curves(item_list, &item_selected, &item_to_select)) + did = true; + + g_slist_free(item_list); + g_slist_free(item_to_select); + g_slist_free(item_selected); continue; } @@ -310,7 +413,7 @@ sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy continue; did = true; - selected = g_slist_remove (selected, item); + *selected = g_slist_remove (*selected, item); // remember the position of the item gint pos = SP_OBJECT_REPR(item)->position(); @@ -318,6 +421,10 @@ sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy Inkscape::XML::Node *parent = SP_OBJECT_REPR(item)->parent(); // remember id char const *id = SP_OBJECT_REPR(item)->attribute("id"); + // remember title + gchar *title = item->title(); + // remember description + gchar *desc = item->desc(); // It's going to resurrect, so we delete without notifying listeners. SP_OBJECT(item)->deleteObject(false); @@ -326,31 +433,26 @@ sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy repr->setAttribute("id", id); // add the new repr to the parent parent->appendChild(repr); + SPObject* newObj = document->getObjectByRepr(repr); + if (title && newObj) { + newObj->setTitle(title); + g_free(title); + } + if (desc && newObj) { + newObj->setDesc(desc); + g_free(desc); + } + // move to the saved position repr->setPosition(pos > 0 ? pos : 0); /* Buglet: We don't re-add the (new version of the) object to the selection of any other * desktops where it was previously selected. */ - to_select = g_slist_prepend (to_select, repr); + *to_select = g_slist_prepend (*to_select, repr); Inkscape::GC::release(repr); } - - g_slist_free (items); - selection->setReprList(to_select); - selection->addList(selected); - g_slist_free (to_select); - g_slist_free (selected); - - if (interactive) { - desktop->clearWaitingCursor(); - if (did) { - sp_document_done(sp_desktop_document(desktop), SP_VERB_OBJECT_TO_CURVE, - _("Object to path")); - } else { - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("No objects to convert to path in the selection.")); - return; - } - } + + return did; } Inkscape::XML::Node * @@ -359,17 +461,98 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) if (!item) return NULL; + Inkscape::XML::Document *xml_doc = SP_OBJECT_REPR(item)->document(); + + if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { + // Special treatment for text: convert each glyph to separate path, then group the paths + Inkscape::XML::Node *g_repr = xml_doc->createElement("svg:g"); + g_repr->setAttribute("transform", SP_OBJECT_REPR(item)->attribute("transform")); + /* Mask */ + gchar *mask_str = (gchar *) SP_OBJECT_REPR(item)->attribute("mask"); + if ( mask_str ) + g_repr->setAttribute("mask", mask_str); + /* Clip path */ + gchar *clip_path_str = (gchar *) SP_OBJECT_REPR(item)->attribute("clip-path"); + if ( clip_path_str ) + g_repr->setAttribute("clip-path", clip_path_str); + /* Rotation center */ + g_repr->setAttribute("inkscape:transform-center-x", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-x"), false); + g_repr->setAttribute("inkscape:transform-center-y", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-y"), false); + /* Whole text's style */ + gchar *style_str = sp_style_write_difference(SP_OBJECT_STYLE(item), + SP_OBJECT_STYLE(SP_OBJECT_PARENT(item))); + g_repr->setAttribute("style", style_str); + g_free(style_str); + Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin(); + do { + Inkscape::Text::Layout::iterator iter_next = iter; + iter_next.nextGlyph(); // iter_next is one glyph ahead from iter + if (iter == iter_next) + break; + + /* This glyph's style */ + SPObject const *pos_obj = 0; + void *rawptr = 0; + te_get_layout(item)->getSourceOfCharacter(iter, &rawptr); + if (!rawptr || !SP_IS_OBJECT(rawptr)) // no source for glyph, abort + break; + pos_obj = SP_OBJECT(rawptr); + while (SP_IS_STRING(pos_obj) && SP_OBJECT_PARENT(pos_obj)) { + pos_obj = SP_OBJECT_PARENT(pos_obj); // SPStrings don't have style + } + gchar *style_str = sp_style_write_difference(SP_OBJECT_STYLE(pos_obj), + SP_OBJECT_STYLE(SP_OBJECT_PARENT(pos_obj))); + + // get path from iter to iter_next: + SPCurve *curve = te_get_layout(item)->convertToCurves(iter, iter_next); + iter = iter_next; // shift to next glyph + if (!curve) { // error converting this glyph + g_free (style_str); + continue; + } + if (curve->is_empty()) { // whitespace glyph? + curve->unref(); + g_free (style_str); + continue; + } + + Inkscape::XML::Node *p_repr = xml_doc->createElement("svg:path"); + + gchar *def_str = sp_svg_write_path(curve->get_pathvector()); + p_repr->setAttribute("d", def_str); + g_free(def_str); + curve->unref(); + + p_repr->setAttribute("style", style_str); + g_free(style_str); + + g_repr->appendChild(p_repr); + Inkscape::GC::release(p_repr); + + if (iter == te_get_layout(item)->end()) + break; + + } while (true); + + return g_repr; + } + SPCurve *curve = NULL; if (SP_IS_SHAPE(item)) { - curve = sp_shape_get_curve(SP_SHAPE(item)); - } else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { - curve = te_get_layout(item)->convertToCurves(); - } + curve = SP_SHAPE(item)->getCurve(); + } if (!curve) return NULL; - Inkscape::XML::Document *xml_doc = SP_OBJECT_REPR(item)->document(); + // Prevent empty paths from being added to the document + // otherwise we end up with zomby markup in the SVG file + if(curve->is_empty()) + { + curve->unref(); + return NULL; + } + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); /* Transformation */ repr->setAttribute("transform", SP_OBJECT_REPR(item)->attribute("transform")); @@ -390,24 +573,21 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) repr->setAttribute("clip-path", clip_path_str); /* Rotation center */ - sp_repr_set_attr(repr, "inkscape:transform-center-x", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-x")); - sp_repr_set_attr(repr, "inkscape:transform-center-y", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-y")); + repr->setAttribute("inkscape:transform-center-x", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-x"), false); + repr->setAttribute("inkscape:transform-center-y", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-y"), false); /* Definition */ - gchar *def_str = sp_svg_write_path(SP_CURVE_BPATH(curve)); + gchar *def_str = sp_svg_write_path(curve->get_pathvector()); repr->setAttribute("d", def_str); g_free(def_str); - sp_curve_unref(curve); + curve->unref(); return repr; } -// FIXME: THIS DOES NOT REVERSE THE NODETYPES ORDER! void -sp_selected_path_reverse() +sp_selected_path_reverse(SPDesktop *desktop) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = sp_desktop_selection(desktop); GSList *items = (GSList *) selection->itemList(); @@ -431,24 +611,31 @@ sp_selected_path_reverse() did = true; SPPath *path = SP_PATH(i->data); - SPCurve *rcurve = sp_curve_reverse(sp_path_get_curve_reference(path)); + SPCurve *rcurve = sp_path_get_curve_reference(path)->create_reverse(); - gchar *str = sp_svg_write_path(SP_CURVE_BPATH(rcurve)); - if ( sp_shape_has_path_effect(SP_SHAPE(path)) ) { + gchar *str = sp_svg_write_path(rcurve->get_pathvector()); + if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(path)) ) { SP_OBJECT_REPR(path)->setAttribute("inkscape:original-d", str); } else { SP_OBJECT_REPR(path)->setAttribute("d", str); } g_free(str); - sp_curve_unref(rcurve); + rcurve->unref(); + + // reverse nodetypes order (Bug #179866) + gchar *nodetypes = g_strdup(SP_OBJECT_REPR(path)->attribute("sodipodi:nodetypes")); + if ( nodetypes ) { + SP_OBJECT_REPR(path)->setAttribute("sodipodi:nodetypes", g_strreverse(nodetypes)); + g_free(nodetypes); + } } desktop->clearWaitingCursor(); if (did) { - sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_REVERSE, - _("Reverse path")); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_REVERSE, + _("Reverse path")); } else { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("No paths to reverse in the selection.")); } @@ -463,4 +650,4 @@ sp_selected_path_reverse() fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :