From: mpv Date: Wed, 4 Dec 2019 06:35:18 +0000 (+0300) Subject: Task #3059 implementation: When “move to the end” of a group, propose to the user... X-Git-Tag: V9_5_0a1~131 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=a2e697d6efc2f36155ad2236ec52915b59cce738;p=modules%2Fshaper.git Task #3059 implementation: When “move to the end” of a group, propose to the user a new group update mode. --- diff --git a/doc/gui/Introduction.rst b/doc/gui/Introduction.rst index d78aba3b9..c35c806f6 100644 --- a/doc/gui/Introduction.rst +++ b/doc/gui/Introduction.rst @@ -241,7 +241,7 @@ Each feature, result, construction, group, field, parameter can be renamed using .. centered:: Construction pop-up menu -The order of features can be changed using *Move to the end* pop-up menu command. It works only for Group features. The selected group will be moved to the end of features list. +The order of features can be changed using *Move to the end* and *Move to the end and split* pop-up menu commands. They work only for Group features. The selected group or several groups will be moved to the end of features list. The *Move to the end and split* also splits the resulting group in several groups: one group per one selection. Folders can be used to arrange long Tree View for features. diff --git a/doc/gui/images/popup_menu_object_browser_feature.png b/doc/gui/images/popup_menu_object_browser_feature.png index 30e40564d..ed32e2f4c 100755 Binary files a/doc/gui/images/popup_menu_object_browser_feature.png and b/doc/gui/images/popup_menu_object_browser_feature.png differ diff --git a/src/CollectionPlugin/CMakeLists.txt b/src/CollectionPlugin/CMakeLists.txt index a02f2aa66..a8770cb7c 100644 --- a/src/CollectionPlugin/CMakeLists.txt +++ b/src/CollectionPlugin/CMakeLists.txt @@ -160,4 +160,7 @@ ADD_UNIT_TESTS( Test3031.py TestGroupWholeFeature1.py TestGroupWholeFeature2.py + TestGroupMoveAndSplit1.py + TestGroupMoveAndSplit2.py + TestGroupMoveAndSplit3.py ) diff --git a/src/CollectionPlugin/CollectionPlugin_Group.cpp b/src/CollectionPlugin/CollectionPlugin_Group.cpp index 82926ab21..c2110a64e 100644 --- a/src/CollectionPlugin/CollectionPlugin_Group.cpp +++ b/src/CollectionPlugin/CollectionPlugin_Group.cpp @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include CollectionPlugin_Group::CollectionPlugin_Group() { @@ -44,3 +46,107 @@ void CollectionPlugin_Group::execute() setResult(aGroup); } } + +// returns name with suffix, not existing in the existing set +static std::string findName( + const std::string theOrigin, int& theSuffix, std::set& theExisting) +{ + std::string aRes; + do { + std::ostringstream aName; + aName< aFeatNames, aResNames; + std::list allFeat = aDoc->allFeatures(); + std::list::iterator allFeatIter = allFeat.begin(); + for(; allFeatIter != allFeat.end(); allFeatIter++) { + FeaturePtr aFeat = *allFeatIter; + if (aFeat->data().get() && aFeat->data()->isValid()) { + aFeatNames.insert(aFeat->name()); + if (aFeat->getKind() == ID() && aFeat->data().get() && aFeat->data()->isValid()) { + std::list::const_iterator aRess = aFeat->results().cbegin(); + for(; aRess != aFeat->results().cend(); aRess++) { + ResultPtr aRes = *aRess; + if (aRes->data().get() && aRes->data()->isValid()) { + aResNames.insert(aRes->data()->name()); + } + } + } + } + } + + AttributeSelectionListPtr aList = selectionList(LIST_ID()); + std::set aRemoved; + bool aStay = false; // to indicate that the good attribute found stays in the list + int anIndex = 1; // index of the name assigned to group-feature and result + // added in the order: 3 2 1 orig=0, so, keep the results to give names later + std::list aResults; + for(int aNext = aList->size() - 1; aNext >= 0; aNext--) { + AttributeSelectionPtr anOldAttr = aList->value(aNext); + if (anOldAttr->isInvalid() || !anOldAttr->context().get()) {// remove invalids + aRemoved.insert(aNext); + continue; + } + if (!aStay) { + aStay = true; + continue; + } + aRemoved.insert(aNext); + FeaturePtr aNew = aDoc->addFeature(ID(), false); + AttributeSelectionListPtr aNewList = aNew->selectionList(LIST_ID()); + aNewList->setSelectionType(aList->selectionType()); + aNewList->append(anOldAttr->contextObject(), anOldAttr->value()); + aResults.push_front(aNew); // to keep the order + } + aResults.push_back(data()->owner()); + // remove all selections except the first + aList->remove(aRemoved); + // set names + if (aResults.size() > 1) { // rename if there are new groups appeared only + std::list::iterator aResIter = aResults.begin(); + for(int aSuffix = 1; aResIter != aResults.end(); aResIter++) { + FeaturePtr aFeat = std::dynamic_pointer_cast(*aResIter); + aFeat->data()->setName(findName(name(), aSuffix, aFeatNames)); + if (!aFeat->results().empty() && !results().empty()) { + int aResSuf = aSuffix - 1; + std::string aResName = findName(firstResult()->data()->name(), aResSuf, aResNames); + aFeat->firstResult()->data()->setName(aResName); + // set the same color of result as in origin + if (firstResult()->data()->attribute(ModelAPI_Result::COLOR_ID()).get()) { + AttributeIntArrayPtr aSourceColor = + firstResult()->data()->intArray(ModelAPI_Result::COLOR_ID()); + if (aSourceColor.get() && aSourceColor->size()) { + AttributeIntArrayPtr aDestColor = + aFeat->firstResult()->data()->intArray(ModelAPI_Result::COLOR_ID()); + aDestColor->setSize(aSourceColor->size()); + for(int a = 0; a < aSourceColor->size(); a++) + aDestColor->setValue(a, aSourceColor->value(a)); + } + } + } + } + // remove also filters if split performed + FiltersFeaturePtr aFilters = aList->filters(); + if (aFilters.get()) { + std::list aFiltersList = aFilters->filters(); + std::list::iterator aFilterName = aFiltersList.begin(); + for(; aFilterName != aFiltersList.end(); aFilterName++) { + aFilters->removeFilter(*aFilterName); + } + } + } + } + return true; +} diff --git a/src/CollectionPlugin/CollectionPlugin_Group.h b/src/CollectionPlugin/CollectionPlugin_Group.h index 0d61046e3..3c1dd0d16 100644 --- a/src/CollectionPlugin/CollectionPlugin_Group.h +++ b/src/CollectionPlugin/CollectionPlugin_Group.h @@ -66,6 +66,8 @@ class CollectionPlugin_Group : public ModelAPI_Feature /// Use plugin manager for features creation CollectionPlugin_Group(); + /// Used for the split action of the group (Move to the end and split) + COLLECTIONPLUGIN_EXPORT virtual bool customAction(const std::string& theActionId); }; #endif diff --git a/src/CollectionPlugin/Test/TestGroupMoveAndSplit1.py b/src/CollectionPlugin/Test/TestGroupMoveAndSplit1.py new file mode 100644 index 000000000..d468c4d34 --- /dev/null +++ b/src/CollectionPlugin/Test/TestGroupMoveAndSplit1.py @@ -0,0 +1,63 @@ +# Copyright (C) 2014-2019 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +# Check the specification case of move to the end and split (#3059) + +from salome.shaper import model +from ModelAPI import * +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Extrusion_1 = model.addExtrusion(Part_1_doc, [], model.selection(), 12, 0) +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchCircle_1 = Sketch_1.addCircle(33.32502963835739, 19.24021483244179, 5) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5) +SketchLine_1 = Sketch_1.addLine(0, 0, 33.32502963835739, 19.24021483244179) +SketchLine_1.setAuxiliary(True) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchPoint_1.result()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.endPoint()) +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_2 = SketchProjection_2.createdFeature() +SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_2.result(), SketchLine_1.result(), 30) +Extrusion_1.setNestedSketch(Sketch_1) +Group_1 = model.addGroup(Part_1_doc, "Faces", [model.selection("FACE", "Extrusion_1_1/To_Face")]) +AngularCopy_1 = model.addMultiRotation(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1")], model.selection("EDGE", "PartSet/OZ"), 12) +model.do() +Part_1_doc.moveFeature(Group_1.feature(), AngularCopy_1.feature(), True) +model.end() + +# must be created 12 groups of faces, 12 results +assert(Part_1_doc.size("Groups") == 12) + +for i in range(12): + resShape = modelAPI_Result(Part_1_doc.object("Groups", i)).shape() + assert(not resShape.isNull()) + # the group result is a compund, check that this is a compound of one face + aShapeExplorer = GeomAPI_ShapeExplorer(resShape, GeomAPI_Shape.FACE) + assert(aShapeExplorer.more()) + assert(aShapeExplorer.current().isFace()) + aShapeExplorer.next() + assert(not aShapeExplorer.more()) + +assert(model.checkPythonDump()) diff --git a/src/CollectionPlugin/Test/TestGroupMoveAndSplit2.py b/src/CollectionPlugin/Test/TestGroupMoveAndSplit2.py new file mode 100644 index 000000000..c39ae457b --- /dev/null +++ b/src/CollectionPlugin/Test/TestGroupMoveAndSplit2.py @@ -0,0 +1,95 @@ +# Copyright (C) 2014-2019 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +# Check the movement to the end and split: move to intermediate position, no duplicates appeared + +from SketchAPI import * + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False) +SketchPoint_1 = SketchProjection_1.createdFeature() +SketchCircle_1 = Sketch_1.addCircle(0, 0, 10) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center()) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 10) +model.do() +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection(), 10, 0) +Group_1 = model.addGroup(Part_1_doc, "Edges", [model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]")]) +ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), 0, 5, [model.selection("SOLID", "Extrusion_1_1")]) +Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face")) +SketchProjection_2 = Sketch_2.addProjection(model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]"), False) +SketchCircle_2 = SketchProjection_2.createdFeature() +SketchCircle_3 = Sketch_2.addCircle(0, 10, 3) +SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchCircle_2.results()[1], SketchCircle_3.center()) +SketchCircle_4 = Sketch_2.addCircle(0, -10, 3) +SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchCircle_2.results()[1], SketchCircle_4.center()) +SketchProjection_3 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OY"), True) +SketchLine_1 = SketchProjection_3.createdFeature() +SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchCircle_3.center(), SketchLine_1.result()) +SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchCircle_4.center(), SketchLine_1.result()) +SketchConstraintRadius_2 = Sketch_2.setRadius(SketchCircle_3.results()[1], 3) +SketchConstraintEqual_1 = Sketch_2.setEqual(SketchCircle_3.results()[1], SketchCircle_4.results()[1]) +ExtrusionCut_1.setNestedSketch(Sketch_2) + +ExtrusionCut_2 = model.addExtrusionCut(Part_1_doc, [], model.selection(), 0, 3, [model.selection("SOLID", "ExtrusionCut_1_1")]) +Sketch_3 = model.addSketch(Part_1_doc, model.selection("FACE", "ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face")) +SketchLine_2 = Sketch_3.addLine(10, 2, -10, 2) +SketchLine_3 = Sketch_3.addLine(-10, 2, -10, -2) +SketchLine_4 = Sketch_3.addLine(-10, -2, 10, -2) +SketchLine_5 = Sketch_3.addLine(10, -2, 10, 2) +SketchConstraintCoincidence_6 = Sketch_3.setCoincident(SketchLine_5.endPoint(), SketchLine_2.startPoint()) +SketchConstraintCoincidence_7 = Sketch_3.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_8 = Sketch_3.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchConstraintCoincidence_9 = Sketch_3.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchConstraintHorizontal_1 = Sketch_3.setHorizontal(SketchLine_2.result()) +SketchConstraintVertical_1 = Sketch_3.setVertical(SketchLine_3.result()) +SketchConstraintHorizontal_2 = Sketch_3.setHorizontal(SketchLine_4.result()) +SketchConstraintVertical_2 = Sketch_3.setVertical(SketchLine_5.result()) +SketchProjection_4 = Sketch_3.addProjection(model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/From_Face])2(ExtrusionCut_1_1/Generated_Edge&ExtrusionCut_1_1/From_Face_1)2([ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchCircle_4_2])2"), False) +SketchArc_1 = SketchProjection_4.createdFeature() +SketchConstraintTangent_1 = Sketch_3.setTangent(SketchLine_5.result(), SketchArc_1.results()[1]) +SketchProjection_5 = Sketch_3.addProjection(model.selection("EDGE", "([ExtrusionCut_1_1/Modified_Face&Extrusion_1_1/To_Face][ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchCircle_4_2])(ExtrusionCut_1_1/Generated_Edge&ExtrusionCut_1_1/From_Face_3)2(ExtrusionCut_1_1/Generated_Edge&ExtrusionCut_1_1/From_Face_2)2([ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchCircle_4_2][ExtrusionCut_1_1/Modified_Face&ExtrusionCut_1_1/From_Face_3])2"), False) +SketchArc_2 = SketchProjection_5.createdFeature() +SketchConstraintTangent_2 = Sketch_3.setTangent(SketchArc_2.results()[1], SketchLine_3.result()) +SketchConstraintDistanceVertical_1 = Sketch_3.setVerticalDistance(SketchAPI_Arc(SketchArc_1).center(), SketchLine_2.startPoint(), 2) +SketchConstraintDistanceVertical_2 = Sketch_3.setVerticalDistance(SketchAPI_Arc(SketchArc_1).center(), SketchLine_4.endPoint(), 2) +ExtrusionCut_2.setNestedSketch(Sketch_3) +model.do() +# move only after the first extrusion-cut +Part_1_doc.setCurrentFeature(ExtrusionCut_1.feature(), True) +model.do() +Part_1_doc.moveFeature(Group_1.feature(), ExtrusionCut_1.feature(), True) +model.end() +assert(Part_1_doc.size("Groups") == 3) # 3 edges in groups results + +# check that simple move to the end provides 4 edges (no duplicates) +model.undo() +model.undo() + +model.begin() +Part_1_doc.moveFeature(Group_1.feature(), ExtrusionCut_2.feature(), True) +model.end() +assert(Part_1_doc.size("Groups") == 4) # 4 edges in groups results + +assert(model.checkPythonDump()) diff --git a/src/CollectionPlugin/Test/TestGroupMoveAndSplit3.py b/src/CollectionPlugin/Test/TestGroupMoveAndSplit3.py new file mode 100644 index 000000000..047237625 --- /dev/null +++ b/src/CollectionPlugin/Test/TestGroupMoveAndSplit3.py @@ -0,0 +1,76 @@ +# Copyright (C) 2014-2019 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +# Check the movement to the end and split: whole results, check names of splitted results and groups + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Extrusion_1 = model.addExtrusion(Part_1_doc, [], model.selection(), 10, 0) +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(-8.333743842364534, 20.52339901477833, -20.15024630541872, 20.52339901477833) +SketchLine_2 = Sketch_1.addLine(-20.15024630541872, 20.52339901477833, -20.15024630541872, 3.980295566502462) +SketchLine_3 = Sketch_1.addLine(-20.15024630541872, 3.980295566502462, -8.333743842364534, 3.980295566502462) +SketchLine_4 = Sketch_1.addLine(-8.333743842364534, 3.980295566502462, -8.333743842364534, 20.52339901477833) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) +SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result()) +SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result()) +SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result()) +SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result()) +SketchCircle_1 = Sketch_1.addCircle(5.721674876847291, 12.81157635467982, 6.421166795138789) +Extrusion_1.setNestedSketch(Sketch_1) +Group_1 = model.addGroup(Part_1_doc, "Faces", [model.selection("SOLID", "Extrusion_1_1"), model.selection("SOLID", "Extrusion_1_2")]) +Group_1.setName("GroupResult") +Group_1.result().setName("GroupResult") +Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face")) +SketchLine_5 = Sketch_2.addLine(3.924377723198604, 15.23693857548147, -14.36967929032953, 15.23693857548147) +SketchLine_6 = Sketch_2.addLine(-14.36967929032953, 15.23693857548147, -14.36967929032953, 11.61585476914922) +SketchLine_7 = Sketch_2.addLine(-14.36967929032953, 11.61585476914922, 3.924377723198604, 11.61585476914922) +SketchLine_8 = Sketch_2.addLine(3.924377723198604, 11.61585476914922, 3.924377723198604, 15.23693857548147) +SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_8.endPoint(), SketchLine_5.startPoint()) +SketchConstraintCoincidence_6 = Sketch_2.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint()) +SketchConstraintCoincidence_8 = Sketch_2.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint()) +SketchConstraintHorizontal_3 = Sketch_2.setHorizontal(SketchLine_5.result()) +SketchConstraintVertical_3 = Sketch_2.setVertical(SketchLine_6.result()) +SketchConstraintHorizontal_4 = Sketch_2.setHorizontal(SketchLine_7.result()) +SketchConstraintVertical_4 = Sketch_2.setVertical(SketchLine_8.result()) +model.do() +Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("WIRE", "Sketch_2/Face-SketchLine_5r-SketchLine_6f-SketchLine_7f-SketchLine_8f_wire")], model.selection(), 2, 5) +Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1"), model.selection("SOLID", "Extrusion_2_1")], keepSubResults = True) +model.do() +Part_1_doc.moveFeature(Group_1.feature(), Fuse_1.feature(), True) +model.end() + +assert(Part_1_doc.size("Groups") == 2) # 2 results because initially 2 results were selected + +# check names of results +from ModelAPI import * +res1 = modelAPI_Result(Part_1_doc.object("Groups", 0)) +assert(res1.data().name() == "GroupResult_1") +res2 = modelAPI_Result(Part_1_doc.object("Groups", 1)) +assert(res2.data().name() == "GroupResult_2") + +assert(model.checkPythonDump()) diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index b1efb51d0..79162a779 100644 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -1239,7 +1239,7 @@ static bool isSub(const CompositeFeaturePtr theMain, const FeaturePtr theSub) { return isSub(theMain, aParent); } -void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) +void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis, const bool theSplit) { bool aCurrentUp = theMoved == currentFeature(false); if (aCurrentUp) { @@ -1264,12 +1264,21 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) } myObjs->moveFeature(theMoved, anAfterThisSub); + + if (theSplit) { // split the group into sub-features + theMoved->customAction("split"); + } + if (aCurrentUp) { // make the moved feature enabled or disabled due to the real status setCurrentFeature(currentFeature(false), false); } else if (theAfterThis == currentFeature(false) || anAfterThisSub == currentFeature(false)) { // must be after move to make enabled all features which are before theMoved setCurrentFeature(theMoved, true); } + + if (theSplit) { // split the group into sub-features + theMoved->customAction("split"); + } } void Model_Document::updateHistory(const std::shared_ptr theObject) diff --git a/src/Model/Model_Document.h b/src/Model/Model_Document.h index 1615ffce4..277c822ba 100644 --- a/src/Model/Model_Document.h +++ b/src/Model/Model_Document.h @@ -127,7 +127,8 @@ class Model_Document : public ModelAPI_Document MODEL_EXPORT virtual void removeFeature(FeaturePtr theFeature); //! Moves the feature to make it after the given one in the history. - MODEL_EXPORT virtual void moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis); + MODEL_EXPORT virtual void moveFeature( + FeaturePtr theMoved, FeaturePtr theAfterThis, const bool theSplit = false); //! Returns the first found object in the group by the object name //! \param theGroupID group that contains an object diff --git a/src/ModelAPI/ModelAPI_Document.h b/src/ModelAPI/ModelAPI_Document.h index 5f51a3376..602c4e803 100644 --- a/src/ModelAPI/ModelAPI_Document.h +++ b/src/ModelAPI/ModelAPI_Document.h @@ -81,7 +81,8 @@ public: //! Moves the feature to make it after the given one in the history. virtual void moveFeature(std::shared_ptr theMoved, - std::shared_ptr theAfterThis) = 0; + std::shared_ptr theAfterThis, + const bool theSplit = false) = 0; ///! Returns the id of the document virtual const int id() const = 0; diff --git a/src/PartSet/PartSet_Module.cpp b/src/PartSet/PartSet_Module.cpp index 342134c7a..f3e9a2856 100644 --- a/src/PartSet/PartSet_Module.cpp +++ b/src/PartSet/PartSet_Module.cpp @@ -553,7 +553,7 @@ bool PartSet_Module::canRedo() const bool PartSet_Module::canApplyAction(const ObjectPtr& theObject, const QString& theActionId) const { bool aValid = true; - if (theActionId == "MOVE_CMD") { + if (theActionId == "MOVE_CMD" || theActionId == "MOVE_SPLIT_CMD") { FeaturePtr aFeature = ModelAPI_Feature::feature(theObject); if (aFeature) { ResultPtr aResult = ModuleBase_Tools::firstResult(aFeature); diff --git a/src/SketcherPrs/SketcherPrs_SymbolPrs.cpp b/src/SketcherPrs/SketcherPrs_SymbolPrs.cpp index 5f79c5fba..e10434cf9 100644 --- a/src/SketcherPrs/SketcherPrs_SymbolPrs.cpp +++ b/src/SketcherPrs/SketcherPrs_SymbolPrs.cpp @@ -185,7 +185,7 @@ Handle(Image_AlienPixMap) SketcherPrs_SymbolPrs::icon() aSizedMap->InitTrash(aPixMap->Format(), aWidth, aHeigh); for (Standard_Size i = 0; i < aWidth; i++) { for (Standard_Size j = 0; j < aHeigh; j++) { - aSizedMap->SetPixelColor(i, j, aPixMap->PixelColor(i / aRatio, j / aRatio)); + aSizedMap->SetPixelColor(int(i), int(j), aPixMap->PixelColor(i / aRatio, j / aRatio)); } } aPixMap = aSizedMap; diff --git a/src/XGUI/XGUI_ContextMenuMgr.cpp b/src/XGUI/XGUI_ContextMenuMgr.cpp index 60db4013c..a174bea87 100644 --- a/src/XGUI/XGUI_ContextMenuMgr.cpp +++ b/src/XGUI/XGUI_ContextMenuMgr.cpp @@ -96,10 +96,14 @@ void XGUI_ContextMenuMgr::createActions() aDesktop, this, SLOT(onRename())); addAction("RENAME_CMD", aAction); - aAction = ModuleBase_Tools::createAction(QIcon(":pictures/move.png"), + aAction = ModuleBase_Tools::createAction(QIcon(":pictures/move_to_end.png"), XGUI_Workshop::MOVE_TO_END_COMMAND, this); addAction("MOVE_CMD", aAction); + aAction = ModuleBase_Tools::createAction(QIcon(":pictures/move_to_end_split.png"), + XGUI_Workshop::MOVE_TO_END_SPLIT_COMMAND, this); + addAction("MOVE_SPLIT_CMD", aAction); + aAction = ModuleBase_Tools::createAction(QIcon(":pictures/clean_history.png"), tr("Clean history"), aDesktop); addAction("CLEAN_HISTORY_CMD", aAction); @@ -326,8 +330,10 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu() if (!(hasParameter || hasFeature)) action("SHOW_ONLY_CMD")->setEnabled(true); } - else if (hasFeature && myWorkshop->canMoveFeature()) + else if (hasFeature && myWorkshop->canMoveFeature()) { action("MOVE_CMD")->setEnabled(true); + action("MOVE_SPLIT_CMD")->setEnabled(true); + } if( aMgr->activeDocument() == aObject->document() ) { @@ -347,8 +353,10 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu() action("SHADING_CMD")->setEnabled(true); action("WIREFRAME_CMD")->setEnabled(true); } - if (hasFeature && myWorkshop->canMoveFeature()) + if (hasFeature && myWorkshop->canMoveFeature()) { action("MOVE_CMD")->setEnabled(true); + action("MOVE_SPLIT_CMD")->setEnabled(true); + } } // end multi-selection // Check folder management commands state if only features are selected @@ -662,6 +670,7 @@ void XGUI_ContextMenuMgr::buildObjBrowserMenu() aList.append(action("RENAME_CMD")); aList.append(action("SHOW_RESULTS_CMD")); aList.append(action("MOVE_CMD")); + aList.append(action("MOVE_SPLIT_CMD")); aList.append(mySeparator1); aList.append(action("INSERT_FOLDER_CMD")); aList.append(action("ADD_TO_FOLDER_BEFORE_CMD")); @@ -770,6 +779,7 @@ void XGUI_ContextMenuMgr::addObjBrowserMenu(QMenu* theMenu) const aActions.append(action("ADD_OUT_FOLDER_AFTER_CMD")); aActions.append(mySeparator3); aActions.append(action("MOVE_CMD")); + aActions.append(action("MOVE_SPLIT_CMD")); aActions.append(action("COLOR_CMD")); aActions.append(action("DEFLECTION_CMD")); aActions.append(action("TRANSPARENCY_CMD")); diff --git a/src/XGUI/XGUI_Workshop.cpp b/src/XGUI/XGUI_Workshop.cpp index aa66b4896..fcee5e38a 100644 --- a/src/XGUI/XGUI_Workshop.cpp +++ b/src/XGUI/XGUI_Workshop.cpp @@ -170,6 +170,7 @@ static Handle(VInspector_CallBack) MyVCallBack; //#define DEBUG_WITH_MESSAGE_REPORT QString XGUI_Workshop::MOVE_TO_END_COMMAND = QObject::tr("Move to the end"); +QString XGUI_Workshop::MOVE_TO_END_SPLIT_COMMAND = QObject::tr("Move to the end and split"); //#define DEBUG_DELETE //#define DEBUG_FEATURE_NAME @@ -1215,7 +1216,8 @@ void XGUI_Workshop::processUndoRedo(const ModuleBase_ActionType theActionType, i else aMgr->redo(); - if (QString((*aIt).c_str()) == MOVE_TO_END_COMMAND) + if (QString((*aIt).c_str()) == MOVE_TO_END_COMMAND || + QString((*aIt).c_str()) == MOVE_TO_END_SPLIT_COMMAND) myObjectBrowser->rebuildDataTree(); } operationMgr()->updateApplyOfOperations(); @@ -1636,8 +1638,8 @@ void XGUI_Workshop::onContextMenuCommand(const QString& theId, bool isChecked) deleteObjects(); else if (theId == "CLEAN_HISTORY_CMD") cleanHistory(); - else if (theId == "MOVE_CMD") - moveObjects(); + else if (theId == "MOVE_CMD" || theId == "MOVE_SPLIT_CMD") + moveObjects(theId == "MOVE_SPLIT_CMD"); else if (theId == "COLOR_CMD") changeColor(aObjects); else if (theId == "DEFLECTION_CMD") @@ -2095,7 +2097,7 @@ bool compareFeature(const FeaturePtr& theF1, const FeaturePtr& theF2) { DocumentPtr aDoc = theF1->document(); return aDoc->index(theF1) < aDoc->index(theF2); } -void XGUI_Workshop::moveObjects() +void XGUI_Workshop::moveObjects(const bool theSplit) { if (!abortAllOperations()) return; @@ -2112,7 +2114,7 @@ void XGUI_Workshop::moveObjects() if (!XGUI_Tools::canRemoveOrRename(desktop(), aFeatures)) return; - QString anActionId = "MOVE_CMD"; + QString anActionId = theSplit ? "MOVE_CMD" : "MOVE_SPLIT_CMD"; QString aDescription = contextMenuMgr()->action(anActionId)->text(); aMgr->startOperation(aDescription.toStdString()); @@ -2128,7 +2130,7 @@ void XGUI_Workshop::moveObjects() if (!aFeature.get() || !myModule->canApplyAction(aFeature, anActionId)) continue; - anActiveDocument->moveFeature(aFeature, aCurrentFeature); + anActiveDocument->moveFeature(aFeature, aCurrentFeature, theSplit); aCurrentFeature = anActiveDocument->currentFeature(true); } aMgr->finishOperation(); diff --git a/src/XGUI/XGUI_Workshop.h b/src/XGUI/XGUI_Workshop.h index 4b06dac3f..37ae6a8a2 100644 --- a/src/XGUI/XGUI_Workshop.h +++ b/src/XGUI/XGUI_Workshop.h @@ -186,7 +186,7 @@ Q_OBJECT bool canMoveFeature(); /// Move selected features to be after the current feature - void moveObjects(); + void moveObjects(const bool theSplit); /// Returns true if the object can be shaded. If the object is a compsolid result, the method /// checks subobjects of the result @@ -316,6 +316,9 @@ Q_OBJECT /// A constant string used for "Move to end" command definition /// It is used for specific processing of Undo/Redo for this command. static QString MOVE_TO_END_COMMAND; + /// A constant string used for "Move to end and split" command definition + /// It is used for specific processing of Undo/Redo for this command. + static QString MOVE_TO_END_SPLIT_COMMAND; /// Closes all in the current session and load the directory /// \param theDirectory a path to directory diff --git a/src/XGUI/XGUI_msg_fr.ts b/src/XGUI/XGUI_msg_fr.ts index 9b519926b..ebe3b8867 100644 --- a/src/XGUI/XGUI_msg_fr.ts +++ b/src/XGUI/XGUI_msg_fr.ts @@ -15,6 +15,10 @@ Move to the end Aller à la fin + + Move to the end and split + Aller à la fin et diviser + SHAPER files (*.shaper *.cadbld) Fichiers SHAPER (*.shaper *.cadbld) diff --git a/src/XGUI/XGUI_pictures.qrc b/src/XGUI/XGUI_pictures.qrc index acb22325c..9b958d9e7 100644 --- a/src/XGUI/XGUI_pictures.qrc +++ b/src/XGUI/XGUI_pictures.qrc @@ -90,5 +90,7 @@ pictures/color.png pictures/normal-view-inversed.png pictures/normal-view.png + pictures/move_to_end.png + pictures/move_to_end_split.png diff --git a/src/XGUI/pictures/move_to_end.png b/src/XGUI/pictures/move_to_end.png new file mode 100644 index 000000000..a80ce7fd5 Binary files /dev/null and b/src/XGUI/pictures/move_to_end.png differ diff --git a/src/XGUI/pictures/move_to_end_split.png b/src/XGUI/pictures/move_to_end_split.png new file mode 100644 index 000000000..bcc017d1e Binary files /dev/null and b/src/XGUI/pictures/move_to_end_split.png differ