X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fpath-chemistry.cpp;h=c44ab5bc616e3c64e26e2558f27a467b59478bfd;hb=91c307b04acb17109e2811ed9f525217e03cea02;hp=cfdbc0b54d276472e500be1762e1ffb3e6047da8;hpb=ae22ad7adc4a7a418e71f5dbab8c1f0f7f464562;p=inkscape.git diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index cfdbc0b54..c44ab5bc6 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -27,39 +27,26 @@ #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); -static bool sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select); - -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 -}; - void -sp_selected_path_combine(void) +sp_selected_path_combine(SPDesktop *desktop) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; Inkscape::Selection *selection = sp_desktop_selection(desktop); + SPDocument *doc = sp_desktop_document(desktop); - if (g_slist_length((GSList *) selection->itemList()) < 2) { - sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select at least two objects to combine.")); + if (g_slist_length((GSList *) selection->itemList()) < 1) { + sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to combine.")); return; } @@ -67,43 +54,69 @@ sp_selected_path_combine(void) // set "busy" cursor desktop->setWaitingCursor(); - sp_selected_path_to_curves0(FALSE, 0); - GSList *items = g_slist_copy((GSList *) selection->itemList()); + + items = sp_degroup_list (items); // descend into any groups in selection + + 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))); + + items = sp_degroup_list (items); // converting to path may have added more groups, descend again + items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); items = g_slist_reverse(items); - // remember the position, id and style of the topmost path, they will be assigned to the combined one + // 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; SPCurve* curve = 0; - bool did = false; SPItem *first = NULL; Inkscape::XML::Node *parent = NULL; + if (did) { + selection->clear(); + } + for (GSList *i = items; i != NULL; i = i->next) { // going from top to bottom SPItem *item = (SPItem *) i->data; if (!SP_IS_PATH(item)) continue; - did = true; - SPCurve *c = sp_shape_get_curve(SP_SHAPE(item)); + 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")); - sp_curve_transform(c, item->transform); + path_effect = g_strdup(SP_OBJECT_REPR(first)->attribute("inkscape:path-effect")); + //c->transform(item->transform); curve = c; } else { - sp_curve_transform(c, item->getRelativeTransform(SP_OBJECT(first))); - sp_curve_append(curve, c, false); - sp_curve_unref(c); + c->transform(item->getRelativeTransform(SP_OBJECT(first))); + curve->append(c, false); + c->unref(); } // unless this is the topmost object, @@ -119,25 +132,28 @@ sp_selected_path_combine(void) g_slist_free(items); if (did) { - selection->clear(); - - // delete the topmost one so that its clones don't get alerted; this object will be - // restored shortly, with the same id SP_OBJECT(first)->deleteObject(false); + // delete the topmost. Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); - // restore id and 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("inkscape:path-effect", path_effect); + g_free(path_effect); + // set path data corresponding to new curve - gchar *dstring = sp_svg_write_path(SP_CURVE_BPATH(curve)); - sp_curve_unref(curve); - repr->setAttribute("d", dstring); + 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); // add the new group to the parent of the topmost @@ -161,10 +177,8 @@ sp_selected_path_combine(void) } 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()) { @@ -189,7 +203,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; @@ -200,20 +214,21 @@ sp_selected_path_break_apart(void) char const *id = SP_OBJECT_REPR(item)->attribute("id"); gchar *style = g_strdup(SP_OBJECT(item)->repr->attribute("style")); + gchar *path_effect = g_strdup(SP_OBJECT(item)->repr->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) { @@ -222,8 +237,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 @@ -246,7 +266,7 @@ sp_selected_path_break_apart(void) g_slist_free(reprs); g_slist_free(list); g_free(style); - + g_free(path_effect); } desktop->clearWaitingCursor(); @@ -261,20 +281,8 @@ sp_selected_path_break_apart(void) /* This function is an entry point from GUI */ void -sp_selected_path_to_curves(bool interactive) -{ - if (interactive) { - sp_selected_path_to_curves0(TRUE, SP_TOCURVE_INTERACTIVE); - } else { - sp_selected_path_to_curves0(false, 0); - } -} - -static void -sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy*/) +sp_selected_path_to_curves(SPDesktop *desktop, bool interactive) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { @@ -315,8 +323,33 @@ sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy } } -static bool -sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select) +/** 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; @@ -325,6 +358,14 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec 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 @@ -332,7 +373,7 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec if (SP_IS_BOX3D(item)) { // convert 3D box to ordinary group of paths; replace the old element in 'selected' with the new group - Inkscape::XML::Node *repr = box3d_convert_to_group(SP_BOX3D(item)); + Inkscape::XML::Node *repr = SP_OBJECT_REPR(box3d_convert_to_group(SP_BOX3D(item))); if (repr) { *to_select = g_slist_prepend (*to_select, repr); @@ -344,7 +385,7 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec } if (SP_IS_GROUP(item)) { - sp_lpe_item_remove_path_effect(SP_LPE_ITEM(item), true); + 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; @@ -373,6 +414,10 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec 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); @@ -381,6 +426,16 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec 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); @@ -399,25 +454,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(); - } + } if (!curve) return NULL; // Prevent empty paths from being added to the document // otherwise we end up with zomby markup in the SVG file - if(curve->end <= 0) + if(curve->is_empty()) { - sp_curve_unref(curve); + curve->unref(); return NULL; } - Inkscape::XML::Document *xml_doc = SP_OBJECT_REPR(item)->document(); Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); /* Transformation */ repr->setAttribute("transform", SP_OBJECT_REPR(item)->attribute("transform")); @@ -438,24 +566,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(); @@ -479,9 +604,9 @@ 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)); + 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 { @@ -489,7 +614,14 @@ sp_selected_path_reverse() } 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();