summaryrefslogtreecommitdiffstats
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
parentUpdate to experimental r13402 (diff)
downloadinkscape-eb4d7e1225a2e1e5a5d1da8d0ab784632bc77291.tar.gz
inkscape-eb4d7e1225a2e1e5a5d1da8d0ab784632bc77291.zip
Add clip group option from Ponyscape
(bzr r13090.1.83)
-rw-r--r--src/interface.cpp11
-rw-r--r--src/interface.h1
-rw-r--r--src/selection-chemistry.cpp112
-rw-r--r--src/selection-chemistry.h1
-rw-r--r--src/ui/dialog/objects.cpp12
-rw-r--r--src/verbs.cpp5
-rw-r--r--src/verbs.h1
7 files changed, 139 insertions, 4 deletions
diff --git a/src/interface.cpp b/src/interface.cpp
index 1cbeb44a3..e47cff598 100644
--- a/src/interface.cpp
+++ b/src/interface.cpp
@@ -1756,6 +1756,13 @@ void ContextMenu::MakeItemMenu (void)
}
mi->show();
append(*mi);
+
+ /*SSet Clip Group */
+ mi = Gtk::manage(new Gtk::MenuItem(_("Create Clip G_roup"),1));
+ mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::CreateGroupClip));
+ mi->set_sensitive(TRUE);
+ mi->show();
+ append(*mi);
/* Set Clip */
mi = Gtk::manage(new Gtk::MenuItem(_("Set Cl_ip"), 1));
@@ -1867,6 +1874,10 @@ void ContextMenu::ReleaseMask(void)
sp_selection_unset_mask(_desktop, false);
}
+void ContextMenu::CreateGroupClip(void)
+{
+ sp_selection_set_clipgroup(_desktop);
+}
void ContextMenu::SetClip(void)
{
diff --git a/src/interface.h b/src/interface.h
index 215a3bfc9..a4eedf9db 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -187,6 +187,7 @@ class ContextMenu : public Gtk::Menu
void SelectSameStrokeStyle(void);
void SelectSameObjectType(void);
void ItemCreateLink(void);
+ void CreateGroupClip(void);
void SetMask(void);
void ReleaseMask(void);
void SetClip(void);
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:
diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h
index d76a67a9d..baa530806 100644
--- a/src/selection-chemistry.h
+++ b/src/selection-chemistry.h
@@ -156,6 +156,7 @@ void sp_document_get_export_hints (SPDocument * doc, Glib::ustring &filename, fl
void sp_selection_create_bitmap_copy (SPDesktop *desktop);
+void sp_selection_set_clipgroup(SPDesktop *desktop);
void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_layer);
void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path);
diff --git a/src/ui/dialog/objects.cpp b/src/ui/dialog/objects.cpp
index 85583a0e7..2d558daae 100644
--- a/src/ui/dialog/objects.cpp
+++ b/src/ui/dialog/objects.cpp
@@ -106,7 +106,7 @@ enum {
BUTTON_LOCK_ALL,
BUTTON_UNLOCK_ALL,
BUTTON_SETCLIP,
-// BUTTON_CLIPGROUP,
+ BUTTON_CLIPGROUP,
// BUTTON_SETINVCLIP,
BUTTON_UNSETCLIP,
BUTTON_SETMASK,
@@ -269,7 +269,7 @@ Gtk::MenuItem& ObjectsPanel::_addPopupItem( SPDesktop *desktop, unsigned int cod
}
if ( action ) {
- label = action->name;
+ // label = action->name;
}
}
}
@@ -1257,6 +1257,10 @@ bool ObjectsPanel::_executeAction()
_fireAction( SP_VERB_LAYER_UNLOCK_ALL );
}
break;
+ case BUTTON_CLIPGROUP:
+ {
+ _fireAction ( SP_VERB_OBJECT_CREATE_CLIP_GROUP );
+ }
case BUTTON_SETCLIP:
{
_fireAction( SP_VERB_OBJECT_SET_CLIPPATH );
@@ -1935,8 +1939,8 @@ ObjectsPanel::ObjectsPanel() :
_popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem()));
_watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_CLIPPATH, 0, "Set Clip", (int)BUTTON_SETCLIP ) );
- //not implemented
- //_watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_CREATE_CLIP_GROUP, 0, "Create Clip Group", (int)BUTTON_CLIPGROUP ) );
+
+ _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_CREATE_CLIP_GROUP, 0, "Create Clip Group", (int)BUTTON_CLIPGROUP ) );
//will never be implemented
//_watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_INVERSE_CLIPPATH, 0, "Set Inverse Clip", (int)BUTTON_SETINVCLIP ) );
diff --git a/src/verbs.cpp b/src/verbs.cpp
index 73613ab9e..f0a49a81a 100644
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
@@ -1568,6 +1568,9 @@ void ObjectVerb::perform( SPAction *action, void *data)
case SP_VERB_OBJECT_SET_CLIPPATH:
sp_selection_set_mask(dt, true, false);
break;
+ case SP_VERB_OBJECT_CREATE_CLIP_GROUP:
+ sp_selection_set_clipgroup(dt);
+ break;
case SP_VERB_OBJECT_EDIT_CLIPPATH:
sp_selection_edit_clip_or_mask(dt, true);
break;
@@ -2717,6 +2720,8 @@ Verb *Verb::_base_verbs[] = {
N_("Remove mask from selection"), NULL),
new ObjectVerb(SP_VERB_OBJECT_SET_CLIPPATH, "ObjectSetClipPath", N_("_Set"),
N_("Apply clipping path to selection (using the topmost object as clipping path)"), NULL),
+ new ObjectVerb(SP_VERB_OBJECT_CREATE_CLIP_GROUP, "ObjectCreateClipGroup", N_("Create Cl_ip Group"),
+ N_("Creates a clip group using the selected objects as a base"), NULL),
new ObjectVerb(SP_VERB_OBJECT_EDIT_CLIPPATH, "ObjectEditClipPath", N_("_Edit"),
N_("Edit clipping path"), INKSCAPE_ICON("path-clip-edit")),
new ObjectVerb(SP_VERB_OBJECT_UNSET_CLIPPATH, "ObjectUnSetClipPath", N_("_Release"),
diff --git a/src/verbs.h b/src/verbs.h
index 821c9ee82..84d31fe42 100644
--- a/src/verbs.h
+++ b/src/verbs.h
@@ -175,6 +175,7 @@ enum {
SP_VERB_OBJECT_EDIT_MASK,
SP_VERB_OBJECT_UNSET_MASK,
SP_VERB_OBJECT_SET_CLIPPATH,
+ SP_VERB_OBJECT_CREATE_CLIP_GROUP,
SP_VERB_OBJECT_EDIT_CLIPPATH,
SP_VERB_OBJECT_UNSET_CLIPPATH,
/* Tag */