Salome HOME
Task #3059 implementation: When “move to the end” of a group, propose to the user...
authormpv <mpv@opencascade.com>
Wed, 4 Dec 2019 06:35:18 +0000 (09:35 +0300)
committermpv <mpv@opencascade.com>
Wed, 4 Dec 2019 06:35:18 +0000 (09:35 +0300)
20 files changed:
doc/gui/Introduction.rst
doc/gui/images/popup_menu_object_browser_feature.png
src/CollectionPlugin/CMakeLists.txt
src/CollectionPlugin/CollectionPlugin_Group.cpp
src/CollectionPlugin/CollectionPlugin_Group.h
src/CollectionPlugin/Test/TestGroupMoveAndSplit1.py [new file with mode: 0644]
src/CollectionPlugin/Test/TestGroupMoveAndSplit2.py [new file with mode: 0644]
src/CollectionPlugin/Test/TestGroupMoveAndSplit3.py [new file with mode: 0644]
src/Model/Model_Document.cpp
src/Model/Model_Document.h
src/ModelAPI/ModelAPI_Document.h
src/PartSet/PartSet_Module.cpp
src/SketcherPrs/SketcherPrs_SymbolPrs.cpp
src/XGUI/XGUI_ContextMenuMgr.cpp
src/XGUI/XGUI_Workshop.cpp
src/XGUI/XGUI_Workshop.h
src/XGUI/XGUI_msg_fr.ts
src/XGUI/XGUI_pictures.qrc
src/XGUI/pictures/move_to_end.png [new file with mode: 0644]
src/XGUI/pictures/move_to_end_split.png [new file with mode: 0644]

index d78aba3b930526f643ac00cd2225e28baf0a7c59..c35c806f64609bfa65a0e587bdb6d1e64a8e1fd1 100644 (file)
@@ -241,7 +241,7 @@ Each feature, result, construction, group, field, parameter can be renamed using
 .. centered::\r
    Construction pop-up menu\r
 \r
 .. centered::\r
    Construction pop-up menu\r
 \r
-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.\r
+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.\r
 \r
 Folders can be used to arrange long Tree View for features.\r
 \r
 \r
 Folders can be used to arrange long Tree View for features.\r
 \r
index 30e40564de322526f602705c080f79103c65dbd7..ed32e2f4cfcf22a0645cfd4f37baf54fee8ec086 100755 (executable)
Binary files a/doc/gui/images/popup_menu_object_browser_feature.png and b/doc/gui/images/popup_menu_object_browser_feature.png differ
index a02f2aa6642d39c5d04f2a7f60cb5dc3c46f5b48..a8770cb7c7059cf0a694b4f22f6fc9b1433f060e 100644 (file)
@@ -160,4 +160,7 @@ ADD_UNIT_TESTS(
                Test3031.py
                TestGroupWholeFeature1.py
                TestGroupWholeFeature2.py
                Test3031.py
                TestGroupWholeFeature1.py
                TestGroupWholeFeature2.py
+               TestGroupMoveAndSplit1.py
+               TestGroupMoveAndSplit2.py
+               TestGroupMoveAndSplit3.py
 )
 )
index 82926ab21921946ac76ac567b84f900ec9eb16bb..c2110a64ee9ed21c8b99c8b9bd83212a79790eae 100644 (file)
@@ -24,7 +24,9 @@
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeString.h>
 #include <ModelAPI_AttributeSelectionList.h>
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeString.h>
 #include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_AttributeIntArray.h>
 #include <ModelAPI_ResultGroup.h>
 #include <ModelAPI_ResultGroup.h>
+#include <sstream>
 
 CollectionPlugin_Group::CollectionPlugin_Group()
 {
 
 CollectionPlugin_Group::CollectionPlugin_Group()
 {
@@ -44,3 +46,107 @@ void CollectionPlugin_Group::execute()
     setResult(aGroup);
   }
 }
     setResult(aGroup);
   }
 }
