From 3649ef4c620b9eae5132d89a39ebd280d9dc50a8 Mon Sep 17 00:00:00 2001 From: Alexander Brock Date: Thu, 27 Oct 2016 19:13:05 +0200 Subject: Add recursive clone unlink feature (bzr r15191.1.1) --- src/menus-skeleton.h | 1 + src/object-set.h | 16 ++- src/selection-chemistry.cpp | 276 ++++++++++++++++++++++++++++---------------- src/verbs.cpp | 5 + src/verbs.h | 1 + 5 files changed, 199 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index 9c7c65140..75302806b 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -66,6 +66,7 @@ static char const menus_skeleton[] = " \n" " \n" " \n" +" \n" " \n" " \n" " \n" diff --git a/src/object-set.h b/src/object-set.h index 8fa20e454..35fdb431a 100644 --- a/src/object-set.h +++ b/src/object-set.h @@ -329,7 +329,19 @@ public: void deleteItems(); void duplicate(bool suppressDone = false, bool duplicateLayer = false); void clone(); - void unlink(); + + /** + * @brief Unlink all directly selected clones. + * @param skip_undo If this is set to true the call to DocumentUndo::done is omitted. + * @return True if anything was unlinked, otherwise false. + */ + bool unlink(bool skip_undo = false); + /** + * @brief Recursively unlink any clones present in the current selection, + * including clones which are used to clip other objects, groups of clones etc. + * @return true if anything was unlinked, otherwise false. + */ + bool unlinkRecursive(); void relink(); void cloneOriginal(); void cloneOriginalPathLPE(); @@ -375,7 +387,7 @@ public: void createBitmapCopy(); void setMask(bool apply_clip_path, bool apply_to_layer = false, bool skip_undo = false); void editMask(bool clip); - void unsetMask(bool apply_clip_path); + void unsetMask(const bool apply_clip_path, const bool skip_undo = false); void setClipGroup(); // moves diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 2f9e72e2c..b4654909a 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -124,8 +124,8 @@ selection_display_message(SPDesktop *desktop, Inkscape::MessageType msgType, Gli desktop->messageStack()->flash(msgType, msg); } else { if (msgType == Inkscape::IMMEDIATE_MESSAGE || - msgType == Inkscape::WARNING_MESSAGE || - msgType == Inkscape::ERROR_MESSAGE) { + msgType == Inkscape::WARNING_MESSAGE || + msgType == Inkscape::ERROR_MESSAGE) { g_printerr("%s\n", msg.c_str()); } } @@ -273,8 +273,8 @@ void SelectionHelper::fixSelection(SPDesktop *dt) for(auto i = boost::rbegin(selList); i != boost::rend(selList); ++i) { SPItem *item = *i; if( item && - !dt->isLayer(item) && - (!item->isLocked())) + !dt->isLayer(item) && + (!item->isLocked())) { items.push_back(item); } @@ -378,19 +378,19 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro void ObjectSet::deleteItems() { if(desktop() && tools_isactive(desktop(), TOOLS_TEXT)){ - if (Inkscape::UI::Tools::sp_text_delete_selection(desktop()->event_context)) { + if (Inkscape::UI::Tools::sp_text_delete_selection(desktop()->event_context)) { DocumentUndo::done(desktop()->getDocument(), SP_VERB_CONTEXT_TEXT, _("Delete text")); return; - } + } } - if (isEmpty()) { - selection_display_message(desktop(),Inkscape::WARNING_MESSAGE, _("Nothing was deleted.")); + if (isEmpty()) { + selection_display_message(desktop(),Inkscape::WARNING_MESSAGE, _("Nothing was deleted.")); return; } std::vector selected(items().begin(), items().end()); - clear(); + clear(); sp_selection_delete_impl(selected); if(SPDesktop *d = desktop()){ d->currentLayer()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); @@ -404,7 +404,7 @@ void ObjectSet::deleteItems() tools_switch( d, tools_active( d ) ); } if(document()) - DocumentUndo::done(document(), SP_VERB_EDIT_DELETE, + DocumentUndo::done(document(), SP_VERB_EDIT_DELETE, _("Delete")); } @@ -430,7 +430,7 @@ void ObjectSet::duplicate(bool suppressDone, bool duplicateLayer) //TODO: understand why layer management is tied to desktop and not to document. return; } - + SPDocument *doc = document(); if(!doc) @@ -586,11 +586,11 @@ std::vector &get_all_items(std::vector &list, SPObject *from, for (auto& child: from->children) { SPItem *item = dynamic_cast(&child); if (item && - !desktop->isLayer(item) && - (!onlysensitive || !item->isLocked()) && - (!onlyvisible || !desktop->itemIsHidden(item)) && - (exclude.empty() || exclude.end() == std::find(exclude.begin(), exclude.end(), &child)) - ) + !desktop->isLayer(item) && + (!onlysensitive || !item->isLocked()) && + (!onlyvisible || !desktop->itemIsHidden(item)) && + (exclude.empty() || exclude.end() == std::find(exclude.begin(), exclude.end(), &child)) + ) { list.insert(list.begin(),item); } @@ -628,10 +628,10 @@ static void sp_edit_select_all_full(SPDesktop *dt, bool force_all_layers, bool i inlayer = PREFS_SELECTION_ALL; switch (inlayer) { - case PREFS_SELECTION_LAYER: { + case PREFS_SELECTION_LAYER: { if ( (onlysensitive && dynamic_cast(dt->currentLayer())->isLocked()) || (onlyvisible && dt->itemIsHidden(dynamic_cast(dt->currentLayer()))) ) - return; + return; std::vector all_items = sp_item_group_item_list(dynamic_cast(dt->currentLayer())); @@ -649,17 +649,17 @@ static void sp_edit_select_all_full(SPDesktop *dt, bool force_all_layers, bool i } } - break; - } - case PREFS_SELECTION_LAYER_RECURSIVE: { - std::vector x; - items = get_all_items(x, dt->currentLayer(), dt, onlyvisible, onlysensitive, FALSE, exclude); - break; - } - default: { - std::vector x; - items = get_all_items(x, dt->currentRoot(), dt, onlyvisible, onlysensitive, FALSE, exclude); - break; + break; + } + case PREFS_SELECTION_LAYER_RECURSIVE: { + std::vector x; + items = get_all_items(x, dt->currentLayer(), dt, onlyvisible, onlysensitive, FALSE, exclude); + break; + } + default: { + std::vector x; + items = get_all_items(x, dt->currentRoot(), dt, onlyvisible, onlysensitive, FALSE, exclude); + break; } } @@ -787,7 +787,7 @@ void ObjectSet::popFromGroup(){ } auto item = items().begin(); // leaving this because it will be useful for - // future implementation of complex pop ungrouping + // future implementation of complex pop ungrouping SPItem *obj = *item; SPItem *parent_group = static_cast(obj->parent); if (!SP_IS_GROUP(parent_group) || SP_IS_LAYER(parent_group)) { @@ -806,7 +806,7 @@ void ObjectSet::popFromGroup(){ if(document()) DocumentUndo::done(document(), SP_VERB_SELECTION_UNGROUP_POP_SELECTION, - _("Pop selection from group")); + _("Pop selection from group")); } @@ -883,7 +883,7 @@ void ObjectSet::ungroup() ungroup_impl(this); if(document()) DocumentUndo::done(document(), SP_VERB_SELECTION_UNGROUP, - _("Ungroup")); + _("Ungroup")); } // TODO replace it with ObjectSet::degroup_list @@ -964,7 +964,7 @@ static SPObject *prev_sibling(SPObject *child) bool sp_item_repr_compare_position_bool(SPObject const *first, SPObject const *second) { return sp_repr_compare_position(((SPItem*)first)->getRepr(), - ((SPItem*)second)->getRepr())<0; + ((SPItem*)second)->getRepr())<0; } void ObjectSet::raise(bool skip_undo){ @@ -1015,8 +1015,8 @@ void ObjectSet::raise(bool skip_undo){ } if(document() && !skip_undo) DocumentUndo::done(document(), SP_VERB_SELECTION_RAISE, - //TRANSLATORS: "Raise" means "to raise an object" in the undo history - C_("Undo action", "Raise")); + //TRANSLATORS: "Raise" means "to raise an object" in the undo history + C_("Undo action", "Raise")); } @@ -1042,7 +1042,7 @@ void ObjectSet::raiseToTop(bool skip_undo) { } if (document() && !skip_undo) { DocumentUndo::done(document(), SP_VERB_SELECTION_TO_FRONT, - _("Raise to top")); + _("Raise to top")); } } @@ -1096,8 +1096,8 @@ void ObjectSet::lower(bool skip_undo){ } if(document() && !skip_undo) DocumentUndo::done(document(), SP_VERB_SELECTION_LOWER, - //TRANSLATORS: "Lower" means "to lower an object" in the undo history - C_("Undo action", "Lower")); + //TRANSLATORS: "Lower" means "to lower an object" in the undo history + C_("Undo action", "Lower")); } @@ -1183,7 +1183,7 @@ take_style_from_item(SPObject *object) return NULL; if ((dynamic_cast(object) && object->firstChild()) || - (dynamic_cast(object) && object->firstChild() && object->firstChild()->getNext() == NULL)) { + (dynamic_cast(object) && object->firstChild() && object->firstChild()->getNext() == NULL)) { // if this is a text with exactly one tspan child, merge the style of that tspan as well // If this is a group, merge the style of its topmost (last) child with style auto list = object->children | boost::adaptors::reversed; @@ -1268,7 +1268,7 @@ void ObjectSet::removeLPE() // check if something is selected if (isEmpty()) { if(desktop()) - desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to remove live path effects from.")); + desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to remove live path effects from.")); return; } auto list= items(); @@ -1281,7 +1281,7 @@ void ObjectSet::removeLPE() if(document()) DocumentUndo::done(document(), SP_VERB_EDIT_REMOVE_LIVEPATHEFFECT, - _("Remove live path effect")); + _("Remove live path effect")); } void ObjectSet::removeFilter() @@ -1300,7 +1300,7 @@ void ObjectSet::removeFilter() sp_repr_css_attr_unref(css); if(document()) DocumentUndo::done(document(), SP_VERB_EDIT_REMOVE_FILTER, - _("Remove filter")); + _("Remove filter")); } @@ -1334,8 +1334,8 @@ void sp_selection_change_layer_maintain_clones(std::vector const &items SPItem *oldparent = dynamic_cast(item->parent); SPItem *newparent = dynamic_cast(where); sp_item_group_ungroup_handle_clones(item, - (oldparent->i2doc_affine()) - *((newparent->i2doc_affine()).inverse())); + (oldparent->i2doc_affine()) + *((newparent->i2doc_affine()).inverse())); } } } @@ -1345,7 +1345,7 @@ void ObjectSet::toNextLayer(bool skip_undo) if(!desktop()) return; SPDesktop *dt=desktop(); //TODO make it desktop-independent - + // check if something is selected if (isEmpty()) { dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to move to the layer above.")); @@ -1515,7 +1515,7 @@ then simply updates the repr from item->transform. */ void ObjectSet::applyAffine(Geom::Affine const &affine, bool set_i2d, bool compensate, - bool adjust_transf_center) + bool adjust_transf_center) { if (isEmpty()) return; @@ -1559,7 +1559,7 @@ void ObjectSet::applyAffine(Geom::Affine const &affine, bool set_i2d, bool compe old_center = item->getCenter(); #if 0 /* Re-enable this once persistent guides have a graphical indication. - At the time of writing, this is the only place to re-enable. */ + At the time of writing, this is the only place to re-enable. */ sp_item_update_cns(*item, desktop()); #endif @@ -1716,7 +1716,7 @@ void ObjectSet::removeTransform() if(document()) DocumentUndo::done(document(), SP_VERB_OBJECT_FLATTEN, - _("Remove transform")); + _("Remove transform")); } void ObjectSet::setScaleAbsolute(double x0, double x1,double y0, double y1) @@ -1955,20 +1955,20 @@ std::vector sp_get_same_fill_or_stroke_color(SPItem *sel, std::vectorstyle->fill) : &(iter->style->stroke); match = false; if (sel_paint->isColor() && iter_paint->isColor() // color == color comparision doesnt seem to work here. - && (sel_paint->value.color.toRGBA32(1.0) == iter_paint->value.color.toRGBA32(1.0))) { + && (sel_paint->value.color.toRGBA32(1.0) == iter_paint->value.color.toRGBA32(1.0))) { match = true; } else if (sel_paint->isPaintserver() && iter_paint->isPaintserver()) { SPPaintServer *sel_server = - (type == SP_FILL_COLOR) ? sel->style->getFillPaintServer() : sel->style->getStrokePaintServer(); + (type == SP_FILL_COLOR) ? sel->style->getFillPaintServer() : sel->style->getStrokePaintServer(); SPPaintServer *iter_server = - (type == SP_FILL_COLOR) ? iter->style->getFillPaintServer() : iter->style->getStrokePaintServer(); + (type == SP_FILL_COLOR) ? iter->style->getFillPaintServer() : iter->style->getStrokePaintServer(); if ((dynamic_cast(sel_server) || dynamic_cast(sel_server) || (dynamic_cast(sel_server) && dynamic_cast(sel_server)->getVector()->isSwatch())) - && - (dynamic_cast(iter_server) || dynamic_cast(iter_server) || - (dynamic_cast(iter_server) && dynamic_cast(iter_server)->getVector()->isSwatch()))) { + && + (dynamic_cast(iter_server) || dynamic_cast(iter_server) || + (dynamic_cast(iter_server) && dynamic_cast(iter_server)->getVector()->isSwatch()))) { SPGradient *sel_vector = dynamic_cast(sel_server)->getVector(); SPGradient *iter_vector = dynamic_cast(iter_server)->getVector(); if (sel_vector == iter_vector) { @@ -2119,7 +2119,7 @@ std::vector sp_get_same_style(SPItem *sel, std::vector &src, S for (int i = 0; i < len; i++) { match = (sel_style->marker_ptrs[i]->set == iter_style->marker_ptrs[i]->set); if (sel_style->marker_ptrs[i]->set && iter_style->marker_ptrs[i]->set && - (strcmp(sel_style->marker_ptrs[i]->value, iter_style->marker_ptrs[i]->value))) { + (strcmp(sel_style->marker_ptrs[i]->value, iter_style->marker_ptrs[i]->value))) { match = false; break; } @@ -2652,13 +2652,88 @@ void ObjectSet::relink() } } +bool ObjectSet::unlinkRecursive() { + bool unlinked = false; + // Main part when called from the GUI. + ObjectSet tmp_set(document()); + if (desktop() || size() > 1) { + if (isEmpty()) { + if (desktop()) { + desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select clones to unlink.")); + } + return false; + } + + std::vector items_(items().begin(), items().end()); + for (auto& it : items_) { + tmp_set.set(it); + if (tmp_set.unlinkRecursive()) { + unlinked = true; + } + it = tmp_set.singleItem(); + assert(it); + } + + if (!unlinked && desktop()) { + desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No clones to unlink in the selection.")); + } + + DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE, + _("Unlink resursively clone")); + return unlinked; + } + // Part when called recursively + assert(NULL == desktop()); + assert (1 == size()); + if (unlink(true)) { + unlinked = true; + } + SPItem* item = singleItem(); + // Check if a clip is set, in that case unset the clip, unlink all clones and set the clip again. + Inkscape::URIReference *clip = item->clip_ref; + if ((NULL != clip) && (NULL != clip->getObject())) { + tmp_set.set(item); + tmp_set.unsetMask(true, true); + std::vector items(tmp_set.items().begin(), tmp_set.items().end()); + std::vector new_items; + for (SPItem * it : items) { + tmp_set.set(it); + if (tmp_set.unlinkRecursive()) { + unlinked = true; + } + it = tmp_set.singleItem(); + assert(it); + new_items.push_back(it); + } + tmp_set.setList(new_items); + tmp_set.setMask(true, false, true); + item = tmp_set.singleItem(); + g_assert(item); + } + + if (SP_IS_GROUP(item)) { + for (SPObject *o = item->firstChild(); o != NULL; ) { + SPObject* next = o->getNext(); + SPItem * child_item = dynamic_cast(o); + if (child_item) { + tmp_set.set(child_item); + if(tmp_set.unlinkRecursive()) { + unlinked = true; + } + } + o = next; + } + } + + return unlinked; +} -void ObjectSet::unlink() +bool ObjectSet::unlink(bool skip_undo) { if (isEmpty()) { if(desktop()) desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select clones to unlink.")); - return; + return false; } // Get a copy of current selection. @@ -2715,8 +2790,11 @@ void ObjectSet::unlink() desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No clones to unlink in the selection.")); } - DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE, - _("Unlink clone")); + if (!skip_undo) { + DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE, + _("Unlink clone")); + } + return unlinked; } void ObjectSet::cloneOriginal() @@ -2876,8 +2954,8 @@ void ObjectSet::cloneOriginalPathLPE() void ObjectSet::toMarker(bool apply) { // sp_selection_tile has similar code - if (desktop() == NULL) { // TODO: We should not need desktop for that. - // Someone get rid of the dt2doc() call. + if (desktop() == NULL) { // TODO: We should not need desktop for that. + // Someone get rid of the dt2doc() call. return; } @@ -2901,7 +2979,7 @@ void ObjectSet::toMarker(bool apply) Geom::Point doc_height( 0, doc->getHeight().value("px")); // calculate the transform to be applied to objects to move them to 0,0 - Geom::Point corner( r->min()[Geom::X], r->max()[Geom::Y] ); // FIXME: Inverted Y coodinate + Geom::Point corner( r->min()[Geom::X], r->max()[Geom::Y] ); // FIXME: Inverted Y coodinate Geom::Point move_p = doc_height - corner; move_p[Geom::Y] = -move_p[Geom::Y]; Geom::Affine move = Geom::Affine(Geom::Translate(move_p)); @@ -3007,7 +3085,7 @@ void ObjectSet::toGuides() /* * Convert objects to . How that happens depends on what is selected: - * + * * 1) A random selection of objects will be embedded into a single element. * * 2) Except, a single will have its content directly embedded into a ; the 'id' and @@ -3033,9 +3111,9 @@ void ObjectSet::toSymbol() // Check if something is selected. if (isEmpty()) { - if (desktop()) - desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select objects to convert to symbol.")); - return; + if (desktop()) + desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select objects to convert to symbol.")); + return; } doc->ensureUpToDate(); @@ -3108,9 +3186,9 @@ void ObjectSet::toSymbol() // Move selected items to new for (std::vector::const_reverse_iterator i=items_.rbegin();i!=items_.rend();++i){ - Inkscape::XML::Node *repr = (*i)->getRepr(); - repr->parent()->removeChild(repr); - symbol_repr->addChild(repr,NULL); + Inkscape::XML::Node *repr = (*i)->getRepr(); + repr->parent()->removeChild(repr); + symbol_repr->addChild(repr,NULL); } if( single_group && transform.isTranslation() ) { @@ -3154,7 +3232,7 @@ void ObjectSet::unSymbol() } SPObject* symbol = single(); - + // Make sure we have only one object in selection. // Require that we really have a . if( symbol == NULL || !dynamic_cast( symbol )) { @@ -3179,14 +3257,14 @@ void ObjectSet::unSymbol() SPObject *object = children[0]; if ( dynamic_cast( object ) ) { if( object->getAttribute("style") == NULL || - object->getAttribute("class") == NULL ) { + object->getAttribute("class") == NULL ) { group->setAttribute("transform", object->getAttribute("transform")); children = object->childList(false); } } } - + for (std::vector::const_reverse_iterator i=children.rbegin();i!=children.rend();++i){ Inkscape::XML::Node *repr = (*i)->getRepr(); repr->parent()->removeChild(repr); @@ -3291,10 +3369,10 @@ void ObjectSet::tile(bool apply) prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); gchar const *pat_id = SPPattern::produce(repr_copies, bbox, doc, - ( Geom::Affine(Geom::Translate(desktop()->dt2doc(Geom::Point(r->min()[Geom::X], - r->max()[Geom::Y])))) - * parent_transform.inverse() ), - parent_transform * move); + ( Geom::Affine(Geom::Translate(desktop()->dt2doc(Geom::Point(r->min()[Geom::X], + r->max()[Geom::Y])))) + * parent_transform.inverse() ), + parent_transform * move); // restore compensation setting prefs->setInt("/options/clonecompensation/value", saved_compensation); @@ -3374,8 +3452,8 @@ void ObjectSet::untile() Inkscape::XML::Node *copy = child.getRepr()->duplicate(xml_doc); SPItem *i = dynamic_cast(item->parent->appendChildRepr(copy)); - // FIXME: relink clones to the new canvas objects - // use SPObject::setid when mental finishes it to steal ids of + // FIXME: relink clones to the new canvas objects + // use SPObject::setid when mental finishes it to steal ids of // this is needed to make sure the new item has curve (simply requestDisplayUpdate does not work) doc->ensureUpToDate(); @@ -3517,7 +3595,7 @@ void ObjectSet::createBitmapCopy() gchar *const basename = g_strdup_printf("%s-%s-%u.png", doc->getName(), items_[0]->getRepr()->attribute("id"), - current); + current); // Imagemagick is known not to handle spaces in filenames, so we replace anything but letters, // digits, and a few other chars, with "_" g_strcanon(basename, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.=+~$#@^&!?", '_'); @@ -3622,12 +3700,12 @@ void ObjectSet::createBitmapCopy() // Do the export sp_export_png_file(doc, filepath, bbox->min()[Geom::X], bbox->min()[Geom::Y], - bbox->max()[Geom::X], bbox->max()[Geom::Y], - width, height, res, res, - (guint32) 0xffffff00, - NULL, NULL, - true, /*bool force_overwrite,*/ - items_); + bbox->max()[Geom::X], bbox->max()[Geom::Y], + width, height, res, res, + (guint32) 0xffffff00, + NULL, NULL, + true, /*bool force_overwrite,*/ + items_); // Run filter, if any if (run) { @@ -3699,7 +3777,7 @@ void ObjectSet::setClipGroup() desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to create clippath or mask from.")); return; } - + std::vector p(xmlNodes().begin(), xmlNodes().end()); sort(p.begin(),p.end(),sp_repr_compare_position_bool); @@ -3794,7 +3872,7 @@ void ObjectSet::setClipGroup() * If \a apply_clip_path parameter is true, clipPath is created, otherwise mask * */ - void ObjectSet::setMask(bool apply_clip_path, bool apply_to_layer, bool skip_undo) +void ObjectSet::setMask(bool apply_clip_path, bool apply_to_layer, bool skip_undo) { if(!desktop() && apply_to_layer) return; @@ -3960,7 +4038,7 @@ void ObjectSet::setClipGroup() } } -void ObjectSet::unsetMask(bool apply_clip_path) { +void ObjectSet::unsetMask(const bool apply_clip_path, const bool skip_undo) { SPDocument *doc = document(); Inkscape::XML::Document *xml_doc = doc->getReprDoc(); @@ -4010,13 +4088,13 @@ void ObjectSet::unsetMask(bool apply_clip_path) { SPGroup *group = dynamic_cast(*i); if (ungroup_masked && group) { - // if we had previously enclosed masked object in group, - // add it to list so we can ungroup it later + // if we had previously enclosed masked object in group, + // add it to list so we can ungroup it later - // ungroup only groups we created when setting clip/mask - if (group->layerMode() == SPGroup::MASK_HELPER) { - items_to_ungroup = g_slist_prepend(items_to_ungroup, group); - } + // ungroup only groups we created when setting clip/mask + if (group->layerMode() == SPGroup::MASK_HELPER) { + items_to_ungroup = g_slist_prepend(items_to_ungroup, group); + } } } @@ -4082,10 +4160,12 @@ void ObjectSet::unsetMask(bool apply_clip_path) { // rebuild selection addList(items_to_select); - if (apply_clip_path) { - DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); - } else { - DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); + if (!skip_undo) { + if (apply_clip_path) { + DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); + } else { + DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); + } } } diff --git a/src/verbs.cpp b/src/verbs.cpp index 7a9875c69..5e3fea677 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1009,6 +1009,9 @@ void EditVerb::perform(SPAction *action, void *data) case SP_VERB_EDIT_UNLINK_CLONE: dt->selection->unlink(); break; + case SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE: + dt->selection->unlinkRecursive(); + break; case SP_VERB_EDIT_RELINK_CLONE: dt->selection->relink(); break; @@ -2530,6 +2533,8 @@ Verb *Verb::_base_verbs[] = { N_("Create a clone (a copy linked to the original) of selected object"), INKSCAPE_ICON("edit-clone")), new EditVerb(SP_VERB_EDIT_UNLINK_CLONE, "EditUnlinkClone", N_("Unlin_k Clone"), N_("Cut the selected clones' links to the originals, turning them into standalone objects"), INKSCAPE_ICON("edit-clone-unlink")), + new EditVerb(SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, "EditUnlinkCloneRecursive", N_("Unlink Clones _recursively"), + N_("Unlink all clones in the selection, even if they are in groups."), INKSCAPE_ICON("edit-clone-unlink")), new EditVerb(SP_VERB_EDIT_RELINK_CLONE, "EditRelinkClone", N_("Relink to Copied"), N_("Relink the selected clones to the object currently on the clipboard"), NULL), new EditVerb(SP_VERB_EDIT_CLONE_SELECT_ORIGINAL, "EditCloneSelectOriginal", N_("Select _Original"), diff --git a/src/verbs.h b/src/verbs.h index 1780e0ebf..d7e966ae4 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -83,6 +83,7 @@ enum { SP_VERB_EDIT_DUPLICATE, SP_VERB_EDIT_CLONE, SP_VERB_EDIT_UNLINK_CLONE, + SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, SP_VERB_EDIT_RELINK_CLONE, SP_VERB_EDIT_CLONE_SELECT_ORIGINAL, SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, -- cgit v1.2.3 From 428acb542d830f86b6f506d762d9b15bb2b7beeb Mon Sep 17 00:00:00 2001 From: Alexander Brock Date: Fri, 4 Nov 2016 16:04:31 +0100 Subject: Preserve clips when unlinking clones (bzr r15204.1.2) --- src/selection-chemistry.cpp | 79 +++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 8639486d5..0b99fd7c4 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2669,41 +2669,66 @@ bool ObjectSet::unlink(const bool skip_undo) for (auto i=items_.rbegin();i!=items_.rend();++i){ SPItem *item = *i; - if (dynamic_cast(item)) { - SPObject *tspan = sp_tref_convert_to_tspan(item); - - if (tspan) { - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - } - - // Set unlink to true, and fall into the next if which - // will include this text item in the new selection - unlinked = true; + ObjectSet tmp_set(document()); + tmp_set.set(item); + bool has_clip = false; + bool has_mask = false; + Inkscape::URIReference *clip = item->clip_ref; + Inkscape::URIReference *mask = item->mask_ref; + if ((NULL != clip) && (NULL != clip->getObject())) { + tmp_set.unsetMask(true,true); + has_clip = true; + } + if ((NULL != mask) && (NULL != mask->getObject())) { + tmp_set.unsetMask(false,true); + has_mask = true; } - if (!(dynamic_cast(item) || dynamic_cast(item))) { - // keep the non-use item in the new selection - new_select.push_back(item); - continue; + if (has_mask || has_clip) { + unlinked = tmp_set.unlink(true) || unlinked; + if (has_mask) + tmp_set.setMask(false,false,true); + if (has_clip) + tmp_set.setMask(true,false,true); + new_select.push_back(tmp_set.singleItem()); } + else { + if (dynamic_cast(item)) { + SPObject *tspan = sp_tref_convert_to_tspan(item); + + if (tspan) { + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } - SPItem *unlink = NULL; - SPUse *use = dynamic_cast(item); - if (use) { - unlink = use->unlink(); - // Unable to unlink use (external or invalid href?) - if (!unlink) { + // Set unlink to true, and fall into the next if which + // will include this text item in the new selection + unlinked = true; + } + + if (!(dynamic_cast(item) || dynamic_cast(item))) { + // keep the non-use item in the new selection new_select.push_back(item); continue; } - } else /*if (SP_IS_TREF(use))*/ { - unlink = dynamic_cast(sp_tref_convert_to_tspan(item)); - g_assert(unlink != NULL); - } - unlinked = true; - // Add ungrouped items to the new selection. - new_select.push_back(unlink); + SPItem *unlink = NULL; + SPUse *use = dynamic_cast(item); + if (use) { + unlink = use->unlink(); + // Unable to unlink use (external or invalid href?) + if (!unlink) { + new_select.push_back(item); + continue; + } + } else /*if (SP_IS_TREF(use))*/ { + unlink = dynamic_cast(sp_tref_convert_to_tspan(item)); + g_assert(unlink != NULL); + } + + unlinked = true; + // Add ungrouped items to the new selection. + new_select.push_back(unlink); + } } if (!new_select.empty()) { // set new selection -- cgit v1.2.3 From f5a7ab4571dd994c1b02ab774b48a7bb5d61dd8f Mon Sep 17 00:00:00 2001 From: Alexander Brock Date: Fri, 4 Nov 2016 19:17:12 +0100 Subject: Make ObjectSet::unlink() work for clones which are both clipped and masked (bzr r15204.1.3) --- src/selection-chemistry.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 0b99fd7c4..dbad56b13 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2671,25 +2671,18 @@ bool ObjectSet::unlink(const bool skip_undo) ObjectSet tmp_set(document()); tmp_set.set(item); - bool has_clip = false; - bool has_mask = false; Inkscape::URIReference *clip = item->clip_ref; Inkscape::URIReference *mask = item->mask_ref; if ((NULL != clip) && (NULL != clip->getObject())) { tmp_set.unsetMask(true,true); - has_clip = true; + unlinked = tmp_set.unlink(true) || unlinked; + tmp_set.setMask(true,false,true); + new_select.push_back(tmp_set.singleItem()); } - if ((NULL != mask) && (NULL != mask->getObject())) { + else if ((NULL != mask) && (NULL != mask->getObject())) { tmp_set.unsetMask(false,true); - has_mask = true; - } - - if (has_mask || has_clip) { unlinked = tmp_set.unlink(true) || unlinked; - if (has_mask) - tmp_set.setMask(false,false,true); - if (has_clip) - tmp_set.setMask(true,false,true); + tmp_set.setMask(false,false,true); new_select.push_back(tmp_set.singleItem()); } else { -- cgit v1.2.3 From 250986a5113de9c1fbe4444247ffa94535c79e26 Mon Sep 17 00:00:00 2001 From: Alexander Brock Date: Sun, 6 Nov 2016 20:02:31 +0100 Subject: Remove duplicate code from unlinkRecursive() and fix issue with clones of groups (bzr r15204.1.4) --- src/selection-chemistry.cpp | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index dbad56b13..694e12f55 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2749,30 +2749,15 @@ bool ObjectSet::unlinkRecursive(const bool skip_undo) { bool unlinked = false; ObjectSet tmp_set(document()); std::vector items_(items().begin(), items().end()); - for (auto& it:items_){ + for (auto& it:items_) { + tmp_set.set(it); + unlinked = tmp_set.unlink(true) || unlinked; + it = tmp_set.singleItem(); if (SP_IS_GROUP(it)) { std::vector c = it->childList(false); tmp_set.setList(c); unlinked = tmp_set.unlinkRecursive(true) || unlinked; } - tmp_set.set(it); - bool has_clip = false; - bool has_mask = false; - Inkscape::URIReference *clip = it->clip_ref; - Inkscape::URIReference *mask = it->mask_ref; - if ((NULL != clip) && (NULL != clip->getObject())) { - tmp_set.unsetMask(true,true); - has_clip = true; - } - if ((NULL != mask) && (NULL != mask->getObject())) { - tmp_set.unsetMask(false,true); - has_mask = true; - } - unlinked = tmp_set.unlink(true) || unlinked; - if (has_mask) - tmp_set.setMask(false,false,true); - if (has_clip) - tmp_set.setMask(true,false,true); } if (!unlinked) { if(desktop()) -- cgit v1.2.3 From d7b2a527bbc557855b96e2ac01c1d8554c124019 Mon Sep 17 00:00:00 2001 From: Alexander Brock Date: Sun, 6 Nov 2016 22:36:33 +0100 Subject: Fix selection issue with ObjectSet::unlinkRecursive() and add tests for it. (bzr r15204.1.5) --- src/selection-chemistry.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 694e12f55..2cfc8aa16 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2767,6 +2767,7 @@ bool ObjectSet::unlinkRecursive(const bool skip_undo) { DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, _("Unlink clone recursively")); } + setList(items_); return unlinked; } -- cgit v1.2.3