Merge lp:~inkscape+alexander/inkscape/clones into lp:~inkscape.dev/inkscape/trunk

Proposed by Alexander Brock
Status: Superseded
Proposed branch: lp:~inkscape+alexander/inkscape/clones
Merge into: lp:~inkscape.dev/inkscape/trunk
Diff against target: 184 lines (+80/-11)
5 files modified
src/menus-skeleton.h (+1/-0)
src/object-set.h (+14/-2)
src/selection-chemistry.cpp (+59/-9)
src/verbs.cpp (+5/-0)
src/verbs.h (+1/-0)
To merge this branch: bzr merge lp:~inkscape+alexander/inkscape/clones
Reviewer Review Type Date Requested Status
Mc code, refactor Pending
Martin Owens Pending
Review via email: mp+310059@code.launchpad.net

This proposal supersedes a proposal from 2016-10-27.

This proposal has been superseded by a proposal from 2016-11-04.

Commit message

Add "Edit->Clone->Unlink Clones recursively" function (ObjectSet::unlinkRecursive)

Description of the change

I added "Edit->Clone->Unlink Clones recursively": It recurses into groups and unlinks every clone it finds, including clones used as clipping paths.

Mc... rewrote the function and I improved it further.

To post a comment you must log in.
Revision history for this message
Mc (mc...) wrote : Posted in a previous version of this proposal

Looks mostly fine, but I'm not a big fan of the _functions which seems to duplicate the code a bit and double number of functions. I'd prefer a ", bool flash = true" or something like that in the already present function and "if(doflash) desktop->messageStack()->flash(whatever)"

I'll re-read the main new function during the week-end and extensively test it.

Revision history for this message
Mc (mc...) wrote : Posted in a previous version of this proposal

Ok, so with the small change I did to the codebase, here's what I think should be done:

-> add a "bool skip_undo" flag to ::setMask(), ::unsetMask(), and ::unlink() similar to what's done in other methods in the file. (Also add it to your recursive method).

-> make a ObjectSet with no SPDesktop (just the SPDocument) containing the selection, so that it does not flash recursively.

-> make all the calls with the skip_undo flag on your ObjectSet.

review: Needs Resubmitting (code, refactor)
Revision history for this message
Alexander Brock (brock-alexander) wrote : Posted in a previous version of this proposal

> Ok, so with the small change I did to the codebase, here's what I think should
> be done:
>
> -> add a "bool skip_undo" flag to ::setMask(), ::unsetMask(), and ::unlink()
> similar to what's done in other methods in the file. (Also add it to your
> recursive method).
>
> -> make a ObjectSet with no SPDesktop (just the SPDocument) containing the
> selection, so that it does not flash recursively.
>
> -> make all the calls with the skip_undo flag on your ObjectSet.

I tried to merge your changes with my branch, failed and restarted from scratch.
I added an unlinkRecursive method to ObjectSet, added the bool skip_undo where I needed it and force-pushed the code to the old location (lp:~inkscape+alexander/inkscape/clones).

Revision history for this message
Mc (mc...) wrote : Posted in a previous version of this proposal

Good !
I did a merge proposal to your branch to propose a simpler version of the recursive function, though it can be improved and further simplified by putting the unclip/reclip business in the unlink() function where it probably belongs.
If you find the courage, you can also propose unit tests to test the function (see testfiles/src/object-set-test.cpp )

Revision history for this message
Tavmjong Bah (tavmjong-free) wrote :

Please get rid of gratuitous spacing differences. It makes it much harder to see what is really being changed.

Revision history for this message
Alexander Brock (brock-alexander) wrote :

@mc...: Thank you for rewriting the unlinkRecursive function. I checked out your version, tested it, found some problems and fixed them:

1.

unlinked = unlinked || tmp_set.unlinkRecursive(true);

From the C++ standard: "Unlike |, || guarantees left-to-right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to true."