+
+// returns name with suffix, not existing in the existing set
+static std::string findName(
+  const std::string theOrigin, int& theSuffix, std::set<std::string>& theExisting)
+{
+  std::string aRes;
+  do {
+    std::ostringstream aName;
+    aName<<theOrigin<<"_"<<theSuffix;
+    aRes = aName.str();
+    theSuffix++;
+  } while(theExisting.count(aRes));
+  theExisting.insert(aRes);
+  return aRes;
+
+}
+
+bool CollectionPlugin_Group::customAction(const std::string& theActionId)
+{
+  if (theActionId == "split") {
+    DocumentPtr aDoc = document();
+    // collect all existing names of features to give unique names
+    std::set<std::string> aFeatNames, aResNames;
+    std::list<FeaturePtr> allFeat = aDoc->allFeatures();
+    std::list<FeaturePtr>::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<ResultPtr>::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<int> 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<ObjectPtr> 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<ObjectPtr>::iterator aResIter = aResults.begin();
+      for(int aSuffix = 1; aResIter != aResults.end(); aResIter++) {
+        FeaturePtr aFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(*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<std::string> aFiltersList = aFilters->filters();
+        std::list<std::string>::iterator aFilterName = aFiltersList.begin();
+        for(; aFilterName != aFiltersList.end(); aFilterName++) {
+          aFilters->removeFilter(*aFilterName);
+        }
+      }
+    }
+  }
+  return true;
+}
index 0d61046e3b5dfe003c37fbe7b5b8e93c36a510d1..3c1dd0d165a3a020314170a8ee3119868c96f03b 100644 (file)
@@ -66,6 +66,8 @@ class CollectionPlugin_Group : public ModelAPI_Feature
   /// Use plugin manager for features creation
   CollectionPlugin_Group();
 
   /// 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
 };
 
 #endif
diff --git a/src/CollectionPlugin/Test/TestGroupMoveAndSplit1.py b/src/CollectionPlugin/Test/TestGroupMoveAndSplit1.py
new file mode 100644 (file)
index 0000000..d468c4d
--- /dev/null
@@ -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 (file)
index 0000000..c39ae45
--- /dev/null
@@ -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 (file)
index 0000000..0472376
--- /dev/null
@@ -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())
index b1efb51d0fbf3a1829c41fc17ad9887985bac883..79162a77945b2302e00d7ddc8818358b7cd60457 100644 (file)
@@ -1239,7 +1239,7 @@ static bool isSub(const CompositeFeaturePtr theMain, const FeaturePtr theSub) {
   return isSub(theMain, aParent);
 }
 
   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) {
 {
   bool aCurrentUp = theMoved == currentFeature(false);
   if (aCurrentUp) {
@@ -1264,12 +1264,21 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis)
   }
 
   myObjs->moveFeature(theMoved, anAfterThisSub);
   }
 
   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 (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<ModelAPI_Object> theObject)
 }
 
 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
index 1615ffce436b3876abf285df3b019d9f744cbffb..277c822ba629730adc39a796a6a78192f72afcc2 100644 (file)
@@ -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 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
 
   //! Returns the first found object in the group by the object name
   //! \param theGroupID group that contains an object
index 5f51a3376418c40a60d9da42b643a19debb07355..602c4e8033280f0b9eeba6ca31d78b677220f75e 100644 (file)
@@ -81,7 +81,8 @@ public:
 
   //! Moves the feature to make it after the given one in the history.
   virtual void moveFeature(std::shared_ptr<ModelAPI_Feature> theMoved,
 
   //! Moves the feature to make it after the given one in the history.
   virtual void moveFeature(std::shared_ptr<ModelAPI_Feature> theMoved,
-                           std::shared_ptr<ModelAPI_Feature> theAfterThis) = 0;
+                           std::shared_ptr<ModelAPI_Feature> theAfterThis,
+                           const bool theSplit = false) = 0;
 
   ///! Returns the id of the document
   virtual const int id() const = 0;
 
   ///! Returns the id of the document
   virtual const int id() const = 0;
index 342134c7a56a353dc290ba7f118e4d8d4e25214a..f3e9a285618d37d6655bfae5944201595ffc54a9 100644 (file)
@@ -553,7 +553,7 @@ bool PartSet_Module::canRedo() const
 bool PartSet_Module::canApplyAction(const ObjectPtr& theObject, const QString& theActionId) const
 {
   bool aValid = true;
 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);
     FeaturePtr aFeature = ModelAPI_Feature::feature(theObject);
     if (aFeature) {
       ResultPtr aResult = ModuleBase_Tools::firstResult(aFeature);
index 5f79c5fbaec5d90b5dca3abe513ef441be0c4bf3..e10434cf9e3f024f5ee28610480786d44dc8e377 100644 (file)
@@ -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->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;
         }
       }
       aPixMap = aSizedMap;
index 60db4013ccbd13c7982158734a7c1c0743faff2f..a174bea87b737f8ae17402cf8e0e725739ea0687 100644 (file)
@@ -96,10 +96,14 @@ void XGUI_ContextMenuMgr::createActions()
                                            aDesktop, this, SLOT(onRename()));
   addAction("RENAME_CMD", aAction);
 
                                            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);
 
                                            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);
   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);
         }
           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_CMD")->setEnabled(true);
+          action("MOVE_SPLIT_CMD")->setEnabled(true);
+        }
 
         if( aMgr->activeDocument() == aObject->document() )
         {
 
         if( aMgr->activeDocument() == aObject->document() )
         {
@@ -347,8 +353,10 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu()
         action("SHADING_CMD")->setEnabled(true);
         action("WIREFRAME_CMD")->setEnabled(true);
       }
         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_CMD")->setEnabled(true);
+        action("MOVE_SPLIT_CMD")->setEnabled(true);
+      }
     } // end multi-selection
 
     // Check folder management commands state if only features are selected
     } // 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("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"));
   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("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"));
       aActions.append(action("COLOR_CMD"));
       aActions.append(action("DEFLECTION_CMD"));
       aActions.append(action("TRANSPARENCY_CMD"));
index aa66b48967f44f4061d8371635c19abb1bda06d6..fcee5e38ad8f2cfd77b6046f1885e560bc77bd74 100644 (file)
@@ -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");
 //#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
 
 //#define DEBUG_DELETE
 //#define DEBUG_FEATURE_NAME
@@ -1215,7 +1216,8 @@ void XGUI_Workshop::processUndoRedo(const ModuleBase_ActionType theActionType, i
     else
       aMgr->redo();
 
     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();
       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();
     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")
   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);
 }
   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;
 {
   if (!abortAllOperations())
     return;
@@ -2112,7 +2114,7 @@ void XGUI_Workshop::moveObjects()
   if (!XGUI_Tools::canRemoveOrRename(desktop(), aFeatures))
     return;
 
   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());
 
   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;
 
     if (!aFeature.get() || !myModule->canApplyAction(aFeature, anActionId))
       continue;
 
-    anActiveDocument->moveFeature(aFeature, aCurrentFeature);
+    anActiveDocument->moveFeature(aFeature, aCurrentFeature, theSplit);
     aCurrentFeature = anActiveDocument->currentFeature(true);
   }
   aMgr->finishOperation();
     aCurrentFeature = anActiveDocument->currentFeature(true);
   }
   aMgr->finishOperation();
index 4b06dac3f54ce95e4804a063b59957d5e081708c..37ae6a8a2be07ea5265ba5463d38f4670c141f45 100644 (file)
@@ -186,7 +186,7 @@ Q_OBJECT
   bool canMoveFeature();
 
   /// Move selected features to be after the current feature
   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
 
   /// 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" 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
 
   /// Closes all in the current session and load the directory
   /// \param theDirectory a path to directory
index 9b519926b28ecb646266fe2fc45f7b34f19b0a90..ebe3b8867122a019377dd0f3feda279c4cd28c1f 100644 (file)
         <source>Move to the end</source>
         <translation>Aller à la fin</translation>
     </message>
         <source>Move to the end</source>
         <translation>Aller à la fin</translation>
     </message>
+    <message>
+        <source>Move to the end and split</source>
+        <translation>Aller à la fin et diviser</translation>
+    </message>
     <message>
         <source>SHAPER files (*.shaper *.cadbld)</source>
         <translation>Fichiers SHAPER (*.shaper *.cadbld)</translation>
     <message>
         <source>SHAPER files (*.shaper *.cadbld)</source>
         <translation>Fichiers SHAPER (*.shaper *.cadbld)</translation>
index acb22325c73f3f8beae5b76342a4f898c265da88..9b958d9e758cde5347d49338cc273f11ccf7af60 100644 (file)
@@ -90,5 +90,7 @@
      <file>pictures/color.png</file>
      <file>pictures/normal-view-inversed.png</file>
      <file>pictures/normal-view.png</file>
      <file>pictures/color.png</file>
      <file>pictures/normal-view-inversed.png</file>
      <file>pictures/normal-view.png</file>
+     <file>pictures/move_to_end.png</file>
+     <file>pictures/move_to_end_split.png</file>
  </qresource>
  </RCC>
  </qresource>
  </RCC>
diff --git a/src/XGUI/pictures/move_to_end.png b/src/XGUI/pictures/move_to_end.png
new file mode 100644 (file)
index 0000000..a80ce7f
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 (file)
index 0000000..bcc017d
Binary files /dev/null and b/src/XGUI/pictures/move_to_end_split.png differ