summaryrefslogtreecommitdiffstats
path: root/src/selection-chemistry.cpp
diff options
context:
space:
mode:
authorLiam P. White <inkscapebronyat-signgmaildotcom>2014-06-14 18:51:11 +0000
committerLiam P. White <inkscapebronyat-signgmaildotcom>2014-06-14 18:51:11 +0000
commiteb4d7e1225a2e1e5a5d1da8d0ab784632bc77291 (patch)
treefc11d1a0d8e6e59982a0b49ccce5b1e074fde169 /src/selection-chemistry.cpp
parentUpdate to experimental r13402 (diff)
downloadinkscape-eb4d7e1225a2e1e5a5d1da8d0ab784632bc77291.tar.gz
inkscape-eb4d7e1225a2e1e5a5d1da8d0ab784632bc77291.zip
Add clip group option from Ponyscape
(bzr r13090.1.83)
Diffstat (limited to 'src/selection-chemistry.cpp')
-rw-r--r--src/selection-chemistry.cpp112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp
index a350dd7a7..d2f6d692a 100644
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -3661,6 +3661,118 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop)
g_free(filepath);
}
+/* Creates a mask or clipPath from selection.
+ * What is a clip group?
+ * A clip group is a tangled mess of XML that allows an object inside a group
+ * to clip the entire group using a few <use>s and generally irritating me.
+ */
+
+void sp_selection_set_clipgroup(SPDesktop *desktop)
+{
+ if (desktop == NULL) {
+ return;
+ }
+ SPDocument* doc = sp_desktop_document(desktop);
+ Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+
+ Inkscape::Selection *selection = sp_desktop_selection(desktop);
+ if (selection->isEmpty()) {
+ desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to create clippath or mask from."));
+ return;
+ }
+
+ GSList const *l = const_cast<GSList *>(selection->reprList());
+
+ GSList *p = g_slist_copy(const_cast<GSList *>(l));
+
+ p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position);
+
+ selection->clear();
+
+ gint topmost = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->position();
+ Inkscape::XML::Node *topmost_parent = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->parent();
+
+ Inkscape::XML::Node *inner = xml_doc->createElement("svg:g");
+ inner->setAttribute("inkscape:label", "Clip");
+
+ while (p) {
+ Inkscape::XML::Node *current = static_cast<Inkscape::XML::Node *>(p->data);
+
+ if (current->parent() == topmost_parent) {
+ Inkscape::XML::Node *spnew = current->duplicate(xml_doc);
+ sp_repr_unparent(current);
+ inner->appendChild(spnew);
+ Inkscape::GC::release(spnew);
+ topmost --; // only reduce count for those items deleted from topmost_parent
+ } else { // move it to topmost_parent first
+ GSList *temp_clip = NULL;
+
+ // At this point, current may already have no item, due to its being a clone whose original is already moved away
+ // So we copy it artificially calculating the transform from its repr->attr("transform") and the parent transform
+ gchar const *t_str = current->attribute("transform");
+ Geom::Affine item_t(Geom::identity());
+ if (t_str)
+ sp_svg_transform_read(t_str, &item_t);
+ item_t *= SP_ITEM(doc->getObjectByRepr(current->parent()))->i2doc_affine();
+ // FIXME: when moving both clone and original from a transformed group (either by
+ // grouping into another parent, or by cut/paste) the transform from the original's
+ // parent becomes embedded into original itself, and this affects its clones. Fix
+ // this by remembering the transform diffs we write to each item into an array and
+ // then, if this is clone, looking up its original in that array and pre-multiplying
+ // it by the inverse of that original's transform diff.
+
+ sp_selection_copy_one(current, item_t, &temp_clip, xml_doc);
+ sp_repr_unparent(current);
+
+ // paste into topmost_parent (temporarily)
+ GSList *copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), &temp_clip);
+ if (temp_clip) g_slist_free(temp_clip);
+ if (copied) { // if success,
+ // take pasted object (now in topmost_parent)
+ Inkscape::XML::Node *in_topmost = static_cast<Inkscape::XML::Node *>(copied->data);
+ // make a copy
+ Inkscape::XML::Node *spnew = in_topmost->duplicate(xml_doc);
+ // remove pasted
+ sp_repr_unparent(in_topmost);
+ // put its copy into group
+ inner->appendChild(spnew);
+ Inkscape::GC::release(spnew);
+ g_slist_free(copied);
+ }
+ }
+ p = g_slist_remove(p, current);
+ }
+
+ Inkscape::XML::Node *outer = xml_doc->createElement("svg:g");
+ outer->appendChild(inner);
+ topmost_parent->appendChild(outer);
+ outer->setPosition(topmost + 1);
+
+ Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
+ clone->setAttribute("x", "0", false);
+ clone->setAttribute("y", "0", false);
+ clone->setAttribute("xlink:href", g_strdup_printf("#%s", inner->attribute("id")), false);
+
+ clone->setAttribute("inkscape:transform-center-x", inner->attribute("inkscape:transform-center-x"), false);
+ clone->setAttribute("inkscape:transform-center-y", inner->attribute("inkscape:transform-center-y"), false);
+
+ const Geom::Affine maskTransform(Geom::Affine::identity());
+ GSList *templist = NULL;
+
+ templist = g_slist_append(templist, clone);
+ // add the new clone to the top of the original's parent
+ gchar const *mask_id = SPClipPath::create(templist, doc, &maskTransform);
+
+ g_slist_free(templist);
+
+ outer->setAttribute("clip-path", g_strdup_printf("url(#%s)", mask_id));
+
+ Inkscape::GC::release(clone);
+
+ selection->set(outer);
+ DocumentUndo::done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Create Clip Group"));
+}
+
/**
* Creates a mask or clipPath from selection.
* Two different modes: