X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fpath-chemistry.cpp;h=bec3c5cbff27f23c51bd27e83da0d13fad7c6c6c;hb=ec12cb48c1da4c3029b9e8c2eea263499d319599;hp=9dfa760ea2c0280138bcf3e89aeea1f9f402fc15;hpb=235e8e7977f17c6070b4691837b8a6cb2bd4cc35;p=inkscape.git diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 9dfa760ea..bec3c5cbf 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -1,5 +1,3 @@ -#define __SP_PATH_CHEMISTRY_C__ - /* * Here are handlers for modifying selections, specific to paths * @@ -7,6 +5,8 @@ * Lauris Kaplinski * bulia byak * Jasper van de Gronde + * Jon A. Cruz + * Abhishek Sharma * * Copyright (C) 1999-2008 Authors * Copyright (C) 2001-2002 Ximian, Inc. @@ -27,10 +27,8 @@ #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" @@ -38,29 +36,19 @@ #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 -}; +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); 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; } @@ -69,10 +57,13 @@ sp_selected_path_combine(void) desktop->setWaitingCursor(); 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)) + if (!SP_IS_PATH(item) && !SP_IS_GROUP(item)) to_paths = g_slist_prepend(to_paths, item); } GSList *converted = NULL; @@ -81,6 +72,8 @@ sp_selected_path_combine(void) 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); @@ -95,12 +88,20 @@ sp_selected_path_combine(void) 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; + + 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 @@ -115,7 +116,7 @@ sp_selected_path_combine(void) //c->transform(item->transform); curve = c; } else { - c->transform(to_2geom(item->getRelativeTransform(SP_OBJECT(first)))); + c->transform(item->getRelativeTransform(SP_OBJECT(first))); curve->append(c, false); c->unref(); } @@ -133,40 +134,41 @@ 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::Document *xml_doc = desktop->doc()->getReprDoc(); Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); // restore id, transform, path effect, and style repr->setAttribute("id", id); - if (transform) repr->setAttribute("transform", transform); + 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(curve->get_pathvector()); curve->unref(); - repr->setAttribute("d", dstring); - if (path_effect) + if (path_effect) { repr->setAttribute("inkscape:original-d", dstring); + } else { + repr->setAttribute("d", dstring); + } g_free(dstring); - repr->setAttribute("inkscape:path-effect", path_effect); - g_free(path_effect); - // add the new group to the parent of the topmost parent->appendChild(repr); // move to the position of the topmost, reduced by the number of deleted items repr->setPosition(position > 0 ? position : 0); - sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_COMBINE, - _("Combine")); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_COMBINE, + _("Combine")); selection->set(repr); @@ -180,10 +182,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()) { @@ -218,10 +218,12 @@ 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")); - gchar *path_effect = g_strdup(SP_OBJECT(item)->repr->attribute("inkscape:path-effect")); + // 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")); - Geom::PathVector apv = curve->get_pathvector() * to_2geom(SP_ITEM(path)->transform); + Geom::PathVector apv = curve->get_pathvector() * SP_ITEM(path)->transform; curve->unref(); @@ -242,14 +244,15 @@ sp_selected_path_break_apart(void) Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); repr->setAttribute("style", style); + repr->setAttribute("inkscape:path-effect", path_effect); + gchar *str = sp_svg_write_path(curve->get_pathvector()); - repr->setAttribute("d", str); if (path_effect) repr->setAttribute("inkscape:original-d", str); + else + repr->setAttribute("d", str); g_free(str); - repr->setAttribute("inkscape:path-effect", path_effect); - // add the new repr to the parent parent->appendChild(repr); @@ -270,14 +273,14 @@ 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.")); } @@ -285,20 +288,8 @@ sp_selected_path_break_apart(void) /* This function is an entry point from GUI */ void -sp_selected_path_to_curves(bool interactive) +sp_selected_path_to_curves(SPDesktop *desktop, 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*/) -{ - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { @@ -330,8 +321,8 @@ sp_selected_path_to_curves0(gboolean interactive, guint32 /*text_grouping_policy if (interactive) { desktop->clearWaitingCursor(); if (did) { - sp_document_done(sp_desktop_document(desktop), SP_VERB_OBJECT_TO_CURVE, - _("Object to path")); + 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; @@ -339,8 +330,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; @@ -349,6 +365,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 @@ -397,6 +421,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); @@ -405,6 +433,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); @@ -423,12 +461,86 @@ 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; @@ -441,7 +553,6 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) 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")); @@ -474,12 +585,9 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) } -// 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(); @@ -514,13 +622,20 @@ sp_selected_path_reverse() g_free(str); 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.")); } @@ -535,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 :