Merge lp:~inkscape+alexander/inkscape/clones into lp:~inkscape.dev/inkscape/trunk
- clones
- Merge into trunk
| Status: | Merged |
|---|---|
| Approved by: | Mc |
| Approved revision: | 15209 |
| Merged at revision: | 15220 |
| Proposed branch: | lp:~inkscape+alexander/inkscape/clones |
| Merge into: | lp:~inkscape.dev/inkscape/trunk |
| Diff against target: |
415 lines (+251/-40) 6 files modified
src/menus-skeleton.h (+1/-0) src/object-set.h (+14/-2) src/selection-chemistry.cpp (+92/-38) src/verbs.cpp (+5/-0) src/verbs.h (+1/-0) testfiles/src/object-set-test.cpp (+138/-0) |
| To merge this branch: | bzr merge lp:~inkscape+alexander/inkscape/clones |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Martin Owens | Pending | ||
| Mc | code, refactor | Pending | |
|
Review via email:
|
|||
This proposal supersedes a proposal from 2016-11-04.
Commit message
Add "Edit->
Description of the change
I removed all the gratuitous spacing differences.
| Mc (mc...) wrote : Posted in a previous version of this proposal | # |
| 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.
| 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).
| 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/
| Tavmjong Bah (tavmjong-free) wrote : Posted in a previous version of this proposal | # |
Please get rid of gratuitous spacing differences. It makes it much harder to see what is really being changed.
| Alexander Brock (brock-alexander) wrote : Posted in a previous version of this proposal | # |
@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.
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.
2.
if(SP_IS_GROUP(it)) {
......
} else if(dynamic_
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.
- 15206. By Alexander Brock <email address hidden>
-
Preserve clips when unlinking clones
| Alexander Brock (brock-alexander) wrote : | # |
The current behaviour for "Edit->
- 15207. By Alexander Brock <email address hidden>
-
Make ObjectSet::unlink() work for clones which are both clipped and masked
| Alexander Brock (brock-alexander) wrote : | # |
I changed the ObjectSet::unlink function so it also works if a clone is both clipped and masked. Also I removed that part of the code from the ObjectSet:
- 15208. By Alexander Brock <email address hidden>
-
Remove duplicate code from unlinkRecursive() and fix issue with clones of groups
| Alexander Brock (brock-alexander) wrote : | # |
I removed the duplicated code for handling objects clipped by clones from ObjectSet:
- 15209. By Alexander Brock <email address hidden>
-
Fix selection issue with ObjectSet:
:unlinkRecursiv e() and add tests for it.
Preview Diff
| 1 | === modified file 'src/menus-skeleton.h' |
| 2 | --- src/menus-skeleton.h 2016-04-15 14:39:49 +0000 |
| 3 | +++ src/menus-skeleton.h 2016-11-06 21:37:32 +0000 |
| 4 | @@ -66,6 +66,7 @@ |
| 5 | " <verb verb-id=\"EditClone\" />\n" |
| 6 | " <verb verb-id=\"DialogClonetiler\" />\n" |
| 7 | " <verb verb-id=\"EditUnlinkClone\" />\n" |
| 8 | +" <verb verb-id=\"EditUnlinkCloneRecursive\" />\n" |
| 9 | " <verb verb-id=\"EditRelinkClone\" />\n" |
| 10 | " <verb verb-id=\"EditCloneSelectOriginal\" />\n" |
| 11 | " <verb verb-id=\"EditCloneOriginalPathLPE\" />\n" |
| 12 | |
| 13 | === modified file 'src/object-set.h' |
| 14 | --- src/object-set.h 2016-11-06 15:33:01 +0000 |
| 15 | +++ src/object-set.h 2016-11-06 21:37:32 +0000 |
| 16 | @@ -330,7 +330,19 @@ |
| 17 | void deleteItems(); |
| 18 | void duplicate(bool suppressDone = false, bool duplicateLayer = false); |
| 19 | void clone(); |
| 20 | - void unlink(); |
| 21 | + |
| 22 | + /** |
| 23 | + * @brief Unlink all directly selected clones. |
| 24 | + * @param skip_undo If this is set to true the call to DocumentUndo::done is omitted. |
| 25 | + * @return True if anything was unlinked, otherwise false. |
| 26 | + */ |
| 27 | + bool unlink(const bool skip_undo = false); |
| 28 | + /** |
| 29 | + * @brief Recursively unlink any clones present in the current selection, |
| 30 | + * including clones which are used to clip other objects, groups of clones etc. |
| 31 | + * @return true if anything was unlinked, otherwise false. |
| 32 | + */ |
| 33 | + bool unlinkRecursive(const bool skip_undo = false); |
| 34 | void relink(); |
| 35 | void cloneOriginal(); |
| 36 | void cloneOriginalPathLPE(); |
| 37 | @@ -376,7 +388,7 @@ |
| 38 | void createBitmapCopy(); |
| 39 | void setMask(bool apply_clip_path, bool apply_to_layer = false, bool skip_undo = false); |
| 40 | void editMask(bool clip); |
| 41 | - void unsetMask(bool apply_clip_path); |
| 42 | + void unsetMask(const bool apply_clip_path, const bool skip_undo = false); |
| 43 | void setClipGroup(); |
| 44 | |
| 45 | // moves |
| 46 | |
| 47 | === modified file 'src/selection-chemistry.cpp' |
| 48 | --- src/selection-chemistry.cpp 2016-11-06 15:33:01 +0000 |
| 49 | +++ src/selection-chemistry.cpp 2016-11-06 21:37:32 +0000 |
| 50 | @@ -2653,12 +2653,12 @@ |
| 51 | } |
| 52 | |
| 53 | |
| 54 | -void ObjectSet::unlink() |
| 55 | +bool ObjectSet::unlink(const bool skip_undo) |
| 56 | { |
| 57 | if (isEmpty()) { |
| 58 | if(desktop()) |
| 59 | desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>clones</b> to unlink.")); |
| 60 | - return; |
| 61 | + return false; |
| 62 | } |
| 63 | |
| 64 | // Get a copy of current selection. |
| 65 | @@ -2669,41 +2669,59 @@ |
| 66 | for (auto i=items_.rbegin();i!=items_.rend();++i){ |
| 67 | SPItem *item = *i; |
| 68 | |
| 69 | - if (dynamic_cast<SPText *>(item)) { |
| 70 | - SPObject *tspan = sp_tref_convert_to_tspan(item); |
| 71 | - |
| 72 | - if (tspan) { |
| 73 | - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); |
| 74 | + ObjectSet tmp_set(document()); |
| 75 | + tmp_set.set(item); |
| 76 | + Inkscape::URIReference *clip = item->clip_ref; |
| 77 | + Inkscape::URIReference *mask = item->mask_ref; |
| 78 | + if ((NULL != clip) && (NULL != clip->getObject())) { |
| 79 | + tmp_set.unsetMask(true,true); |
| 80 | + unlinked = tmp_set.unlink(true) || unlinked; |
| 81 | + tmp_set.setMask(true,false,true); |
| 82 | + new_select.push_back(tmp_set.singleItem()); |
| 83 | + } |
| 84 | + else if ((NULL != mask) && (NULL != mask->getObject())) { |
| 85 | + tmp_set.unsetMask(false,true); |
| 86 | + unlinked = tmp_set.unlink(true) || unlinked; |
| 87 | + tmp_set.setMask(false,false,true); |
| 88 | + new_select.push_back(tmp_set.singleItem()); |
| 89 | + } |
| 90 | + else { |
| 91 | + if (dynamic_cast<SPText *>(item)) { |
| 92 | + SPObject *tspan = sp_tref_convert_to_tspan(item); |
| 93 | + |
| 94 | + if (tspan) { |
| 95 | + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); |
| 96 | + } |
| 97 | + |
| 98 | + // Set unlink to true, and fall into the next if which |
| 99 | + // will include this text item in the new selection |
| 100 | + unlinked = true; |
| 101 | } |
| 102 | |
| 103 | - // Set unlink to true, and fall into the next if which |
| 104 | - // will include this text item in the new selection |
| 105 | - unlinked = true; |
| 106 | - } |
| 107 | - |
| 108 | - if (!(dynamic_cast<SPUse *>(item) || dynamic_cast<SPTRef *>(item))) { |
| 109 | - // keep the non-use item in the new selection |
| 110 | - new_select.push_back(item); |
| 111 | - continue; |
| 112 | - } |
| 113 | - |
| 114 | - SPItem *unlink = NULL; |
| 115 | - SPUse *use = dynamic_cast<SPUse *>(item); |
| 116 | - if (use) { |
| 117 | - unlink = use->unlink(); |
| 118 | - // Unable to unlink use (external or invalid href?) |
| 119 | - if (!unlink) { |
| 120 | + if (!(dynamic_cast<SPUse *>(item) || dynamic_cast<SPTRef *>(item))) { |
| 121 | + // keep the non-use item in the new selection |
| 122 | new_select.push_back(item); |
| 123 | continue; |
| 124 | } |
| 125 | - } else /*if (SP_IS_TREF(use))*/ { |
| 126 | - unlink = dynamic_cast<SPItem *>(sp_tref_convert_to_tspan(item)); |
| 127 | - g_assert(unlink != NULL); |
| 128 | + |
| 129 | + SPItem *unlink = NULL; |
| 130 | + SPUse *use = dynamic_cast<SPUse *>(item); |
| 131 | + if (use) { |
| 132 | + unlink = use->unlink(); |
| 133 | + // Unable to unlink use (external or invalid href?) |
| 134 | + if (!unlink) { |
| 135 | + new_select.push_back(item); |
| 136 | + continue; |
| 137 | + } |
| 138 | + } else /*if (SP_IS_TREF(use))*/ { |
| 139 | + unlink = dynamic_cast<SPItem *>(sp_tref_convert_to_tspan(item)); |
| 140 | + g_assert(unlink != NULL); |
| 141 | + } |
| 142 | + |
| 143 | + unlinked = true; |
| 144 | + // Add ungrouped items to the new selection. |
| 145 | + new_select.push_back(unlink); |
| 146 | } |
| 147 | - |
| 148 | - unlinked = true; |
| 149 | - // Add ungrouped items to the new selection. |
| 150 | - new_select.push_back(unlink); |
| 151 | } |
| 152 | |
| 153 | if (!new_select.empty()) { // set new selection |
| 154 | @@ -2715,8 +2733,42 @@ |
| 155 | desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to unlink</b> in the selection.")); |
| 156 | } |
| 157 | |
| 158 | - DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE, |
| 159 | - _("Unlink clone")); |
| 160 | + if (!skip_undo) { |
| 161 | + DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE, |
| 162 | + _("Unlink clone")); |
| 163 | + } |
| 164 | + return unlinked; |
| 165 | +} |
| 166 | + |
| 167 | +bool ObjectSet::unlinkRecursive(const bool skip_undo) { |
| 168 | + if (isEmpty()){ |
| 169 | + if (desktop()) |
| 170 | + desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>clones</b> to unlink.")); |
| 171 | + return false; |
| 172 | + } |
| 173 | + bool unlinked = false; |
| 174 | + ObjectSet tmp_set(document()); |
| 175 | + std::vector<SPItem*> items_(items().begin(), items().end()); |
| 176 | + for (auto& it:items_) { |
| 177 | + tmp_set.set(it); |
| 178 | + unlinked = tmp_set.unlink(true) || unlinked; |
| 179 | + it = tmp_set.singleItem(); |
| 180 | + if (SP_IS_GROUP(it)) { |
| 181 | + std::vector<SPObject*> c = it->childList(false); |
| 182 | + tmp_set.setList(c); |
| 183 | + unlinked = tmp_set.unlinkRecursive(true) || unlinked; |
| 184 | + } |
| 185 | + } |
| 186 | + if (!unlinked) { |
| 187 | + if(desktop()) |
| 188 | + desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to unlink</b> in the selection.")); |
| 189 | + } |
| 190 | + if (!skip_undo) { |
| 191 | + DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, |
| 192 | + _("Unlink clone recursively")); |
| 193 | + } |
| 194 | + setList(items_); |
| 195 | + return unlinked; |
| 196 | } |
| 197 | |
| 198 | void ObjectSet::cloneOriginal() |
| 199 | @@ -3960,7 +4012,7 @@ |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | -void ObjectSet::unsetMask(bool apply_clip_path) { |
| 204 | +void ObjectSet::unsetMask(const bool apply_clip_path, const bool skip_undo) { |
| 205 | SPDocument *doc = document(); |
| 206 | Inkscape::XML::Document *xml_doc = doc->getReprDoc(); |
| 207 | |
| 208 | @@ -4082,10 +4134,12 @@ |
| 209 | // rebuild selection |
| 210 | addList(items_to_select); |
| 211 | |
| 212 | - if (apply_clip_path) { |
| 213 | - DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); |
| 214 | - } else { |
| 215 | - DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); |
| 216 | + if (!skip_undo) { |
| 217 | + if (apply_clip_path) { |
| 218 | + DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); |
| 219 | + } else { |
| 220 | + DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); |
| 221 | + } |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | |
| 226 | === modified file 'src/verbs.cpp' |
| 227 | --- src/verbs.cpp 2016-11-06 15:33:01 +0000 |
| 228 | +++ src/verbs.cpp 2016-11-06 21:37:32 +0000 |
| 229 | @@ -1009,6 +1009,9 @@ |
| 230 | case SP_VERB_EDIT_UNLINK_CLONE: |
| 231 | dt->selection->unlink(); |
| 232 | break; |
| 233 | + case SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE: |
| 234 | + dt->selection->unlinkRecursive(); |
| 235 | + break; |
| 236 | case SP_VERB_EDIT_RELINK_CLONE: |
| 237 | dt->selection->relink(); |
| 238 | break; |
| 239 | @@ -2530,6 +2533,8 @@ |
| 240 | N_("Create a clone (a copy linked to the original) of selected object"), INKSCAPE_ICON("edit-clone")), |
| 241 | new EditVerb(SP_VERB_EDIT_UNLINK_CLONE, "EditUnlinkClone", N_("Unlin_k Clone"), |
| 242 | N_("Cut the selected clones' links to the originals, turning them into standalone objects"), INKSCAPE_ICON("edit-clone-unlink")), |
| 243 | + new EditVerb(SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, "EditUnlinkCloneRecursive", N_("Unlink Clones _recursively"), |
| 244 | + N_("Unlink all clones in the selection, even if they are in groups."), INKSCAPE_ICON("edit-clone-unlink")), |
| 245 | new EditVerb(SP_VERB_EDIT_RELINK_CLONE, "EditRelinkClone", N_("Relink to Copied"), |
| 246 | N_("Relink the selected clones to the object currently on the clipboard"), NULL), |
| 247 | new EditVerb(SP_VERB_EDIT_CLONE_SELECT_ORIGINAL, "EditCloneSelectOriginal", N_("Select _Original"), |
| 248 | |
| 249 | === modified file 'src/verbs.h' |
| 250 | --- src/verbs.h 2016-10-08 06:16:53 +0000 |
| 251 | +++ src/verbs.h 2016-11-06 21:37:32 +0000 |
| 252 | @@ -83,6 +83,7 @@ |
| 253 | SP_VERB_EDIT_DUPLICATE, |
| 254 | SP_VERB_EDIT_CLONE, |
| 255 | SP_VERB_EDIT_UNLINK_CLONE, |
| 256 | + SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, |
| 257 | SP_VERB_EDIT_RELINK_CLONE, |
| 258 | SP_VERB_EDIT_CLONE_SELECT_ORIGINAL, |
| 259 | SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, |
| 260 | |
| 261 | === modified file 'testfiles/src/object-set-test.cpp' |
| 262 | --- testfiles/src/object-set-test.cpp 2016-11-02 23:08:41 +0000 |
| 263 | +++ testfiles/src/object-set-test.cpp 2016-11-06 21:37:32 +0000 |
| 264 | @@ -105,6 +105,25 @@ |
| 265 | ObjectSet* set2; |
| 266 | }; |
| 267 | |
| 268 | +#define SP_IS_CLONE(obj) (dynamic_cast<const SPUse*>(obj) != NULL) |
| 269 | + |
| 270 | +bool containsClone(ObjectSet* set) { |
| 271 | + for (auto it : set->items()) { |
| 272 | + if (SP_IS_CLONE(it)) { |
| 273 | + return true; |
| 274 | + } |
| 275 | + if (SP_IS_GROUP(it)) { |
| 276 | + ObjectSet tmp_set(set->document()); |
| 277 | + std::vector<SPObject*> c = it->childList(false); |
| 278 | + tmp_set.setList(c); |
| 279 | + if (containsClone(&tmp_set)) { |
| 280 | + return true; |
| 281 | + } |
| 282 | + } |
| 283 | + } |
| 284 | + return false; |
| 285 | +} |
| 286 | + |
| 287 | TEST_F(ObjectSetTest, Basics) { |
| 288 | EXPECT_EQ(0, set->size()); |
| 289 | set->add(A); |
| 290 | @@ -429,6 +448,125 @@ |
| 291 | SetUpTestCase(); |
| 292 | } |
| 293 | |
| 294 | +TEST_F(ObjectSetTest, unlinkRecursiveBasic) { |
| 295 | + // This is the same as the test (ObjectSetTest, Ops), but with unlinkRecursive instead of unlink. |
| 296 | + set->set(r1.get()); |
| 297 | + set->add(r2.get()); |
| 298 | + set->add(r3.get()); |
| 299 | + EXPECT_FALSE(containsClone(set)); |
| 300 | + set->duplicate(); |
| 301 | + EXPECT_FALSE(containsClone(set)); |
| 302 | + EXPECT_EQ(9, _doc->getRoot()->children.size());//metadata, defs, namedview, and those 3x2 rects. |
| 303 | + EXPECT_EQ(3, set->size()); |
| 304 | + EXPECT_FALSE(set->includes(r1.get())); |
| 305 | + set->deleteItems(); |
| 306 | + EXPECT_FALSE(containsClone(set)); |
| 307 | + EXPECT_TRUE(set->isEmpty()); |
| 308 | + set->add(r1.get()); |
| 309 | + set->add(r2.get()); |
| 310 | + set->add(r3.get()); |
| 311 | + EXPECT_FALSE(containsClone(set)); |
| 312 | + set->group();//r1-3 are now invalid (grouping makes copies) |
| 313 | + r1.release(); |
| 314 | + r2.release(); |
| 315 | + r3.release(); |
| 316 | + EXPECT_FALSE(containsClone(set)); |
| 317 | + EXPECT_EQ(4, _doc->getRoot()->children.size()); |
| 318 | + EXPECT_EQ(1, set->size()); |
| 319 | + set->ungroup(); |
| 320 | + EXPECT_FALSE(containsClone(set)); |
| 321 | + EXPECT_EQ(6, _doc->getRoot()->children.size()); |
| 322 | + EXPECT_EQ(3, set->size()); |
| 323 | + /* Uncomment this when toNextLayer is made desktop-independent |
| 324 | + set->group(); |
| 325 | + set2->add(set->singleItem()->childList(false)[0]); |
| 326 | + EXPECT_EQ(3, set->singleItem()->children.size()); |
| 327 | + EXPECT_EQ(4, _doc->getRoot()->children.size()); |
| 328 | + set2->popFromGroup(); |
| 329 | + EXPECT_EQ(2, set->singleItem()->children.size()); |
| 330 | + EXPECT_EQ(5, _doc->getRoot()->children.size()); |
| 331 | + set->ungroup(); |
| 332 | + set->add(set2->singleItem()); |
| 333 | + */ |
| 334 | + set->clone(); |
| 335 | + EXPECT_TRUE(containsClone(set)); |
| 336 | + EXPECT_EQ(9, _doc->getRoot()->children.size()); |
| 337 | + EXPECT_EQ(3, set->size()); |
| 338 | + EXPECT_NE(nullptr, dynamic_cast<SPUse*>(*(set->items().begin()))); |
| 339 | + EXPECT_EQ(nullptr, dynamic_cast<SPRect*>(*(set->items().begin()))); |
| 340 | + set->unlinkRecursive(); |
| 341 | + EXPECT_FALSE(containsClone(set)); |
| 342 | + EXPECT_EQ(9, _doc->getRoot()->children.size()); |
| 343 | + EXPECT_EQ(3, set->size()); |
| 344 | + EXPECT_EQ(nullptr, dynamic_cast<SPUse*>(*(set->items().begin()))); |
| 345 | + EXPECT_NE(nullptr, dynamic_cast<SPRect*>(*(set->items().begin()))); |
| 346 | + set->clone(); //creates 3 clones |
| 347 | + EXPECT_TRUE(containsClone(set)); |
| 348 | + set->clone(); //creates 3 clones of clones |
| 349 | + EXPECT_TRUE(containsClone(set)); |
| 350 | + EXPECT_EQ(15, _doc->getRoot()->children.size()); |
| 351 | + EXPECT_EQ(3, set->size()); |
| 352 | + EXPECT_NE(nullptr, dynamic_cast<SPUse*>( ((SPUse*)(*(set->items().begin())))->get_original()));//"original is a Use" |
| 353 | + set->unlinkRecursive(); //clone of clone of rect -> rect |
| 354 | + EXPECT_FALSE(containsClone(set)); |
| 355 | + EXPECT_EQ(nullptr, dynamic_cast<SPUse*>(*(set->items().begin()))); |
| 356 | + EXPECT_NE(nullptr, dynamic_cast<SPRect*>(*(set->items().begin()))); |
| 357 | + set->clone(); |
| 358 | + EXPECT_TRUE(containsClone(set)); |
| 359 | + set->set(*(set->items().begin())); |
| 360 | + set->cloneOriginal();//get clone original |
| 361 | + EXPECT_EQ(18, _doc->getRoot()->children.size()); |
| 362 | + EXPECT_EQ(1, set->size()); |
| 363 | + EXPECT_NE(nullptr, dynamic_cast<SPRect*>(*(set->items().begin()))); |
| 364 | + TearDownTestCase(); |
| 365 | + SetUpTestCase(); |
| 366 | +} |
| 367 | + |
| 368 | +TEST_F(ObjectSetTest, unlinkRecursiveAdvanced) { |
| 369 | + set->set(r1.get()); |
| 370 | + set->add(r2.get()); |
| 371 | + set->add(r3.get()); |
| 372 | + set->group();//r1-3 are now invalid (grouping makes copies) |
| 373 | + r1.release(); |
| 374 | + r2.release(); |
| 375 | + r3.release(); |
| 376 | + EXPECT_FALSE(containsClone(set)); |
| 377 | + EXPECT_EQ(1, set->size()); |
| 378 | + SPItem* original = set->singleItem(); |
| 379 | + set->clone(); |
| 380 | + EXPECT_TRUE(containsClone(set)); |
| 381 | + EXPECT_EQ(1, set->size()); |
| 382 | + set->add(original); |
| 383 | + EXPECT_TRUE(containsClone(set)); |
| 384 | + EXPECT_EQ(2, set->size()); |
| 385 | + set->group(); |
| 386 | + EXPECT_TRUE(containsClone(set)); |
| 387 | + EXPECT_EQ(1, set->size()); |
| 388 | + original = set->singleItem(); |
| 389 | + set->clone(); |
| 390 | + EXPECT_TRUE(containsClone(set)); |
| 391 | + EXPECT_EQ(1, set->size()); |
| 392 | + set->add(original); |
| 393 | + EXPECT_TRUE(containsClone(set)); |
| 394 | + EXPECT_EQ(2, set->size()); |
| 395 | + set->group(); |
| 396 | + EXPECT_TRUE(containsClone(set)); |
| 397 | + EXPECT_EQ(1, set->size()); |
| 398 | + original = set->singleItem(); |
| 399 | + set->clone(); |
| 400 | + EXPECT_TRUE(containsClone(set)); |
| 401 | + EXPECT_EQ(1, set->size()); |
| 402 | + set->add(original); |
| 403 | + EXPECT_TRUE(containsClone(set)); |
| 404 | + EXPECT_EQ(2, set->size()); |
| 405 | + set->unlinkRecursive(); |
| 406 | + EXPECT_FALSE(containsClone(set)); |
| 407 | + EXPECT_EQ(2, set->size()); |
| 408 | + |
| 409 | + TearDownTestCase(); |
| 410 | + SetUpTestCase(); |
| 411 | +} |
| 412 | + |
| 413 | TEST_F(ObjectSetTest, ZOrder) { |
| 414 | //sp_object_compare_position_bool == true iff "r1<r2" iff r1 is "before" r2 in the file, ie r1 is lower than r2 |
| 415 | EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get())); |

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.