So if unlinked is already true then the unlinkRecursive function is not called, preventing groups of clones from beeing unlinked. I changed it:

unlinked = tmp_set.unlinkRecursive(true) || unlinked;

2.

if(SP_IS_GROUP(it)) {
......
} else if(dynamic_cast<SPUse*>(it) || dynamic_cast<SPText*>(it) || dynamic_cast<SPTRef*>(it)) {

This causes two problems: First, if an Object is a group masked (or clipped) by a clone that clone would not be unlinked.
Second, if the object is not SPUse, SPText or SPTRef it still is necessary to run the code, it might be clipped or masked by a clone of some object.
I removed the else if so the code runs unconditionally.

3.

unlinked = unlinked || tmp_set.unlink();

This causes calls to DocumentUndo::done (also see 1.), so I changed it:

unlinked = tmp_set.unlink(true) || unlinked;

15205. By Alexander Brock <email address hidden>

Add "Edit->Clone->Unlink Clones recursively" function (ObjectSet::unlinkRecursive)

15206. By Alexander Brock <email address hidden>

Preserve clips when unlinking clones

15207. By Alexander Brock <email address hidden>

Make ObjectSet::unlink() work for clones which are both clipped and masked

15208. By Alexander Brock <email address hidden>

Remove duplicate code from unlinkRecursive() and fix issue with clones of groups

15209. By Alexander Brock <email address hidden>

Fix selection issue with ObjectSet::unlinkRecursive() and add tests for it.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/menus-skeleton.h'
--- src/menus-skeleton.h 2016-04-15 14:39:49 +0000
+++ src/menus-skeleton.h 2016-11-04 14:19:19 +0000
@@ -66,6 +66,7 @@
66" <verb verb-id=\"EditClone\" />\n"66" <verb verb-id=\"EditClone\" />\n"
67" <verb verb-id=\"DialogClonetiler\" />\n"67" <verb verb-id=\"DialogClonetiler\" />\n"
68" <verb verb-id=\"EditUnlinkClone\" />\n"68" <verb verb-id=\"EditUnlinkClone\" />\n"
69" <verb verb-id=\"EditUnlinkCloneRecursive\" />\n"
69" <verb verb-id=\"EditRelinkClone\" />\n"70" <verb verb-id=\"EditRelinkClone\" />\n"
70" <verb verb-id=\"EditCloneSelectOriginal\" />\n"71" <verb verb-id=\"EditCloneSelectOriginal\" />\n"
71" <verb verb-id=\"EditCloneOriginalPathLPE\" />\n"72" <verb verb-id=\"EditCloneOriginalPathLPE\" />\n"
7273
=== modified file 'src/object-set.h'
--- src/object-set.h 2016-11-03 23:28:50 +0000
+++ src/object-set.h 2016-11-04 14:19:19 +0000
@@ -330,7 +330,19 @@
330 void deleteItems();330 void deleteItems();
331 void duplicate(bool suppressDone = false, bool duplicateLayer = false);331 void duplicate(bool suppressDone = false, bool duplicateLayer = false);
332 void clone();332 void clone();
333 void unlink();333
334 /**
335 * @brief Unlink all directly selected clones.
336 * @param skip_undo If this is set to true the call to DocumentUndo::done is omitted.
337 * @return True if anything was unlinked, otherwise false.
338 */
339 bool unlink(const bool skip_undo = false);
340 /**
341 * @brief Recursively unlink any clones present in the current selection,
342 * including clones which are used to clip other objects, groups of clones etc.
343 * @return true if anything was unlinked, otherwise false.
344 */
345 bool unlinkRecursive(const bool skip_undo = false);
334 void relink();346 void relink();
335 void cloneOriginal();347 void cloneOriginal();
336 void cloneOriginalPathLPE();348 void cloneOriginalPathLPE();
@@ -376,7 +388,7 @@
376 void createBitmapCopy();388 void createBitmapCopy();
377 void setMask(bool apply_clip_path, bool apply_to_layer = false, bool skip_undo = false);389 void setMask(bool apply_clip_path, bool apply_to_layer = false, bool skip_undo = false);
378 void editMask(bool clip);390 void editMask(bool clip);
379 void unsetMask(bool apply_clip_path);391 void unsetMask(const bool apply_clip_path, const bool skip_undo = false);
380 void setClipGroup();392 void setClipGroup();
381 393
382 // moves394 // moves
383395
=== modified file 'src/selection-chemistry.cpp'
--- src/selection-chemistry.cpp 2016-11-02 23:08:41 +0000
+++ src/selection-chemistry.cpp 2016-11-04 14:19:19 +0000
@@ -2653,12 +2653,12 @@
2653}2653}
26542654
26552655
2656void ObjectSet::unlink()2656bool ObjectSet::unlink(const bool skip_undo)
2657{2657{
2658 if (isEmpty()) {2658 if (isEmpty()) {
2659 if(desktop())2659 if(desktop())
2660 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>clones</b> to unlink."));2660 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>clones</b> to unlink."));
2661 return;2661 return false;
2662 }2662 }
26632663
2664 // Get a copy of current selection.2664 // Get a copy of current selection.
@@ -2715,8 +2715,56 @@
2715 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to unlink</b> in the selection."));2715 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to unlink</b> in the selection."));
2716 }2716 }
27172717
2718 DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE,2718 if (!skip_undo) {
2719 _("Unlink clone"));2719 DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE,
2720 _("Unlink clone"));
2721 }
2722 return unlinked;
2723}
2724
2725bool ObjectSet::unlinkRecursive(const bool skip_undo) {
2726 if (isEmpty()){
2727 if (desktop())
2728 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>clones</b> to unlink."));
2729 return false;
2730 }
2731 bool unlinked = false;
2732 ObjectSet tmp_set(document());
2733 std::vector<SPItem*> items_(items().begin(), items().end());
2734 for (auto& it:items_){
2735 if (SP_IS_GROUP(it)) {
2736 std::vector<SPObject*> c = it->childList(false);
2737 tmp_set.setList(c);
2738 unlinked = tmp_set.unlinkRecursive(true) || unlinked;
2739 }
2740 tmp_set.set(it);
2741 bool has_clip = false;
2742 bool has_mask = false;
2743 Inkscape::URIReference *clip = it->clip_ref;
2744 Inkscape::URIReference *mask = it->mask_ref;
2745 if ((NULL != clip) && (NULL != clip->getObject())) {
2746 tmp_set.unsetMask(true,true);
2747 has_clip = true;
2748 }
2749 if ((NULL != mask) && (NULL != mask->getObject())) {
2750 tmp_set.unsetMask(false,true);
2751 has_mask = true;
2752 }
2753 unlinked = tmp_set.unlink(true) || unlinked;
2754 if (has_mask)
2755 tmp_set.setMask(false,false,true);
2756 if (has_clip)
2757 tmp_set.setMask(true,false,true);
2758 }
2759 if (!unlinked) {
2760 if(desktop())
2761 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to unlink</b> in the selection."));
2762 }
2763 if (!skip_undo) {
2764 DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE,
2765 _("Unlink clone recursively"));
2766 }
2767 return unlinked;
2720}2768}
27212769
2722void ObjectSet::cloneOriginal()2770void ObjectSet::cloneOriginal()
@@ -3960,7 +4008,7 @@
3960 }4008 }
3961}4009}
39624010
3963void ObjectSet::unsetMask(bool apply_clip_path) {4011void ObjectSet::unsetMask(const bool apply_clip_path, const bool skip_undo) {
3964 SPDocument *doc = document();4012 SPDocument *doc = document();
3965 Inkscape::XML::Document *xml_doc = doc->getReprDoc();4013 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
39664014
@@ -4082,10 +4130,12 @@
4082 // rebuild selection4130 // rebuild selection
4083 addList(items_to_select);4131 addList(items_to_select);
40844132
4085 if (apply_clip_path) {4133 if (!skip_undo) {
4086 DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path"));4134 if (apply_clip_path) {
4087 } else {4135 DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path"));
4088 DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask"));4136 } else {
4137 DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask"));
4138 }
4089 }4139 }
4090}4140}
40914141
40924142
=== modified file 'src/verbs.cpp'
--- src/verbs.cpp 2016-11-02 22:42:43 +0000
+++ src/verbs.cpp 2016-11-04 14:19:19 +0000
@@ -1009,6 +1009,9 @@
1009 case SP_VERB_EDIT_UNLINK_CLONE:1009 case SP_VERB_EDIT_UNLINK_CLONE:
1010 dt->selection->unlink();1010 dt->selection->unlink();
1011 break;1011 break;
1012 case SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE:
1013 dt->selection->unlinkRecursive();
1014 break;
1012 case SP_VERB_EDIT_RELINK_CLONE:1015 case SP_VERB_EDIT_RELINK_CLONE:
1013 dt->selection->relink();1016 dt->selection->relink();
1014 break;1017 break;
@@ -2530,6 +2533,8 @@
2530 N_("Create a clone (a copy linked to the original) of selected object"), INKSCAPE_ICON("edit-clone")),2533 N_("Create a clone (a copy linked to the original) of selected object"), INKSCAPE_ICON("edit-clone")),
2531 new EditVerb(SP_VERB_EDIT_UNLINK_CLONE, "EditUnlinkClone", N_("Unlin_k Clone"),2534 new EditVerb(SP_VERB_EDIT_UNLINK_CLONE, "EditUnlinkClone", N_("Unlin_k Clone"),
2532 N_("Cut the selected clones' links to the originals, turning them into standalone objects"), INKSCAPE_ICON("edit-clone-unlink")),2535 N_("Cut the selected clones' links to the originals, turning them into standalone objects"), INKSCAPE_ICON("edit-clone-unlink")),
2536 new EditVerb(SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, "EditUnlinkCloneRecursive", N_("Unlink Clones _recursively"),
2537 N_("Unlink all clones in the selection, even if they are in groups."), INKSCAPE_ICON("edit-clone-unlink")),
2533 new EditVerb(SP_VERB_EDIT_RELINK_CLONE, "EditRelinkClone", N_("Relink to Copied"),2538 new EditVerb(SP_VERB_EDIT_RELINK_CLONE, "EditRelinkClone", N_("Relink to Copied"),
2534 N_("Relink the selected clones to the object currently on the clipboard"), NULL),2539 N_("Relink the selected clones to the object currently on the clipboard"), NULL),
2535 new EditVerb(SP_VERB_EDIT_CLONE_SELECT_ORIGINAL, "EditCloneSelectOriginal", N_("Select _Original"),2540 new EditVerb(SP_VERB_EDIT_CLONE_SELECT_ORIGINAL, "EditCloneSelectOriginal", N_("Select _Original"),
25362541
=== modified file 'src/verbs.h'
--- src/verbs.h 2016-10-08 06:16:53 +0000
+++ src/verbs.h 2016-11-04 14:19:19 +0000
@@ -83,6 +83,7 @@
83 SP_VERB_EDIT_DUPLICATE,83 SP_VERB_EDIT_DUPLICATE,
84 SP_VERB_EDIT_CLONE,84 SP_VERB_EDIT_CLONE,
85 SP_VERB_EDIT_UNLINK_CLONE,85 SP_VERB_EDIT_UNLINK_CLONE,
86 SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE,
86 SP_VERB_EDIT_RELINK_CLONE,87 SP_VERB_EDIT_RELINK_CLONE,
87 SP_VERB_EDIT_CLONE_SELECT_ORIGINAL,88 SP_VERB_EDIT_CLONE_SELECT_ORIGINAL,
88 SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE,89 SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE,