From 7ebf53e94dffb9f7c98d9adf129ad4da491d9353 Mon Sep 17 00:00:00 2001 From: Syres916 <46537884+Syres916@users.noreply.github.com> Date: Sat, 16 Oct 2021 12:20:55 +0100 Subject: [PATCH 001/138] [Sketcher] Minor bugfix to display angle... ...constraint names as per recommendation, see discussion https://forum.freecadweb.org/viewtopic.php?f=3&t=62953 --- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 698dadd0a4..c3a89f5c12 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -3193,7 +3193,7 @@ QString ViewProviderSketch::getPresentationString(const Constraint *constraint) // Hide units if user has requested it, is being displayed in the base // units, and the schema being used has a clear base unit in the first // place. Otherwise, display units. - if( iHideUnits ) + if( iHideUnits && constraint->Type != Sketcher::Angle ) { // Only hide the default length unit. Right now there is not an easy way // to get that from the Unit system so we have to manually add it here. @@ -5766,7 +5766,7 @@ Restart: break; SoDatumLabel *asciiText = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - asciiText->string = SbString(Constr->getPresentationValue().getUserString().toUtf8().constData()); + asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); asciiText->datumtype = SoDatumLabel::ANGLE; asciiText->param1 = Constr->LabelDistance; asciiText->param2 = startangle; From fcb5c768ff40f4200485ac7d3958e4af67d896f0 Mon Sep 17 00:00:00 2001 From: easyw Date: Wed, 20 Oct 2021 10:10:43 +0200 Subject: [PATCH 002/138] deleting temp file (result of a previous export) --- src/Mod/Import/stepZ.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/Mod/Import/stepZ.py b/src/Mod/Import/stepZ.py index afec487d45..a6f0034a02 100644 --- a/src/Mod/Import/stepZ.py +++ b/src/Mod/Import/stepZ.py @@ -22,7 +22,7 @@ from PySide import QtGui, QtCore import tempfile import shutil -___stpZversion___ = "1.3.8" +___stpZversion___ = "1.3.9" # support both gz and zipfile archives # Catia seems to use gz, Inventor zipfile # improved import, open and export @@ -162,25 +162,21 @@ def export(objs,filename): if os.path.exists(outfpathT_stp): - sayzw("File cannot be compressed because a file with the same name exists '"+ outfpathT_stp +"'") - QtGui.QApplication.restoreOverrideCursor() - reply = QtGui.QMessageBox.information(None,"info", "File cannot be compressed because\na file with the same name exists\n'"+ outfpath_stp + "'") + os.remove(outfpathT_stp) + sayzw("Old temp file with the same name removed '"+ outfpathT_stp +"'") + ImportGui.export(objs,outfpathT_stp) + with builtin.open(outfpathT_stp, 'rb') as f_in: + file_content = f_in.read() + new_f_content = file_content + f_in.close() + with gz.open(outfpathT_str, 'wb') as f_out: + f_out.write(new_f_content) + f_out.close() + if os.path.exists(outfpath): + shutil.move(outfpathT_str, outfpath) + #os.remove(outfpathT_stp) else: - ImportGui.export(objs,outfpathT_stp) - with builtin.open(outfpathT_stp, 'rb') as f_in: - file_content = f_in.read() - new_f_content = file_content - f_in.close() - with gz.open(outfpathT_str, 'wb') as f_out: - f_out.write(new_f_content) - f_out.close() - if os.path.exists(outfpath): - os.remove(outfpath) - shutil.move(outfpathT_str, outfpath) - #os.remove(outfpathT_stp) - else: - shutil.move(outfpathT_str, outfpath) - #os.remove(outfpathT_stp) + shutil.move(outfpathT_str, outfpath) + #os.remove(outfpathT_stp) #### - From 25c3da4f0b09a42d8d83f6ee0d33a8839356ec06 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:15:12 +0200 Subject: [PATCH 003/138] Draft: Fix Draft_SelectPlane ignores nesting --- src/Mod/Draft/WorkingPlane.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index af53f6695c..3e130699dc 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -521,7 +521,7 @@ class Plane: self.v = v2 self.axis = v3 - def alignToFace(self, shape, offset=0): + def alignToFace(self, shape, offset=0, parent=None): """Align the plane to a face. It uses the center of mass of the face as `position`, @@ -542,6 +542,10 @@ class Plane: Defaults to zero. A value which will be used to offset the plane in the direction of its `axis`. + parent : object + Defaults to None. The ParentGeoFeatureGroup of the object + the face belongs to. + Returns ------- bool @@ -554,17 +558,21 @@ class Plane: """ # Set face to the unique selected face, if found if shape.ShapeType == 'Face': - self.alignToPointAndAxis(shape.Faces[0].CenterOfMass, - shape.Faces[0].normalAt(0, 0), - offset) + if parent: + place = parent.getGlobalPlacement() + else: + place = FreeCAD.Placement() + cen = place.multVec(shape.Faces[0].CenterOfMass) + place.Base = FreeCAD.Vector(0, 0, 0) # Reset the Base for the conversion of the normal. + nor = place.multVec(shape.Faces[0].normalAt(0, 0)) + self.alignToPointAndAxis(cen, nor, offset) import DraftGeomUtils q = DraftGeomUtils.getQuad(shape) if q: - self.u = q[1] - self.v = q[2] + self.u = place.multVec(q[1]) + self.v = place.multVec(q[2]) if not DraftVecUtils.equals(self.u.cross(self.v), self.axis): - self.u = q[2] - self.v = q[1] + self.u, self.v = self.v, self.u if DraftVecUtils.equals(self.u, Vector(0, 0, 1)): # the X axis is vertical: rotate 90 degrees self.u, self.v = self.v.negative(), self.u From b6faf7f016e00b38a6330cdad4a43a74944f2cd7 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:16:49 +0200 Subject: [PATCH 004/138] Draft: Fix Draft_SelectPlane ignores nesting --- src/Mod/Draft/draftguitools/gui_selectplane.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mod/Draft/draftguitools/gui_selectplane.py b/src/Mod/Draft/draftguitools/gui_selectplane.py index adf753db5c..22d97e722d 100644 --- a/src/Mod/Draft/draftguitools/gui_selectplane.py +++ b/src/Mod/Draft/draftguitools/gui_selectplane.py @@ -209,7 +209,9 @@ class Draft_SelectPlane: if len(sel.SubElementNames) == 1: # look for a face or a plane if "Face" in sel.SubElementNames[0]: - FreeCAD.DraftWorkingPlane.alignToFace(sel.SubObjects[0], self.getOffset()) + FreeCAD.DraftWorkingPlane.alignToFace(sel.SubObjects[0], + self.getOffset(), + sel.Object.getParentGeoFeatureGroup()) self.display(FreeCAD.DraftWorkingPlane.axis) return True elif sel.SubElementNames[0] == "Plane": From 13ff620f36eadcb2367d969c22d7d72f91254d86 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Wed, 20 Oct 2021 13:25:18 +0200 Subject: [PATCH 005/138] Draft: Fix TeighaFileConverter tooltip The TeighaFileConverter preference is now used for 3 DWG converters. The tooltip needs to reflect that. --- src/Mod/Draft/Resources/ui/preferences-dwg.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/Resources/ui/preferences-dwg.ui b/src/Mod/Draft/Resources/ui/preferences-dwg.ui index 4d97ab91cc..b52ea78448 100644 --- a/src/Mod/Draft/Resources/ui/preferences-dwg.ui +++ b/src/Mod/Draft/Resources/ui/preferences-dwg.ui @@ -82,7 +82,7 @@ - The path to your ODA (formerly Teigha) File Converter executable + The path to your DWG file converter executable TeighaFileConverter From 4e0b8c535f4d1c476cbd08ff449109eee5b4c204 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 20 Oct 2021 08:06:05 -0500 Subject: [PATCH 006/138] [Sketcher] Workaround for Elements theme issue After merging the change to the Constraint status label in f2a073ca5, the TaskSketcherElements would sometimes be reduced in size to just a line or two, if multiple task views were expanded and a stylesheet was enabled. This commit introduces a minimum size to the TSE to prevent that from occurring. --- src/Mod/Sketcher/Gui/TaskSketcherElements.ui | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.ui b/src/Mod/Sketcher/Gui/TaskSketcherElements.ui index 777e9ce69e..d2321f6930 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.ui +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.ui @@ -10,6 +10,12 @@ 401 + + + 0 + 400 + + Form From 30ba396a8252bfb6f404c52f1f8ef06a74f6fb21 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 20 Oct 2021 19:42:39 +0200 Subject: [PATCH 007/138] Mesh: improve MeshGeomFacet::IntersectWithFacet --- src/Mod/Mesh/App/Core/Elements.cpp | 74 ++++++++++++++++++------------ src/Mod/Mesh/App/Core/Elements.h | 4 ++ src/Mod/Mesh/App/MeshTestsApp.py | 53 +++++++++++++++++++++ 3 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/Mod/Mesh/App/Core/Elements.cpp b/src/Mod/Mesh/App/Core/Elements.cpp index 4e8cad49d3..bd434698ce 100644 --- a/src/Mod/Mesh/App/Core/Elements.cpp +++ b/src/Mod/Mesh/App/Core/Elements.cpp @@ -1036,6 +1036,15 @@ void MeshGeomFacet::SubSample (float fStep, std::vector &rclPoin rclPoints.insert(rclPoints.end(), clPoints.begin(), clPoints.end()); } +bool MeshGeomFacet::IsCoplanar(const MeshGeomFacet &facet) const +{ + const float eps = 1e-06f; + const float unit = 0.9995f; + float mult = fabs(this->GetNormal() * facet.GetNormal()); + float dist = fabs(DistancePlaneToPoint(facet._aclPoints[0])); + return (mult >= unit) && (dist <= eps); +} + /** * Fast Triangle-Triangle Intersection Test by Tomas Moeller * http://www.acm.org/jgt/papers/Moller97/tritri.html @@ -1068,6 +1077,39 @@ int MeshGeomFacet::IntersectWithFacet (const MeshGeomFacet& rclFacet, Base::Vector3f& rclPt0, Base::Vector3f& rclPt1) const { + // Note: tri_tri_intersect_with_isection() does not return line of + // intersection when triangles are coplanar. See tritritest.h:18 and 658. + if (IsCoplanar(rclFacet)) { + // Since tri_tri_intersect_with_isection may return garbage values try to get + // sensible values with edge/edge intersections + std::vector intersections; + for (short i=0; i<3; i++) { + MeshGeomEdge edge1 = GetEdge(i); + for (short j=0; j<3; j++) { + MeshGeomEdge edge2 = rclFacet.GetEdge(j); + Base::Vector3f point; + if (edge1.IntersectWithEdge(edge2, point)) { + intersections.push_back(point); + } + } + } + + // If triangles overlap there can be more than two intersection points + // In that case use any two of them. + if (intersections.size() >= 2) { + rclPt0 = intersections[0]; + rclPt1 = intersections[1]; + return 2; + } + else if (intersections.size() == 1) { + rclPt0 = intersections[0]; + rclPt1 = intersections[0]; + return 1; + } + + return 0; + } + float V[3][3], U[3][3]; int coplanar = 0; float isectpt1[3], isectpt2[3]; @@ -1089,45 +1131,17 @@ int MeshGeomFacet::IntersectWithFacet (const MeshGeomFacet& rclFacet, rclPt0.x = isectpt1[0]; rclPt0.y = isectpt1[1]; rclPt0.z = isectpt1[2]; rclPt1.x = isectpt2[0]; rclPt1.y = isectpt2[1]; rclPt1.z = isectpt2[2]; - // Note: tri_tri_intersect_with_isection() does not return line of - // intersection when triangles are coplanar. See tritritest.h:18 and 658. - if (coplanar) { - // Since tri_tri_intersect_with_isection may return garbage values try to get - // sensible values with edge/edge intersections - std::vector intersections; - for (short i=0; i<3; i++) { - MeshGeomEdge edge1 = GetEdge(i); - for (short j=0; j<3; j++) { - MeshGeomEdge edge2 = rclFacet.GetEdge(j); - Base::Vector3f point; - if (edge1.IntersectWithEdge(edge2, point)) { - intersections.push_back(point); - } - } - } - - // If triangles overlap there can be more than two intersection points - // In that case use any two of them. - if (intersections.size() >= 2) { - rclPt0 = intersections[0]; - rclPt1 = intersections[1]; - } - else if (intersections.size() == 1) { - rclPt0 = intersections[0]; - rclPt1 = intersections[0]; - } - return 2; - } - // With extremely acute-angled triangles it may happen that the algorithm // claims an intersection but the intersection points are far outside the // model. So, a plausibility check is to verify that the intersection points // are inside the bounding boxes of both triangles. Base::BoundBox3f box1 = this->GetBoundBox(); + box1.Enlarge(0.001f); if (!box1.IsInBox(rclPt0) || !box1.IsInBox(rclPt1)) return 0; Base::BoundBox3f box2 = rclFacet.GetBoundBox(); + box2.Enlarge(0.001f); if (!box2.IsInBox(rclPt0) || !box2.IsInBox(rclPt1)) return 0; diff --git a/src/Mod/Mesh/App/Core/Elements.h b/src/Mod/Mesh/App/Core/Elements.h index a0af29e49c..513c4adacb 100644 --- a/src/Mod/Mesh/App/Core/Elements.h +++ b/src/Mod/Mesh/App/Core/Elements.h @@ -546,6 +546,10 @@ public: /** Apply a transformation on the triangle. */ void Transform(const Base::Matrix4D&); + /** + * Checks if the two triangles are coplanar. + */ + bool IsCoplanar(const MeshGeomFacet &facet) const; protected: Base::Vector3f _clNormal; /**< Normal of the facet. */ diff --git a/src/Mod/Mesh/App/MeshTestsApp.py b/src/Mod/Mesh/App/MeshTestsApp.py index d467b1e016..4c32fc6570 100644 --- a/src/Mod/Mesh/App/MeshTestsApp.py +++ b/src/Mod/Mesh/App/MeshTestsApp.py @@ -226,6 +226,59 @@ class MeshGeoTestCases(unittest.TestCase): res=f1.intersect(f2) self.assertTrue(len(res) == 0) + def testIntersectionOfTransformedMesh(self): + self.planarMesh.append( [0.0,10.0,10.0] ) + self.planarMesh.append( [10.0,0.0,10.0] ) + self.planarMesh.append( [10.0,10.0,10.0] ) + self.planarMesh.append( [6.0,8.0,10.0] ) + self.planarMesh.append( [16.0,8.0,10.0] ) + self.planarMesh.append( [6.0,18.0,10.0] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + + mat = Base.Matrix() + mat.rotateX(1.0) + mat.rotateY(1.0) + mat.rotateZ(1.0) + planarMeshObject.transformGeometry(mat) + + f1 = planarMeshObject.Facets[0] + f2 = planarMeshObject.Facets[1] + res=f1.intersect(f2) + self.assertEqual(len(res), 2) + + def testIntersectionOfParallelTriangles(self): + self.planarMesh.append( [0.0,10.0,10.0] ) + self.planarMesh.append( [10.0,0.0,10.0] ) + self.planarMesh.append( [10.0,10.0,10.0] ) + self.planarMesh.append( [6.0,8.0,10.1] ) + self.planarMesh.append( [16.0,8.0,10.1] ) + self.planarMesh.append( [6.0,18.0,10.1] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + + mat = Base.Matrix() + mat.rotateX(1.0) + mat.rotateY(1.0) + mat.rotateZ(1.0) + planarMeshObject.transformGeometry(mat) + + f1 = planarMeshObject.Facets[0] + f2 = planarMeshObject.Facets[1] + res=f1.intersect(f2) + self.assertTrue(len(res) == 0) + + def testIntersectionOnEdge(self): + self.planarMesh.append( [5.0, -1.9371663331985474, 0.49737977981567383] ) + self.planarMesh.append( [4.0, -1.9371663331985474, 0.49737977981567383] ) + self.planarMesh.append( [5.0, -1.9842294454574585, 0.25066646933555603] ) + self.planarMesh.append( [4.6488823890686035, -1.7827962636947632, 0.4577442705631256] ) + self.planarMesh.append( [4.524135112762451, -2.0620131492614746, 0.5294350385665894] ) + self.planarMesh.append( [4.6488823890686035, -1.8261089324951172, 0.23069120943546295] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + f1 = planarMeshObject.Facets[0] + f2 = planarMeshObject.Facets[1] + res = f1.intersect(f2) + self.assertEqual(len(res), 2) + def testIntersectionCoplanar(self): self.planarMesh.append( [0.,10.,10.] ) self.planarMesh.append( [10.,0.,10.] ) From b93aa77a1c67c2990c7dd76a8752b5ed6d48a847 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 20 Oct 2021 21:57:51 +0200 Subject: [PATCH 008/138] Gui: move handling of failed document saving to a function to reduce code duplication --- src/Gui/Document.cpp | 90 +++++++++++++++++++++++--------------------- src/Gui/Document.h | 2 + 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index c4fdf1a943..589deecede 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -405,13 +405,16 @@ bool Document::setEdit(Gui::ViewProvider* p, int ModNum, const char *subname) d->_editViewProviderParent = vp; d->_editSubElement.clear(); d->_editSubname.clear(); - if(subname) { + + if (subname) { const char *element = Data::ComplexGeoData::findElementName(subname); - if(element) { + if (element) { d->_editSubname = std::string(subname,element-subname); d->_editSubElement = element; - }else + } + else { d->_editSubname = subname; + } } auto sobjs = obj->getSubObjectList(subname); @@ -1096,6 +1099,31 @@ static bool checkCanonicalPath(const std::map &docs) return ret == QMessageBox::Yes; } +bool Document::askIfSavingFailed(const QString& error) +{ + int ret = QMessageBox::question( + getMainWindow(), + QObject::tr("Could not save document"), + QObject::tr("There was an issue trying to save the file. " + "This may be because some of the parent folders do not exist, " + "or you do not have sufficient permissions, " + "or for other reasons. Error details:\n\n\"%1\"\n\n" + "Would you like to save the file with a different name?") + .arg(error), + QMessageBox::Yes, QMessageBox::No); + + if (ret == QMessageBox::No) { + // TODO: Understand what exactly is supposed to be returned here + getMainWindow()->showMessage(QObject::tr("Saving aborted"), 2000); + return false; + } + else if (ret == QMessageBox::Yes) { + return saveAs(); + } + + return false; +} + /// Save the document bool Document::save(void) { @@ -1105,7 +1133,7 @@ bool Document::save(void) std::map dmap; try { docs = getDocument()->getDependentDocuments(); - for(auto it=docs.begin(); it!=docs.end();) { + for (auto it=docs.begin(); it!=docs.end();) { App::Document *doc = *it; if (doc == getDocument()) { dmap[doc] = doc->mustExecute(); @@ -1123,18 +1151,21 @@ bool Document::save(void) dmap[doc] = doc->mustExecute(); ++it; } - }catch(const Base::RuntimeError &e) { + } + catch (const Base::RuntimeError &e) { FC_ERR(e.what()); docs = {getDocument()}; dmap.clear(); dmap[getDocument()] = getDocument()->mustExecute(); } - if(docs.size()>1) { + + if (docs.size()>1) { int ret = QMessageBox::question(getMainWindow(), QObject::tr("Save dependent files"), QObject::tr("The file contains external dependencies. " "Do you want to save the dependent files, too?"), QMessageBox::Yes,QMessageBox::No); + if (ret != QMessageBox::Yes) { docs = {getDocument()}; dmap.clear(); @@ -1147,35 +1178,22 @@ bool Document::save(void) Gui::WaitCursor wc; // save all documents - for(auto doc : docs) { + for (auto doc : docs) { // Changed 'mustExecute' status may be triggered by saving external document - if(!dmap[doc] && doc->mustExecute()) { + if (!dmap[doc] && doc->mustExecute()) { App::AutoTransaction trans("Recompute"); Command::doCommand(Command::Doc,"App.getDocument(\"%s\").recompute()",doc->getName()); } + Command::doCommand(Command::Doc,"App.getDocument(\"%s\").save()",doc->getName()); auto gdoc = Application::Instance->getDocument(doc); - if(gdoc) gdoc->setModified(false); + if (gdoc) + gdoc->setModified(false); } } catch (const Base::FileException& e) { - int ret = QMessageBox::question( - getMainWindow(), - QObject::tr("Could not save document"), - QObject::tr("There was an issue trying to save the file. " - "This may be because some of the parent folders do not exist, " - "or you do not have sufficient permissions, " - "or for other reasons. Error details:\n\n\"%1\"\n\n" - "Would you like to save the file with a different name?") - .arg(QString::fromUtf8(e.what())), - QMessageBox::Yes, QMessageBox::No); - if (ret == QMessageBox::No) { - // TODO: Understand what exactly is supposed to be returned here - getMainWindow()->showMessage(QObject::tr("Saving aborted"), 2000); - return false; - } else if (ret == QMessageBox::Yes) { - return saveAs(); - } + e.ReportException(); + return askIfSavingFailed(QString::fromUtf8(e.what())); } catch (const Base::Exception& e) { QMessageBox::critical(getMainWindow(), QObject::tr("Saving document failed"), @@ -1198,6 +1216,7 @@ bool Document::saveAs(void) QString fn = FileDialog::getSaveFileName(getMainWindow(), QObject::tr("Save %1 Document").arg(exe), QString::fromUtf8(getDocument()->FileName.getValue()), QString::fromLatin1("%1 %2 (*.FCStd)").arg(exe).arg(QObject::tr("Document"))); + if (!fn.isEmpty()) { QFileInfo fi; fi.setFile(fn); @@ -1217,23 +1236,8 @@ bool Document::saveAs(void) getMainWindow()->appendRecentFile(fi.filePath()); } catch (const Base::FileException& e) { - int ret = QMessageBox::question( - getMainWindow(), - QObject::tr("Could not save document"), - QObject::tr("There was an issue trying to save the file. " - "This may be because some of the parent folders do not exist, " - "or you do not have sufficient permissions, " - "or for other reasons. Error details:\n\n\"%1\"\n\n" - "Would you like to save the file with a different name?") - .arg(QString::fromUtf8(e.what())), - QMessageBox::Yes, QMessageBox::No); - if (ret == QMessageBox::No) { - // TODO: Understand what exactly is supposed to be returned here - getMainWindow()->showMessage(QObject::tr("Saving aborted"), 2000); - return false; - } else if (ret == QMessageBox::Yes) { - return saveAs(); - } + e.ReportException(); + return askIfSavingFailed(QString::fromUtf8(e.what())); } catch (const Base::Exception& e) { QMessageBox::critical(getMainWindow(), QObject::tr("Saving document failed"), diff --git a/src/Gui/Document.h b/src/Gui/Document.h index b24887aaf8..a7884670ed 100644 --- a/src/Gui/Document.h +++ b/src/Gui/Document.h @@ -306,6 +306,8 @@ private: /// Check other documents for the same transaction ID bool checkTransactionID(bool undo, int iSteps); + /// Ask for user interaction if saving has failed + bool askIfSavingFailed(const QString&); struct DocumentP* d; static int _iDocCount; From 5da633d59fe0ce9a2000f56820912335d5eb3ae3 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 20 Oct 2021 22:21:52 +0200 Subject: [PATCH 009/138] Gui: [skip ci] improve whitespaces --- src/Gui/View3DPy.cpp | 102 ++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index 50e8325c3a..2ca90e415f 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -243,7 +243,7 @@ PyObject *View3DInventorPy::method_varargs_ext_handler(PyObject *_self_and_name_ catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } } @@ -307,7 +307,7 @@ Py::Object View3DInventorPy::message(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } return Py::None(); @@ -328,7 +328,7 @@ Py::Object View3DInventorPy::fitAll(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } return Py::None(); @@ -474,7 +474,7 @@ Py::Object View3DInventorPy::viewBottom(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -495,7 +495,7 @@ Py::Object View3DInventorPy::viewFront(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -516,7 +516,7 @@ Py::Object View3DInventorPy::viewLeft(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -537,7 +537,7 @@ Py::Object View3DInventorPy::viewRear(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -558,7 +558,7 @@ Py::Object View3DInventorPy::viewRight(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -579,7 +579,7 @@ Py::Object View3DInventorPy::viewTop(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -600,7 +600,7 @@ Py::Object View3DInventorPy::viewIsometric(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -621,7 +621,7 @@ Py::Object View3DInventorPy::viewDimetric(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -642,7 +642,7 @@ Py::Object View3DInventorPy::viewTrimetric(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -733,7 +733,7 @@ Py::Object View3DInventorPy::viewDefaultOrientation(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -759,7 +759,7 @@ Py::Object View3DInventorPy::viewRotateLeft(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -785,7 +785,7 @@ Py::Object View3DInventorPy::viewRotateRight(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -806,7 +806,7 @@ Py::Object View3DInventorPy::zoomIn(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -827,7 +827,7 @@ Py::Object View3DInventorPy::zoomOut(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -1073,7 +1073,7 @@ Py::Object View3DInventorPy::getCamera(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } } @@ -1092,7 +1092,7 @@ Py::Object View3DInventorPy::getViewDirection(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } } @@ -1126,7 +1126,7 @@ Py::Object View3DInventorPy::setViewDirection(const Py::Tuple& args) catch (const std::exception& e) { throw Py::RuntimeError(e.what()); } - catch(...) { + catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } @@ -1415,23 +1415,23 @@ Py::Object View3DInventorPy::getObjectInfo(const Py::Tuple& args) Gui::Document* doc = _view->getViewer()->getDocument(); ViewProvider *vp = doc ? doc->getViewProviderByPathFromHead(Point->getPath()) : _view->getViewer()->getViewProviderByPath(Point->getPath()); - if(vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { - if(!vp->isSelectable()) + if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { + if (!vp->isSelectable()) return ret; ViewProviderDocumentObject* vpd = static_cast(vp); - if(vp->useNewSelectionModel()) { + if (vp->useNewSelectionModel()) { std::string subname; - if(!vp->getElementPicked(Point,subname)) + if (!vp->getElementPicked(Point,subname)) return ret; auto obj = vpd->getObject(); - if(!obj) + if (!obj) return ret; - if(subname.size()) { + if (subname.size()) { std::pair elementName; auto sobj = App::GeoFeature::resolveElement(obj,subname.c_str(),elementName); - if(!sobj) + if (!sobj) return ret; - if(sobj!=obj) { + if (sobj != obj) { dict.setItem("ParentObject",Py::Object(obj->getPyObject(),true)); dict.setItem("SubName",Py::String(subname)); obj = sobj; @@ -1443,7 +1443,8 @@ Py::Object View3DInventorPy::getObjectInfo(const Py::Tuple& args) dict.setItem("Object", Py::String(obj->getNameInDocument())); dict.setItem("Component",Py::String(subname)); - } else { + } + else { dict.setItem("Document", Py::String(vpd->getObject()->getDocument()->getName())); dict.setItem("Object", @@ -1530,19 +1531,19 @@ Py::Object View3DInventorPy::getObjectsInfo(const Py::Tuple& args) if(!vp->isSelectable()) continue; ViewProviderDocumentObject* vpd = static_cast(vp); - if(vp->useNewSelectionModel()) { + if (vp->useNewSelectionModel()) { std::string subname; - if(!vp->getElementPicked(point,subname)) + if (!vp->getElementPicked(point,subname)) continue; auto obj = vpd->getObject(); - if(!obj) + if (!obj) continue; - if(subname.size()) { + if (subname.size()) { std::pair elementName; auto sobj = App::GeoFeature::resolveElement(obj,subname.c_str(),elementName); - if(!sobj) + if (!sobj) continue; - if(sobj!=obj) { + if (sobj != obj) { dict.setItem("ParentObject",Py::Object(obj->getPyObject(),true)); dict.setItem("SubName",Py::String(subname)); obj = sobj; @@ -1554,7 +1555,8 @@ Py::Object View3DInventorPy::getObjectsInfo(const Py::Tuple& args) dict.setItem("Object", Py::String(obj->getNameInDocument())); dict.setItem("Component",Py::String(subname)); - } else { + } + else { dict.setItem("Document", Py::String(vpd->getObject()->getDocument()->getName())); dict.setItem("Object", @@ -2492,21 +2494,23 @@ Py::Object View3DInventorPy::removeDraggerCallback(const Py::Tuple& args) Py::Object View3DInventorPy::setActiveObject(const Py::Tuple& args) { - PyObject* docObject = Py_None; - char* name; + PyObject* docObject = Py_None; + char* name; char *subname = 0; if (!PyArg_ParseTuple(args.ptr(), "s|Os", &name, &docObject, &subname)) - throw Py::Exception(); + throw Py::Exception(); - if (docObject == Py_None) - _view->setActiveObject(0, name); - else{ - if(!PyObject_TypeCheck(docObject, &App::DocumentObjectPy::Type)) + if (docObject == Py_None) { + _view->setActiveObject(0, name); + } + else { + if (!PyObject_TypeCheck(docObject, &App::DocumentObjectPy::Type)) throw Py::TypeError("Expect the second argument to be a document object or None"); - App::DocumentObject* obj = static_cast(docObject)->getDocumentObjectPtr(); - _view->setActiveObject(obj, name, subname); - } - return Py::None(); + App::DocumentObject* obj = static_cast(docObject)->getDocumentObjectPtr(); + _view->setActiveObject(obj, name, subname); + } + + return Py::None(); } Py::Object View3DInventorPy::getActiveObject(const Py::Tuple& args) @@ -2519,10 +2523,10 @@ Py::Object View3DInventorPy::getActiveObject(const Py::Tuple& args) App::DocumentObject *parent = 0; std::string subname; App::DocumentObject* obj = _view->getActiveObject(name,&parent,&subname); - if(!obj) + if (!obj) return Py::None(); - if(PyObject_IsTrue(resolve)) + if (PyObject_IsTrue(resolve)) return Py::asObject(obj->getPyObject()); return Py::TupleN( From a9002f421857d1276dc9d07e4b54208124ec79f4 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 20 Oct 2021 22:33:50 +0200 Subject: [PATCH 010/138] Gui: [skip ci] improve whitespaces --- src/Gui/ActiveObjectList.cpp | 94 ++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/src/Gui/ActiveObjectList.cpp b/src/Gui/ActiveObjectList.cpp index aff4d1b06b..2d7bf2184d 100644 --- a/src/Gui/ActiveObjectList.cpp +++ b/src/Gui/ActiveObjectList.cpp @@ -44,93 +44,107 @@ App::DocumentObject *ActiveObjectList::getObject(const ObjectInfo &info, bool re App::DocumentObject **parent, std::string *subname) const { - if(parent) *parent = info.obj; - if(subname) *subname = info.subname; + if (parent) + *parent = info.obj; + if (subname) + *subname = info.subname; auto obj = info.obj; - if(!obj || !obj->getNameInDocument()) - return 0; - if(info.subname.size()) { + if (!obj || !obj->getNameInDocument()) + return nullptr; + if (!info.subname.empty()) { obj = obj->getSubObject(info.subname.c_str()); - if(!obj) - return 0; + if (!obj) + return nullptr; } - return resolve?obj->getLinkedObject(true):obj; + + return resolve ? obj->getLinkedObject(true) : obj; } void ActiveObjectList::setHighlight(const ObjectInfo &info, HighlightMode mode, bool enable) { - auto obj = getObject(info,false); - if(!obj) return; + auto obj = getObject(info, false); + if (!obj) + return; auto vp = dynamic_cast(Application::Instance->getViewProvider(obj)); - if(!vp) return; + if (!vp) + return; if (TreeParams::Instance()->TreeActiveAutoExpand()) { vp->getDocument()->signalExpandObject(*vp, enable ? TreeItemMode::ExpandPath : TreeItemMode::CollapseItem, info.obj, info.subname.c_str()); } - vp->getDocument()->signalHighlightObject(*vp, mode,enable,info.obj,info.subname.c_str()); + vp->getDocument()->signalHighlightObject(*vp, mode, enable, info.obj, info.subname.c_str()); } Gui::ActiveObjectList::ObjectInfo Gui::ActiveObjectList::getObjectInfo(App::DocumentObject *obj, const char *subname) const { ObjectInfo info; - info.obj = 0; - if(!obj || !obj->getNameInDocument()) + info.obj = nullptr; + if (!obj || !obj->getNameInDocument()) return info; - if(subname) { + + if (subname) { info.obj = obj; info.subname = subname; - }else{ + } + else { // If the input object is not from this document, it must be brought in // by some link type object of this document. We only accept the object // if we can find such object in the current selection. auto sels = Gui::Selection().getSelection(_Doc->getDocument()->getName(),false); - for(auto &sel : sels) { - if(sel.pObject == obj || sel.pObject->getLinkedObject(true)==obj) { + for (auto &sel : sels) { + if (sel.pObject == obj || sel.pObject->getLinkedObject(true)==obj) { info.obj = sel.pObject; break; } - for(auto dot=strchr(sel.SubName,'.');dot;dot=strchr(dot+1,'.')) { + + for (auto dot=strchr(sel.SubName,'.');dot;dot=strchr(dot+1,'.')) { std::string subname(sel.SubName,dot-sel.SubName+1); auto sobj = sel.pObject->getSubObject(subname.c_str()); - if(!sobj) break; - if(sobj == obj || sobj->getLinkedObject(true) == obj) { + if (!sobj) + break; + if (sobj == obj || sobj->getLinkedObject(true) == obj) { info.obj = sel.pObject; info.subname = subname; break; } } - if(info.obj) break; + + if (info.obj) + break; } - if(!info.obj && obj->getDocument()==_Doc->getDocument()) + + if (!info.obj && obj->getDocument()==_Doc->getDocument()) info.obj = obj; } return info; } bool Gui::ActiveObjectList::hasObject(App::DocumentObject *obj, - const char *name, const char *subname) const + const char *name, const char *subname) const { auto it = _ObjectMap.find(name); - if(it==_ObjectMap.end()) + if (it == _ObjectMap.end()) return false; - auto info = getObjectInfo(obj,subname); - return info.obj==it->second.obj && info.subname==it->second.subname; + auto info = getObjectInfo(obj, subname); + return info.obj == it->second.obj && info.subname == it->second.subname; } void Gui::ActiveObjectList::setObject(App::DocumentObject* obj, const char* name, - const char *subname, const Gui::HighlightMode& mode) + const char *subname, const Gui::HighlightMode& mode) { auto it = _ObjectMap.find(name); - if(it!=_ObjectMap.end()) { - setHighlight(it->second,mode,false); + if (it!=_ObjectMap.end()) { + setHighlight(it->second, mode, false); _ObjectMap.erase(it); } - if(!obj) return; + + if (!obj) + return; auto info = getObjectInfo(obj,subname); - if(!info.obj) { + if (!info.obj) { FC_ERR("Cannot set active object " << obj->getFullName() << '.' << (subname?subname:"") << " in document '" << _Doc->getDocument()->getName() @@ -139,7 +153,7 @@ void Gui::ActiveObjectList::setObject(App::DocumentObject* obj, const char* name } _ObjectMap[name] = info; - setHighlight(info,mode,true); + setHighlight(info, mode, true); } bool Gui::ActiveObjectList::hasObject(const char*name)const @@ -149,13 +163,11 @@ bool Gui::ActiveObjectList::hasObject(const char*name)const void ActiveObjectList::objectDeleted(const ViewProviderDocumentObject &vp) { - //maybe boost::bimap or boost::multi_index - for (auto it = _ObjectMap.begin(); it != _ObjectMap.end(); ++it) - { - if (it->second.obj == vp.getObject()) - { - _ObjectMap.erase(it); - return; + //maybe boost::bimap or boost::multi_index + for (auto it = _ObjectMap.begin(); it != _ObjectMap.end(); ++it) { + if (it->second.obj == vp.getObject()) { + _ObjectMap.erase(it); + return; + } } - } } From 42e8287699fb031f76b55503696fd6e8d4b124fe Mon Sep 17 00:00:00 2001 From: luz paz Date: Wed, 20 Oct 2021 18:18:33 -0400 Subject: [PATCH 011/138] Fix various typos Found via `codespell -q 3 -L aci,ake,aline,alle,alledges,alocation,als,ang,anid,apoints,ba,beginn,behaviour,bloaded,bottome,byteorder,calculater,cancelled,cancelling,cas,cascade,centimetre,childs,colour,colours,commen,connexion,currenty,dof,doubleclick,dum,eiter,elemente,ende,feld,finde,findf,freez,hist,iff,indicies,initialisation,initialise,initialised,initialises,initialisiert,inout,ist,kilometre,lod,mantatory,methode,metres,millimetre,modell,nd,noe,normale,normaly,nto,numer,oder,ontop,orgin,orginx,orginy,ot,pard,parms,pres,programm,que,recurrance,rougly,seperator,serie,sinc,strack,substraction,te,thist,thru,tread,uint,unter,vertexes,wallthickness,whitespaces -S ./.git,*.po,*.ts,./ChangeLog.txt,./src/3rdParty,./src/Mod/Assembly/App/opendcm,./src/CXX,./src/zipios++,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL,./src/WindowsInstaller,./src/Doc/FreeCAD.uml,./build/doc/SourceDocu` --- src/Mod/Draft/Resources/ui/preferences-dwg.ui | 2 +- src/Mod/Draft/draftgeoutils/wires.py | 2 +- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 2 +- src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp | 2 +- src/Mod/Spreadsheet/App/SheetPy.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mod/Draft/Resources/ui/preferences-dwg.ui b/src/Mod/Draft/Resources/ui/preferences-dwg.ui index b52ea78448..2f6e1572fd 100644 --- a/src/Mod/Draft/Resources/ui/preferences-dwg.ui +++ b/src/Mod/Draft/Resources/ui/preferences-dwg.ui @@ -38,7 +38,7 @@ - This is the method FreeCAD will use to convert DWG files to DXF. If "Automatic" is chosen, FreeCAD will try to find one of the following convertors in the same order as they are shown here. If FreeCAD is unable to find any, you might need to choose a specific convertor and indicate its path here under. Choose the "dwg2dxf" utility if using LibreDWG, "ODAFileConverter" if using the ODA file converter, or the "dwg2dwg" utility if using the pro version of QCAD. + This is the method FreeCAD will use to convert DWG files to DXF. If "Automatic" is chosen, FreeCAD will try to find one of the following converters in the same order as they are shown here. If FreeCAD is unable to find any, you might need to choose a specific converter and indicate its path here under. Choose the "dwg2dxf" utility if using LibreDWG, "ODAFileConverter" if using the ODA file converter, or the "dwg2dwg" utility if using the pro version of QCAD. DWGConversion diff --git a/src/Mod/Draft/draftgeoutils/wires.py b/src/Mod/Draft/draftgeoutils/wires.py index fabc5c2601..683f87b037 100644 --- a/src/Mod/Draft/draftgeoutils/wires.py +++ b/src/Mod/Draft/draftgeoutils/wires.py @@ -354,7 +354,7 @@ def removeInterVertices(wire): def cleanProjection(shape, tessellate=True, seglength=0.05): - """Return a compound of edges, optionally tesselate ellipses, splines + """Return a compound of edges, optionally tessellate ellipses, splines and bezcurves. The function was formerly used to workaround bugs in the projection diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 716cdba5ea..f6a4f0b57b 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -7137,7 +7137,7 @@ bool CmdSketcherConstrainSnellsLaw::isActive(void) DEF_STD_CMD_A(CmdSketcherConstrainInternalAlignment) // NOTE: This command is deprecated. Nobody seriously uses today manual creation of an internal alignment constraint -// The only reason this code remains is the extremelly unlikely scenario that some user macro may rely on it. +// The only reason this code remains is the extremely unlikely scenario that some user macro may rely on it. CmdSketcherConstrainInternalAlignment::CmdSketcherConstrainInternalAlignment() :Command("Sketcher_ConstrainInternalAlignment") { diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp index 7e3d84711e..5dda156b78 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp @@ -1002,7 +1002,7 @@ void TaskSketcherConstrains::onSelectionChanged(const Gui::SelectionChanges& msg if(geoid != Sketcher::Constraint::GeoUndef && pointpos == Sketcher::none){ // It is not possible to update on single addition/removal of a geometric element, // as one removal may imply removing a constraint that should be added by a different element - // that is still selected. The necessary checks outweight a full rebuild of the filter. + // that is still selected. The necessary checks outweigh a full rebuild of the filter. updateAssociatedConstraintsFilter(); } } diff --git a/src/Mod/Spreadsheet/App/SheetPy.xml b/src/Mod/Spreadsheet/App/SheetPy.xml index a2c4bf6330..d968809ea7 100644 --- a/src/Mod/Spreadsheet/App/SheetPy.xml +++ b/src/Mod/Spreadsheet/App/SheetPy.xml @@ -176,7 +176,7 @@ recomputeCells(from, to=None) Manually recompute cells in the given range with the given order without -following depedency order. +following dependency order. From b24ac5f2f8871333040fc170e5ed53ac05eb606a Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 21 Oct 2021 15:50:07 +0200 Subject: [PATCH 012/138] App: [skip ci] add get() method to WeakPtrT class --- src/App/DocumentObserver.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/App/DocumentObserver.h b/src/App/DocumentObserver.h index c92cb3155b..c32c780a01 100644 --- a/src/App/DocumentObserver.h +++ b/src/App/DocumentObserver.h @@ -387,6 +387,11 @@ public: bool operator!= (const WeakPtrT& p) const { return ptr != p.ptr; } + /*! Get a pointer to the object or 0 if it doesn't exist any more. */ + T* get() const noexcept + { + return ptr.get(); + } private: // disable From 68e4806788a2e2001a34cfe2e6f91f37de7cc75e Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 21 Oct 2021 15:50:18 +0200 Subject: [PATCH 013/138] Gui: [skip ci] add get() method to WeakPtrT class --- src/Gui/DocumentObserver.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Gui/DocumentObserver.h b/src/Gui/DocumentObserver.h index 6608fa9df7..63a20df9b4 100644 --- a/src/Gui/DocumentObserver.h +++ b/src/Gui/DocumentObserver.h @@ -274,6 +274,11 @@ public: bool operator!= (const WeakPtrT& p) const { return ptr != p.ptr; } + /*! Get a pointer to the object or 0 if it doesn't exist any more. */ + T* get() const noexcept + { + return ptr.get(); + } private: // disable From 796efc4c6b76b93ea36905eb013b79bb197dbbef Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 21 Oct 2021 16:22:11 +0200 Subject: [PATCH 014/138] Sketcher: fix segmentation fault when using sketch validation dialog after document has been closed Therefore replace the raw pointer of SketchObject with the template class WeakPtrT. This class will be notified as soon as its handled object will be deleted. --- .../Sketcher/Gui/TaskSketcherValidation.cpp | 50 +++++++++++++++++-- src/Mod/Sketcher/Gui/TaskSketcherValidation.h | 3 +- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherValidation.cpp b/src/Mod/Sketcher/Gui/TaskSketcherValidation.cpp index afd57cf1ba..11b9eb0cd2 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherValidation.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherValidation.cpp @@ -107,6 +107,9 @@ void SketcherValidation::changeEvent(QEvent *e) void SketcherValidation::on_findButton_clicked() { + if (sketch.expired()) + return; + double prec = Precision::Confusion(); bool ok; double conv; @@ -150,6 +153,9 @@ void SketcherValidation::on_findButton_clicked() void SketcherValidation::on_fixButton_clicked() { + if (sketch.expired()) + return; + // undo command open App::Document* doc = sketch->getDocument(); doc->openTransaction("add coincident constraint"); @@ -167,6 +173,9 @@ void SketcherValidation::on_fixButton_clicked() void SketcherValidation::on_highlightButton_clicked() { + if (sketch.expired()) + return; + std::vector points; points = sketchAnalyser.getOpenVertices(); @@ -178,6 +187,9 @@ void SketcherValidation::on_highlightButton_clicked() void SketcherValidation::on_findConstraint_clicked() { + if (sketch.expired()) + return; + if (sketch->evaluateConstraints()) { QMessageBox::information(this, tr("No invalid constraints"), tr("No invalid constraints found")); @@ -192,12 +204,18 @@ void SketcherValidation::on_findConstraint_clicked() void SketcherValidation::on_fixConstraint_clicked() { + if (sketch.expired()) + return; + sketch->validateConstraints(); ui->fixConstraint->setEnabled(false); } void SketcherValidation::on_findReversed_clicked() { + if (sketch.expired()) + return; + std::vector points; const std::vector& geom = sketch->getExternalGeometry(); for (std::size_t i=0; igetDocument(); doc->openTransaction("Sketch porting"); @@ -255,6 +276,9 @@ void SketcherValidation::on_swapReversed_clicked() void SketcherValidation::on_orientLockEnable_clicked() { + if (sketch.expired()) + return; + App::Document* doc = sketch->getDocument(); doc->openTransaction("Constraint orientation lock"); @@ -269,6 +293,9 @@ void SketcherValidation::on_orientLockEnable_clicked() void SketcherValidation::on_orientLockDisable_clicked() { + if (sketch.expired()) + return; + App::Document* doc = sketch->getDocument(); doc->openTransaction("Constraint orientation unlock"); @@ -284,6 +311,9 @@ void SketcherValidation::on_orientLockDisable_clicked() void SketcherValidation::on_delConstrExtr_clicked() { + if (sketch.expired()) + return; + int reply; reply = QMessageBox::question(this, tr("Delete constraints to external geom."), @@ -337,21 +367,28 @@ void SketcherValidation::showPoints(const std::vector& pts) } coords->point.finishEditing(); - Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(sketch); - vp->getRoot()->addChild(coincidenceRoot); + if (!sketch.expired()) { + Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(sketch.get()); + vp->getRoot()->addChild(coincidenceRoot); + } } void SketcherValidation::hidePoints() { if (coincidenceRoot) { - Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(sketch); - vp->getRoot()->removeChild(coincidenceRoot); - coincidenceRoot = 0; + if (!sketch.expired()) { + Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(sketch.get()); + vp->getRoot()->removeChild(coincidenceRoot); + } + coincidenceRoot = nullptr; } } void SketcherValidation::on_findDegenerated_clicked() { + if (sketch.expired()) + return; + double prec = Precision::Confusion(); int count = sketchAnalyser.detectDegeneratedGeometries(prec); @@ -369,6 +406,9 @@ void SketcherValidation::on_findDegenerated_clicked() void SketcherValidation::on_fixDegenerated_clicked() { + if (sketch.expired()) + return; + // undo command open App::Document* doc = sketch->getDocument(); doc->openTransaction("Remove degenerated geometry"); diff --git a/src/Mod/Sketcher/Gui/TaskSketcherValidation.h b/src/Mod/Sketcher/Gui/TaskSketcherValidation.h index 127a561e65..c787587385 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherValidation.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherValidation.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,7 @@ private: private: std::unique_ptr ui; - Sketcher::SketchObject* sketch; + App::WeakPtrT sketch; Sketcher::SketchAnalysis sketchAnalyser; SoGroup* coincidenceRoot; }; From ee311b08354561ffe068357b30515bd725e5de0f Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 21 Oct 2021 18:23:03 +0200 Subject: [PATCH 015/138] Surface: [skip ci] only try to build surface if at least two boundary curves are used --- src/Mod/Surface/App/FeatureFilling.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Mod/Surface/App/FeatureFilling.cpp b/src/Mod/Surface/App/FeatureFilling.cpp index 2b3b9a4a20..d0b17c2362 100644 --- a/src/Mod/Surface/App/FeatureFilling.cpp +++ b/src/Mod/Surface/App/FeatureFilling.cpp @@ -298,6 +298,7 @@ App::DocumentObjectExecReturn *Filling::execute(void) } // Add the constraints of border curves/faces (bound) + int numBoundaries = BoundaryEdges.getSize(); addConstraints(builder, BoundaryEdges, BoundaryFaces, BoundaryOrder, Standard_True); // Add additional edge constraints if available (unbound) @@ -316,7 +317,8 @@ App::DocumentObjectExecReturn *Filling::execute(void) } //Build the face - builder.Build(); + if (numBoundaries > 1) + builder.Build(); if (!builder.IsDone()) { Standard_Failure::Raise("Failed to create a face from constraints"); } @@ -327,7 +329,6 @@ App::DocumentObjectExecReturn *Filling::execute(void) return App::DocumentObject::StdReturn; } catch (Standard_Failure& e) { - return new App::DocumentObjectExecReturn(e.GetMessageString()); } } From 954481bc2b61d439e9160028472f7f3815e67066 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Thu, 21 Oct 2021 21:40:55 +0200 Subject: [PATCH 016/138] Draft: Fix Draft_Edit sketch issue Draft_Edit did not work on wall sketch after reopening file --- .../draftguitools/gui_edit_sketcher_objects.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py b/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py index 20b94192f7..e6211d6b2b 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py @@ -49,8 +49,11 @@ class SketcherSketchObjectGuiTools(GuiTools): 0 : startpoint 1 : endpoint """ + import Part editpoints = [] - if obj.GeometryCount == 1: + if (obj.ConstraintCount == 0 + and obj.GeometryCount == 1 + and type(obj.Geometry[0]) == Part.LineSegment): editpoints.append(obj.getPoint(0,1)) editpoints.append(obj.getPoint(0,2)) return editpoints @@ -60,7 +63,6 @@ class SketcherSketchObjectGuiTools(GuiTools): App.Console.PrintWarning(_wrn + "\n") return None - def update_object_from_edit_points(self, obj, node_idx, v, alt_edit_mode=0): """Move a single line sketch vertex a certain displacement. @@ -69,11 +71,12 @@ class SketcherSketchObjectGuiTools(GuiTools): 0 : startpoint 1 : endpoint """ - import Sketcher + line = obj.Geometry[0] if node_idx == 0: - obj.movePoint(0, 1, v) + line.StartPoint = v elif node_idx == 1: - obj.movePoint(0, 2, v) + line.EndPoint = v + obj.Geometry = [line] obj.recompute() ## @} From a5411da5b96241c65835d55caafd73d60e669532 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 21 Oct 2021 21:45:52 +0200 Subject: [PATCH 017/138] Gui: code-refactoring of document recovery handling to reduce code duplication --- src/Gui/Application.cpp | 76 +------------------ src/Gui/DocumentRecovery.cpp | 143 +++++++++++++++++++++++++++-------- src/Gui/DocumentRecovery.h | 19 +++++ 3 files changed, 132 insertions(+), 106 deletions(-) diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 99500fa9c4..fa233c9ce9 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -2416,80 +2416,8 @@ void Application::setStyleSheet(const QString& qssFile, bool tiledBackground) void Application::checkForPreviousCrashes() { - QDir tmp = QString::fromUtf8(App::Application::getTempPath().c_str()); - tmp.setNameFilters(QStringList() << QString::fromLatin1("*.lock")); - tmp.setFilter(QDir::Files); - - QList restoreDocFiles; - QString exeName = QString::fromLatin1(App::GetApplication().getExecutableName()); - QList locks = tmp.entryInfoList(); - for (QList::iterator it = locks.begin(); it != locks.end(); ++it) { - QString bn = it->baseName(); - // ignore the lock file for this instance - QString pid = QString::number(QCoreApplication::applicationPid()); - if (bn.startsWith(exeName) && bn.indexOf(pid) < 0) { - QString fn = it->absoluteFilePath(); - boost::interprocess::file_lock flock((const char*)fn.toLocal8Bit()); - if (flock.try_lock()) { - // OK, this file is a leftover from a previous crash - QString crashed_pid = bn.mid(exeName.length()+1); - // search for transient directories with this PID - QString filter; - QTextStream str(&filter); - str << exeName << "_Doc_*_" << crashed_pid; - tmp.setNameFilters(QStringList() << filter); - tmp.setFilter(QDir::Dirs); - QList dirs = tmp.entryInfoList(); - if (dirs.isEmpty()) { - // delete the lock file immediately if no transient directories are related - tmp.remove(fn); - } - else { - int countDeletedDocs = 0; - QString recovery_files = QString::fromLatin1("fc_recovery_files"); - for (QList::iterator it = dirs.begin(); it != dirs.end(); ++it) { - QDir doc_dir(it->absoluteFilePath()); - doc_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries); - uint entries = doc_dir.entryList().count(); - if (entries == 0) { - // in this case we can delete the transient directory because - // we cannot do anything - if (tmp.rmdir(it->filePath())) - countDeletedDocs++; - } - // search for the existence of a recovery file - else if (doc_dir.exists(QLatin1String("fc_recovery_file.xml"))) { - // store the transient directory in case it's not empty - restoreDocFiles << *it; - } - // search for the 'fc_recovery_files' sub-directory and check that it's the only entry - else if (entries == 1 && doc_dir.exists(recovery_files)) { - // if the sub-directory is empty delete the transient directory - QDir rec_dir(doc_dir.absoluteFilePath(recovery_files)); - rec_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries); - if (rec_dir.entryList().isEmpty()) { - doc_dir.rmdir(recovery_files); - if (tmp.rmdir(it->filePath())) - countDeletedDocs++; - } - } - } - - // all directories corresponding to the lock file have been deleted - // so delete the lock file, too - if (countDeletedDocs == dirs.size()) { - tmp.remove(fn); - } - } - } - } - } - - if (!restoreDocFiles.isEmpty()) { - Gui::Dialog::DocumentRecovery dlg(restoreDocFiles, Gui::getMainWindow()); - if (dlg.foundDocuments()) - dlg.exec(); - } + Gui::Dialog::DocumentRecoveryFinder finder; + finder.checkForPreviousCrashes(); } App::Document *Application::reopen(App::Document *doc) { diff --git a/src/Gui/DocumentRecovery.cpp b/src/Gui/DocumentRecovery.cpp index 44e7f74116..4f2d60006e 100644 --- a/src/Gui/DocumentRecovery.cpp +++ b/src/Gui/DocumentRecovery.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,7 @@ FC_LOG_LEVEL_INIT("Gui",true,true) using namespace Gui; using namespace Gui::Dialog; +namespace sp = std::placeholders; // taken from the script doctools.py std::string DocumentRecovery::doctools = @@ -553,41 +555,20 @@ void DocumentRecovery::on_buttonCleanup_clicked() d_ptr->ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); d_ptr->ui.buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true); - QDir tmp = QString::fromUtf8(App::Application::getTempPath().c_str()); - tmp.setNameFilters(QStringList() << QString::fromLatin1("*.lock")); - tmp.setFilter(QDir::Files); + DocumentRecoveryHandler handler; + handler.checkForPreviousCrashes(std::bind(&DocumentRecovery::cleanup, this, sp::_1, sp::_2, sp::_3)); + QMessageBox::information(this, tr("Finished"), tr("Transient directories deleted.")); +} - QString exeName = QString::fromLatin1(App::GetApplication().getExecutableName()); - QList locks = tmp.entryInfoList(); - for (QList::iterator it = locks.begin(); it != locks.end(); ++it) { - QString bn = it->baseName(); - // ignore the lock file for this instance - QString pid = QString::number(QCoreApplication::applicationPid()); - if (bn.startsWith(exeName) && bn.indexOf(pid) < 0) { - QString fn = it->absoluteFilePath(); - boost::interprocess::file_lock flock((const char*)fn.toLocal8Bit()); - if (flock.try_lock()) { - // OK, this file is a leftover from a previous crash - QString crashed_pid = bn.mid(exeName.length()+1); - // search for transient directories with this PID - QString filter; - QTextStream str(&filter); - str << exeName << "_Doc_*_" << crashed_pid; - tmp.setNameFilters(QStringList() << filter); - tmp.setFilter(QDir::Dirs); - QList dirs = tmp.entryInfoList(); - if (!dirs.isEmpty()) { - for (QList::iterator jt = dirs.begin(); jt != dirs.end(); ++jt) { - clearDirectory(*jt); - tmp.rmdir(jt->fileName()); - } - } - tmp.remove(it->fileName()); - } +void DocumentRecovery::cleanup(QDir& tmp, const QList& dirs, const QString& lockFile) +{ + if (!dirs.isEmpty()) { + for (QList::const_iterator jt = dirs.cbegin(); jt != dirs.cend(); ++jt) { + clearDirectory(*jt); + tmp.rmdir(jt->fileName()); } } - - QMessageBox::information(this, tr("Finished"), tr("Transient directories deleted.")); + tmp.remove(lockFile); } void DocumentRecovery::clearDirectory(const QFileInfo& dir) @@ -613,4 +594,102 @@ void DocumentRecovery::clearDirectory(const QFileInfo& dir) } } +// ---------------------------------------------------------------------------- + +void DocumentRecoveryFinder::checkForPreviousCrashes() +{ + DocumentRecoveryHandler handler; + handler.checkForPreviousCrashes(std::bind(&DocumentRecoveryFinder::checkDocumentDirs, this, sp::_1, sp::_2, sp::_3)); + + showRecoveryDialogIfNeeded(); +} + +void DocumentRecoveryFinder::checkDocumentDirs(QDir& tmp, const QList& dirs, const QString& fn) +{ + if (dirs.isEmpty()) { + // delete the lock file immediately if no transient directories are related + tmp.remove(fn); + } + else { + int countDeletedDocs = 0; + QString recovery_files = QString::fromLatin1("fc_recovery_files"); + for (QList::const_iterator it = dirs.cbegin(); it != dirs.cend(); ++it) { + QDir doc_dir(it->absoluteFilePath()); + doc_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries); + uint entries = doc_dir.entryList().count(); + if (entries == 0) { + // in this case we can delete the transient directory because + // we cannot do anything + if (tmp.rmdir(it->filePath())) + countDeletedDocs++; + } + // search for the existence of a recovery file + else if (doc_dir.exists(QLatin1String("fc_recovery_file.xml"))) { + // store the transient directory in case it's not empty + restoreDocFiles << *it; + } + // search for the 'fc_recovery_files' sub-directory and check that it's the only entry + else if (entries == 1 && doc_dir.exists(recovery_files)) { + // if the sub-directory is empty delete the transient directory + QDir rec_dir(doc_dir.absoluteFilePath(recovery_files)); + rec_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries); + if (rec_dir.entryList().isEmpty()) { + doc_dir.rmdir(recovery_files); + if (tmp.rmdir(it->filePath())) + countDeletedDocs++; + } + } + } + + // all directories corresponding to the lock file have been deleted + // so delete the lock file, too + if (countDeletedDocs == dirs.size()) { + tmp.remove(fn); + } + } +} + +void DocumentRecoveryFinder::showRecoveryDialogIfNeeded() +{ + if (!restoreDocFiles.isEmpty()) { + Gui::Dialog::DocumentRecovery dlg(restoreDocFiles, Gui::getMainWindow()); + if (dlg.foundDocuments()) + dlg.exec(); + } +} + +// ---------------------------------------------------------------------------- + +void DocumentRecoveryHandler::checkForPreviousCrashes(const std::function&, const QString&)> & callableFunc) const +{ + QDir tmp = QString::fromUtf8(App::Application::getTempPath().c_str()); + tmp.setNameFilters(QStringList() << QString::fromLatin1("*.lock")); + tmp.setFilter(QDir::Files); + + QString exeName = QString::fromLatin1(App::GetApplication().getExecutableName()); + QList locks = tmp.entryInfoList(); + for (QList::iterator it = locks.begin(); it != locks.end(); ++it) { + QString bn = it->baseName(); + // ignore the lock file for this instance + QString pid = QString::number(QCoreApplication::applicationPid()); + if (bn.startsWith(exeName) && bn.indexOf(pid) < 0) { + QString fn = it->absoluteFilePath(); + boost::interprocess::file_lock flock((const char*)fn.toLocal8Bit()); + if (flock.try_lock()) { + // OK, this file is a leftover from a previous crash + QString crashed_pid = bn.mid(exeName.length()+1); + // search for transient directories with this PID + QString filter; + QTextStream str(&filter); + str << exeName << "_Doc_*_" << crashed_pid; + tmp.setNameFilters(QStringList() << filter); + tmp.setFilter(QDir::Dirs); + QList dirs = tmp.entryInfoList(); + + callableFunc(tmp, dirs, it->fileName()); + } + } + } +} + #include "moc_DocumentRecovery.cpp" diff --git a/src/Gui/DocumentRecovery.h b/src/Gui/DocumentRecovery.h index 966f551173..4b47306fe5 100644 --- a/src/Gui/DocumentRecovery.h +++ b/src/Gui/DocumentRecovery.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace Gui { namespace Dialog { @@ -53,6 +54,7 @@ protected: void contextMenuEvent(QContextMenuEvent*); QString createProjectFile(const QString&); void clearDirectory(const QFileInfo&); + void cleanup(QDir&, const QList&, const QString&); protected Q_SLOTS: void on_buttonCleanup_clicked(); @@ -65,6 +67,23 @@ private: Q_DECLARE_PRIVATE(DocumentRecovery) }; +class DocumentRecoveryFinder { +public: + void checkForPreviousCrashes(); + +private: + void checkDocumentDirs(QDir&, const QList&, const QString&); + void showRecoveryDialogIfNeeded(); + +private: + QList restoreDocFiles; +}; + +class DocumentRecoveryHandler { +public: + void checkForPreviousCrashes(const std::function&, const QString&)> & callableFunc) const; +}; + } //namespace Dialog } //namespace Gui From a156245704c1637392ca5b350beeab66eeb767b8 Mon Sep 17 00:00:00 2001 From: Tobias Frost Date: Fri, 22 Oct 2021 09:39:04 +0200 Subject: [PATCH 018/138] Fixes SurfaceExtend icon not being found. Before this patch, ViewProviderExtend::getIcon() returned "Surface_Extend", which does not exist; The icon file is "Surface_ExtendFace". --- src/Mod/Surface/Gui/ViewProviderExtend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Surface/Gui/ViewProviderExtend.cpp b/src/Mod/Surface/Gui/ViewProviderExtend.cpp index ed5b5f261f..5d575c48f6 100644 --- a/src/Mod/Surface/Gui/ViewProviderExtend.cpp +++ b/src/Mod/Surface/Gui/ViewProviderExtend.cpp @@ -35,7 +35,7 @@ namespace SurfaceGui { QIcon ViewProviderExtend::getIcon(void) const { - return Gui::BitmapFactory().pixmap("Surface_Extend"); + return Gui::BitmapFactory().pixmap("Surface_ExtendFace"); } } //namespace SurfaceGui From aa19f8b05fbbd2f40f075fcad0e6d22b3dc2bff0 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Oct 2021 14:11:00 +0200 Subject: [PATCH 019/138] Part: adjust orientation of refined face to input faces --- src/Mod/Part/App/modelRefine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Mod/Part/App/modelRefine.cpp b/src/Mod/Part/App/modelRefine.cpp index 91c9a3d2e9..90dabaea99 100644 --- a/src/Mod/Part/App/modelRefine.cpp +++ b/src/Mod/Part/App/modelRefine.cpp @@ -1061,6 +1061,11 @@ bool FaceUniter::process() TopoDS_Face newFace = (*typeIt)->buildFace(adjacencySplitter.getGroup(adjacentIndex)); if (!newFace.IsNull()) { + // the created face should have the same orientation as the input faces + const FaceVectorType& faces = adjacencySplitter.getGroup(adjacentIndex); + if (!faces.empty() && newFace.Orientation() != faces[0].Orientation()) { + newFace.Orientation(faces[0].Orientation()); + } facesToSew.push_back(newFace); if (facesToRemove.capacity() <= facesToRemove.size() + adjacencySplitter.getGroup(adjacentIndex).size()) facesToRemove.reserve(facesToRemove.size() + adjacencySplitter.getGroup(adjacentIndex).size()); From e3ebe4bc9f37736a06e03805192d5b26f29692e0 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Oct 2021 17:11:16 +0200 Subject: [PATCH 020/138] Mesh: add basic support of 3MF file format --- src/Mod/Mesh/App/Core/MeshIO.cpp | 105 +++++++++++++++++++++++++++++++ src/Mod/Mesh/App/Core/MeshIO.h | 8 ++- src/Mod/Mesh/App/MeshPyImp.cpp | 1 + src/Mod/Mesh/Gui/Command.cpp | 1 + 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index 09d1ad91dc..9fde9d8515 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -1947,6 +1948,7 @@ std::vector MeshOutput::supportedMeshFormats() fmt.emplace_back("wrz"); fmt.emplace_back("amf"); fmt.emplace_back("asy"); + fmt.emplace_back("3mf"); return fmt; } @@ -2004,6 +2006,9 @@ MeshIO::Format MeshOutput::GetFormat(const char* FileName) else if (file.hasExtension("amf")) { return MeshIO::AMF; } + else if (file.hasExtension("3mf")) { + return MeshIO::ThreeMF; + } else if (file.hasExtension("smf")) { return MeshIO::SMF; } @@ -2113,6 +2118,11 @@ bool MeshOutput::SaveAny(const char* FileName, MeshIO::Format format) const if (!SaveX3DOM(str)) throw Base::FileException("Export of X3DOM failed",FileName); } + else if (fileformat == MeshIO::ThreeMF) { + // write file + if (!Save3MF(str)) + throw Base::FileException("Export of 3MF failed",FileName); + } else if (fileformat == MeshIO::PY) { // write file if (!SavePython(str)) @@ -2183,6 +2193,8 @@ bool MeshOutput::SaveFormat(std::ostream &str, MeshIO::Format fmt) const case MeshIO::WRZ: // it's up to the client to create the needed stream return SaveVRML(str); + case MeshIO::ThreeMF: + return Save3MF(str); case MeshIO::NAS: return SaveNastran(str); case MeshIO::PLY: @@ -2974,6 +2986,99 @@ void MeshOutput::SaveXML (Base::Writer &writer) const writer.decInd(); } +/** Saves the mesh object into a 3MF file. */ +bool MeshOutput::Save3MF(std::ostream &str) const +{ + zipios::ZipOutputStream zip(str); + zip.putNextEntry("/3D/3dmodel.model"); + if (!Save3MFModel(zip)) + return false; + zip.closeEntry(); + + zip.putNextEntry("_rels/.rels"); + if (!Save3MFRels(zip)) + return false; + zip.closeEntry(); + + zip.putNextEntry("[Content_Types].xml"); + if (!Save3MFContent(zip)) + return false; + zip.closeEntry(); + return true; +} + +bool MeshOutput::Save3MFRels(std::ostream &str) const +{ + str << "\n" + << "" + "" + ""; + return true; +} + +bool MeshOutput::Save3MFContent(std::ostream &str) const +{ + str << "\n" + << "" + "" + "" + ""; + return true; +} + +bool MeshOutput::Save3MFModel (std::ostream &str) const +{ + const MeshPointArray& rPoints = _rclMesh.GetPoints(); + const MeshFacetArray& rFacets = _rclMesh.GetFacets(); + + if (!str || str.bad() == true) + return false; + + str << "\n" + << "\n" + << "FreeCAD\n"; + str << Base::blanks(2) << "\n"; + str << Base::blanks(4) << "\n"; + str << Base::blanks(6) << "\n"; + + // vertices + str << Base::blanks(8) << "\n"; + Base::Vector3f pt; + std::size_t index = 0; + for (MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it, ++index) { + pt.Set(it->x, it->y, it->z); + if (this->apply_transform) { + this->_transform.multVec(pt, pt); + } + str << Base::blanks(10) << "\n"; + } + str << Base::blanks(8) << "\n"; + + // facet indices + str << Base::blanks(8) << "\n"; + for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) { + str << Base::blanks(10) << "_aulPoints[0] + << "\" v2=\"" << it->_aulPoints[1] + << "\" v3=\"" << it->_aulPoints[2] + << "\" />\n"; + } + str << Base::blanks(8) << "\n"; + + str << Base::blanks(6) << "\n"; + str << Base::blanks(4) << "\n"; + str << Base::blanks(2) << "\n"; + str << Base::blanks(2) << "\n"; + str << Base::blanks(4) << "\n"; + str << Base::blanks(2) << "\n"; + str << "\n"; + return true; +} + /** Writes an IDTF file. */ bool MeshOutput::SaveIDTF (std::ostream &str) const { diff --git a/src/Mod/Mesh/App/Core/MeshIO.h b/src/Mod/Mesh/App/Core/MeshIO.h index fdb369c348..4e2fc61f0c 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.h +++ b/src/Mod/Mesh/App/Core/MeshIO.h @@ -60,7 +60,8 @@ namespace MeshIO { PY, AMF, SMF, - ASY + ASY, + ThreeMF }; enum Binding { OVERALL, @@ -197,6 +198,8 @@ public: bool SaveAsymptote (std::ostream &rstrOut) const; /** Saves the mesh object into an XML file. */ void SaveXML (Base::Writer &writer) const; + /** Saves the mesh object into a 3MF file. */ + bool Save3MF (std::ostream &str) const; /** Saves a node to an OpenInventor file. */ bool SaveMeshNode (std::ostream &rstrIn); /** Writes an IDTF file. */ @@ -223,6 +226,9 @@ public: protected: /** Writes an X3D file. */ bool SaveX3DContent (std::ostream &rstrOut, bool exportViewpoints) const; + bool Save3MFModel(std::ostream &str) const; + bool Save3MFRels(std::ostream &str) const; + bool Save3MFContent(std::ostream &str) const; protected: const MeshKernel &_rclMesh; /**< reference to mesh data structure */ diff --git a/src/Mod/Mesh/App/MeshPyImp.cpp b/src/Mod/Mesh/App/MeshPyImp.cpp index 4f47e92368..3876f0ff91 100644 --- a/src/Mod/Mesh/App/MeshPyImp.cpp +++ b/src/Mod/Mesh/App/MeshPyImp.cpp @@ -222,6 +222,7 @@ PyObject* MeshPy::write(PyObject *args, PyObject *kwds) ext["APLY" ] = MeshCore::MeshIO::APLY; ext["PY" ] = MeshCore::MeshIO::PY; ext["ASY" ] = MeshCore::MeshIO::ASY; + ext["3MF" ] = MeshCore::MeshIO::ThreeMF; static char* keywords_path[] = {"Filename","Format","Name","Material",NULL}; if (PyArg_ParseTupleAndKeywords(args, kwds, "et|ssO", keywords_path, "utf-8", diff --git a/src/Mod/Mesh/Gui/Command.cpp b/src/Mod/Mesh/Gui/Command.cpp index f92ca46735..52f078b099 100644 --- a/src/Mod/Mesh/Gui/Command.cpp +++ b/src/Mod/Mesh/Gui/Command.cpp @@ -524,6 +524,7 @@ void CmdMeshExport::activated(int) ext << qMakePair(QString::fromLatin1("%1 (*.nas *.bdf)").arg(QObject::tr("Nastran")), "NAS"); ext << qMakePair(QString::fromLatin1("%1 (*.py)").arg(QObject::tr("Python module def")), "PY"); ext << qMakePair(QString::fromLatin1("%1 (*.asy)").arg(QObject::tr("Asymptote Format")), "ASY"); + ext << qMakePair(QString::fromLatin1("%1 (*.3mf)").arg(QObject::tr("3D Manufacturing Format")), "3MF"); ext << qMakePair(QString::fromLatin1("%1 (*.*)").arg(QObject::tr("All Files")), ""); // Undefined QStringList filter; for (QList >::iterator it = ext.begin(); it != ext.end(); ++it) From 3924d410730ecb7c5dcccef87cc815b9b714c105 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Oct 2021 18:49:49 +0200 Subject: [PATCH 021/138] Part: do not open a second transaction when there is already a pending transaction --- src/Mod/Part/Gui/TaskAttacher.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index 8f608f7f8a..0c7273c858 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -1060,7 +1060,8 @@ TaskDlgAttacher::~TaskDlgAttacher() void TaskDlgAttacher::open() { Gui::Document* document = Gui::Application::Instance->getDocument(ViewProvider->getObject()->getDocument()); - document->openCommand(QT_TRANSLATE_NOOP("Command", "Edit attachment")); + if (!document->hasPendingCommand()) + document->openCommand(QT_TRANSLATE_NOOP("Command", "Edit attachment")); } void TaskDlgAttacher::clicked(int) From d39233313b22e2ef9d73b3b545e28b1a19319a5b Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Oct 2021 19:06:57 +0200 Subject: [PATCH 022/138] PD: [skip ci] code cleanup --- src/Mod/PartDesign/Gui/ViewProvider.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Mod/PartDesign/Gui/ViewProvider.cpp b/src/Mod/PartDesign/Gui/ViewProvider.cpp index dff2defa82..55396796af 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.cpp +++ b/src/Mod/PartDesign/Gui/ViewProvider.cpp @@ -64,23 +64,8 @@ ViewProvider::~ViewProvider() bool ViewProvider::doubleClicked(void) { -#if 0 - // TODO May be move to setEdit()? (2015-07-26, Fat-Zer) - if (body != NULL) { - // Drop into insert mode so that the user doesn't see all the geometry that comes later in the tree - // Also, this way the user won't be tempted to use future geometry as external references for the sketch - oldTip = body->Tip.getValue(); - if (oldTip != this->pcObject) - Gui::Command::doCommand(Gui::Command::Gui,"FreeCADGui.runCommand('PartDesign_MoveTip')"); - else - oldTip = NULL; - } else { - oldTip = NULL; - } -#endif - try { - PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); + PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); std::string Msg("Edit "); Msg += this->pcObject->Label.getValue(); Gui::Command::openCommand(Msg.c_str()); From d9d876172234ed767614f602db74cb9b354c6aa6 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Fri, 22 Oct 2021 19:47:23 +0200 Subject: [PATCH 023/138] Draft: Fix snap cycling if length input has focus --- src/Mod/Draft/DraftGui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 842af542a3..0df4412c60 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -498,6 +498,7 @@ class DraftToolBar: self.layout.addLayout(al) self.labellength = self._label("labellength", ll) self.lengthValue = self._inputfield("lengthValue", ll) + self.lengthValue.installEventFilter(self.baseWidget) # Required to detect snap cycling if focusOnLength is True. self.lengthValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString) self.labelangle = self._label("labelangle", al) self.angleLock = self._checkbox("angleLock",al,checked=self.alock) From 45c3cee2641161874b31e060b48a04bdb150fa90 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Oct 2021 20:01:43 +0200 Subject: [PATCH 024/138] PD: open transaction when calling item of context-menu of sketch based features --- src/Mod/PartDesign/Gui/ViewProvider.cpp | 25 ++++++++++++++++--- src/Mod/PartDesign/Gui/ViewProvider.h | 2 ++ src/Mod/PartDesign/Gui/ViewProviderGroove.cpp | 4 +-- src/Mod/PartDesign/Gui/ViewProviderPad.cpp | 7 +----- src/Mod/PartDesign/Gui/ViewProviderPocket.cpp | 4 +-- .../PartDesign/Gui/ViewProviderRevolution.cpp | 4 +-- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/Mod/PartDesign/Gui/ViewProvider.cpp b/src/Mod/PartDesign/Gui/ViewProvider.cpp index 55396796af..156424678f 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.cpp +++ b/src/Mod/PartDesign/Gui/ViewProvider.cpp @@ -31,6 +31,7 @@ #include #endif +#include #include #include #include @@ -66,9 +67,8 @@ bool ViewProvider::doubleClicked(void) { try { PartDesign::Body* body = PartDesign::Body::findBodyOf(getObject()); - std::string Msg("Edit "); - Msg += this->pcObject->Label.getValue(); - Gui::Command::openCommand(Msg.c_str()); + QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); + Gui::Command::openCommand(text.toUtf8()); PartDesignGui::setEdit(pcObject,body); } catch (const Base::Exception&) { @@ -77,6 +77,25 @@ bool ViewProvider::doubleClicked(void) return true; } +void ViewProvider::startDefaultEditMode() +{ + QString text = QObject::tr("Edit %1").arg(QString::fromUtf8(getObject()->Label.getValue())); + Gui::Command::openCommand(text.toUtf8()); + + Gui::Document* document = this->getDocument(); + if (document) { + document->setEdit(this, ViewProvider::Default); + } +} + +void ViewProvider::addDefaultAction(QMenu* menu, const QString& text) +{ + QAction* act = menu->addAction(text); + act->setData(QVariant((int)ViewProvider::Default)); + Gui::ActionFunction* func = new Gui::ActionFunction(menu); + func->trigger(act, boost::bind(&ViewProvider::startDefaultEditMode, this)); +} + void ViewProvider::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { QAction* act = menu->addAction(QObject::tr("Set colors..."), receiver, member); diff --git a/src/Mod/PartDesign/Gui/ViewProvider.h b/src/Mod/PartDesign/Gui/ViewProvider.h index 1506f5eef2..6ff7ffccb4 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.h +++ b/src/Mod/PartDesign/Gui/ViewProvider.h @@ -76,6 +76,8 @@ protected: virtual void setupContextMenu(QMenu* menu, QObject* receiver, const char* member) override; virtual bool setEdit(int ModNum) override; virtual void unsetEdit(int ModNum) override; + void startDefaultEditMode(); + void addDefaultAction(QMenu*, const QString&); virtual bool onDelete(const std::vector &) override; diff --git a/src/Mod/PartDesign/Gui/ViewProviderGroove.cpp b/src/Mod/PartDesign/Gui/ViewProviderGroove.cpp index 782effe822..b173676cbc 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderGroove.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderGroove.cpp @@ -47,9 +47,7 @@ ViewProviderGroove::~ViewProviderGroove() void ViewProviderGroove::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { - QAction* act; - act = menu->addAction(QObject::tr("Edit groove"), receiver, member); - act->setData(QVariant((int)ViewProvider::Default)); + addDefaultAction(menu, QObject::tr("Edit groove")); PartDesignGui::ViewProviderSketchBased::setupContextMenu(menu, receiver, member); } diff --git a/src/Mod/PartDesign/Gui/ViewProviderPad.cpp b/src/Mod/PartDesign/Gui/ViewProviderPad.cpp index 93e5cc63d2..e7eeeaa3d1 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPad.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderPad.cpp @@ -47,12 +47,7 @@ ViewProviderPad::~ViewProviderPad() void ViewProviderPad::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { - // Note: This methode couldn't be unified with others because menu entry string - // should present united in sources for proper translation and shouldn't be - // constructed on runtime. - QAction* act; - act = menu->addAction(QObject::tr("Edit pad"), receiver, member); - act->setData(QVariant((int)ViewProvider::Default)); + addDefaultAction(menu, QObject::tr("Edit pad")); PartDesignGui::ViewProviderSketchBased::setupContextMenu(menu, receiver, member); } diff --git a/src/Mod/PartDesign/Gui/ViewProviderPocket.cpp b/src/Mod/PartDesign/Gui/ViewProviderPocket.cpp index e610196a89..8343ceb8fd 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPocket.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderPocket.cpp @@ -49,9 +49,7 @@ ViewProviderPocket::~ViewProviderPocket() void ViewProviderPocket::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { - QAction* act; - act = menu->addAction(QObject::tr("Edit pocket"), receiver, member); - act->setData(QVariant((int)ViewProvider::Default)); + addDefaultAction(menu, QObject::tr("Edit pocket")); PartDesignGui::ViewProviderSketchBased::setupContextMenu(menu, receiver, member); } diff --git a/src/Mod/PartDesign/Gui/ViewProviderRevolution.cpp b/src/Mod/PartDesign/Gui/ViewProviderRevolution.cpp index 1cf70ebbb1..80c869e6a9 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderRevolution.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderRevolution.cpp @@ -47,9 +47,7 @@ ViewProviderRevolution::~ViewProviderRevolution() void ViewProviderRevolution::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { - QAction* act; - act = menu->addAction(QObject::tr("Edit revolution"), receiver, member); - act->setData(QVariant((int)ViewProvider::Default)); + addDefaultAction(menu, QObject::tr("Edit revolution")); PartDesignGui::ViewProviderSketchBased::setupContextMenu(menu, receiver, member); } From 3cb0f20b5aec671b27ec36fcea69357f9815481a Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Fri, 22 Oct 2021 20:04:06 +0200 Subject: [PATCH 025/138] Update DraftGui.py The Y and Z inputs also require the event filter. --- src/Mod/Draft/DraftGui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 0df4412c60..b59bf0333b 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -473,9 +473,11 @@ class DraftToolBar: self.xValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString) self.labely = self._label("labely", yl) self.yValue = self._inputfield("yValue", yl) + self.yValue.installEventFilter(self.baseWidget) # Required to detect snap cycling in case of Y constraining. self.yValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString) self.labelz = self._label("labelz", zl) self.zValue = self._inputfield("zValue", zl) + self.zValue.installEventFilter(self.baseWidget) # Required to detect snap cycling in case of Z constraining. self.zValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString) self.pointButton = self._pushbutton("addButton", bl, icon="Draft_AddPoint") From 87ad1c0dde7affee7620435f04a12a55a3322022 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Oct 2021 20:23:16 +0200 Subject: [PATCH 026/138] PD: [skip ci] when creating a datum object allow user to create a body if needed --- src/Mod/PartDesign/Gui/Command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 8a52a037c7..5e74f23473 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -107,7 +107,7 @@ void UnifiedDatumCommand(Gui::Command &cmd, Base::Type type, std::string name) bEditSelected = true; } - PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */false); + PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true); if (bEditSelected) { std::string tmp = std::string("Edit ")+name; From 65923bfdbf38c428ff4d52a16b0f5839af8017a7 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 23 Oct 2021 15:15:09 +0200 Subject: [PATCH 027/138] Gui: add virtual methods undoActions()/redoActions() to MDIView to simplify code in UndoDialog/RedoDialog --- src/Gui/DlgUndoRedo.cpp | 46 ++++---------------------------- src/Gui/MDIView.cpp | 28 +++++++++++++++++++ src/Gui/MDIView.h | 6 +++++ src/Gui/TextDocumentEditorView.h | 4 +-- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/Gui/DlgUndoRedo.cpp b/src/Gui/DlgUndoRedo.cpp index 2d183dcb60..536e647c31 100644 --- a/src/Gui/DlgUndoRedo.cpp +++ b/src/Gui/DlgUndoRedo.cpp @@ -29,9 +29,7 @@ #include "DlgUndoRedo.h" #include "Application.h" #include "MainWindow.h" -#include "Document.h" -#include "EditorView.h" -#include "TextDocumentEditorView.h" +#include "MDIView.h" using namespace Gui::Dialog; @@ -64,28 +62,11 @@ void UndoDialog::onFetchInfo() clear(); // Remove first all items MDIView* mdi = getMainWindow()->activeWindow(); - EditorView* editview = qobject_cast(mdi); - TextDocumentEditorView* textedit = qobject_cast(mdi); - if (editview) { - QStringList vecUndos = editview->undoActions(); + if (mdi) { + QStringList vecUndos = mdi->undoActions(); for (QStringList::Iterator i = vecUndos.begin(); i != vecUndos.end(); ++i) addAction(*i, this, SLOT(onSelected())); } - else if (textedit) { - QStringList vecUndos = textedit->undoActions(); - for (QStringList::Iterator i = vecUndos.begin(); i != vecUndos.end(); ++i) - addAction(*i, this, SLOT(onSelected())); - } - else if (mdi) { - Gui::Document* pcDoc = mdi->getGuiDocument(); - if (pcDoc) { - std::vector vecUndos = pcDoc->getUndoVector(); - for (std::vector::iterator i = vecUndos.begin(); i != vecUndos.end(); ++i) { - QString text = QCoreApplication::translate("Command", i->c_str()); - addAction(text, this, SLOT(onSelected())); - } - } - } } /** Closes the dialog and sends the message 'Undo' to the currently active MDI view. */ @@ -129,28 +110,11 @@ void RedoDialog::onFetchInfo() clear(); // Remove first all items MDIView* mdi = getMainWindow()->activeWindow(); - EditorView* editview = qobject_cast(mdi); - TextDocumentEditorView* textedit = qobject_cast(mdi); - if (editview) { - QStringList vecRedos = editview->redoActions(); + if (mdi) { + QStringList vecRedos = mdi->redoActions(); for (QStringList::Iterator i = vecRedos.begin(); i != vecRedos.end(); ++i) addAction(*i, this, SLOT(onSelected())); } - else if (textedit) { - QStringList vecRedos = textedit->redoActions(); - for (QStringList::Iterator i = vecRedos.begin(); i != vecRedos.end(); ++i) - addAction(*i, this, SLOT(onSelected())); - } - else if (mdi) { - Gui::Document* pcDoc = mdi->getGuiDocument(); - if (pcDoc) { - std::vector vecRedos = pcDoc->getRedoVector(); - for (std::vector::iterator i = vecRedos.begin(); i != vecRedos.end(); ++i) { - QString text = QCoreApplication::translate("Command", i->c_str()); - addAction(text, this, SLOT(onSelected())); - } - } - } } /** Closes the dialog and sends the message 'Redo' to the currently active MDI view. */ diff --git a/src/Gui/MDIView.cpp b/src/Gui/MDIView.cpp index c95b2dbe8f..22d2a7658f 100644 --- a/src/Gui/MDIView.cpp +++ b/src/Gui/MDIView.cpp @@ -241,6 +241,34 @@ void MDIView::printPreview() std::cerr << "Printing preview not implemented for " << this->metaObject()->className() << std::endl; } +QStringList MDIView::undoActions() const +{ + QStringList actions; + Gui::Document* doc = getGuiDocument(); + if (doc) { + std::vector vecUndos = doc->getUndoVector(); + for (std::vector::iterator i = vecUndos.begin(); i != vecUndos.end(); ++i) { + actions << QCoreApplication::translate("Command", i->c_str()); + } + } + + return actions; +} + +QStringList MDIView::redoActions() const +{ + QStringList actions; + Gui::Document* doc = getGuiDocument(); + if (doc) { + std::vector vecRedos = doc->getRedoVector(); + for (std::vector::iterator i = vecRedos.begin(); i != vecRedos.end(); ++i) { + actions << QCoreApplication::translate("Command", i->c_str()); + } + } + + return actions; +} + QSize MDIView::minimumSizeHint () const { return QSize(400, 300); diff --git a/src/Gui/MDIView.h b/src/Gui/MDIView.h index 0aadbb4ead..5a0e97431a 100644 --- a/src/Gui/MDIView.h +++ b/src/Gui/MDIView.h @@ -95,6 +95,12 @@ public: virtual void printPreview(); //@} + /** @name Undo/Redo actions */ + //@{ + virtual QStringList undoActions() const; + virtual QStringList redoActions() const; + //@} + QSize minimumSizeHint () const; /// MDI view mode enum diff --git a/src/Gui/TextDocumentEditorView.h b/src/Gui/TextDocumentEditorView.h index f53f03f7eb..193b70ec84 100644 --- a/src/Gui/TextDocumentEditorView.h +++ b/src/Gui/TextDocumentEditorView.h @@ -53,8 +53,8 @@ public: QPlainTextEdit* getEditor() const { return editor; } App::TextDocument* getTextObject() const { return textDocument; } - QStringList undoActions() const; - QStringList redoActions() const; + QStringList undoActions() const override; + QStringList redoActions() const override; protected: void showEvent(QShowEvent*) override; From 398381c16e075702b134468b9923762645605c13 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 23 Oct 2021 16:39:28 +0200 Subject: [PATCH 028/138] Part: for vertex, edge, wire use BRepExtrema_DistShapeShape to check whether point is inside --- src/Mod/Part/App/TopoShapePyImp.cpp | 44 ++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/Mod/Part/App/TopoShapePyImp.cpp b/src/Mod/Part/App/TopoShapePyImp.cpp index 806d24d8ee..c6a25d3d74 100644 --- a/src/Mod/Part/App/TopoShapePyImp.cpp +++ b/src/Mod/Part/App/TopoShapePyImp.cpp @@ -26,6 +26,7 @@ # include # include # include +# include # include # include # include @@ -2198,26 +2199,49 @@ PyObject* TopoShapePy::isInside(PyObject *args) PyObject* checkFace = Py_False; TopAbs_State stateIn = TopAbs_IN; if (!PyArg_ParseTuple(args, "O!dO!", &(Base::VectorPy::Type), &point, &tolerance, &PyBool_Type, &checkFace)) - return NULL; + return nullptr; + try { TopoDS_Shape shape = getTopoShapePtr()->getShape(); - BRepClass3d_SolidClassifier solidClassifier(shape); + if (shape.IsNull()) { + PyErr_SetString(PartExceptionOCCError, "Cannot handle null shape"); + return nullptr; + } + Base::Vector3d pnt = static_cast(point)->value(); gp_Pnt vertex = gp_Pnt(pnt.x,pnt.y,pnt.z); - solidClassifier.Perform(vertex, tolerance); - Standard_Boolean test = (solidClassifier.State() == stateIn); - if (PyObject_IsTrue(checkFace) && (solidClassifier.IsOnAFace())) - test = Standard_True; - return Py_BuildValue("O", (test ? Py_True : Py_False)); + if (shape.ShapeType() == TopAbs_VERTEX || + shape.ShapeType() == TopAbs_EDGE || + shape.ShapeType() == TopAbs_WIRE) { + + BRepBuilderAPI_MakeVertex mkVertex(vertex); + BRepExtrema_DistShapeShape extss; + extss.LoadS1(mkVertex.Vertex()); + extss.LoadS2(shape); + if (!extss.Perform()) { + PyErr_SetString(PartExceptionOCCError, "Failed to determine distance to shape"); + return nullptr; + } + Standard_Boolean test = (extss.Value() <= tolerance); + return Py_BuildValue("O", (test ? Py_True : Py_False)); + } + else { + BRepClass3d_SolidClassifier solidClassifier(shape); + solidClassifier.Perform(vertex, tolerance); + Standard_Boolean test = (solidClassifier.State() == stateIn); + + if (PyObject_IsTrue(checkFace) && (solidClassifier.IsOnAFace())) + test = Standard_True; + return Py_BuildValue("O", (test ? Py_True : Py_False)); + } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); - return NULL; + return nullptr; } catch (const std::exception& e) { PyErr_SetString(PartExceptionOCCError, e.what()); - return NULL; + return nullptr; } } From 044929342aa4f1795654e5e90c7da2012dcc2728 Mon Sep 17 00:00:00 2001 From: luz paz Date: Sun, 3 Oct 2021 14:55:48 -0400 Subject: [PATCH 029/138] Sketcher: fix typos in Constraint Widget - Follow-up to 2f789e6ce79 - Sketcher: fix relevant typos - Re-Add mistakenly deleted files - fix build --- src/Mod/Sketcher/App/SketchObject.cpp | 2 +- src/Mod/Sketcher/Gui/CMakeLists.txt | 8 +- src/Mod/Sketcher/Gui/CommandCreateGeo.cpp | 2 +- src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp | 2 +- src/Mod/Sketcher/Gui/TaskDlgEditSketch.h | 4 +- ...trains.cpp => TaskSketcherConstraints.cpp} | 76 +++++++++---------- ...Constrains.h => TaskSketcherConstraints.h} | 14 ++-- ...nstrains.ui => TaskSketcherConstraints.ui} | 4 +- 8 files changed, 56 insertions(+), 56 deletions(-) rename src/Mod/Sketcher/Gui/{TaskSketcherConstrains.cpp => TaskSketcherConstraints.cpp} (93%) rename src/Mod/Sketcher/Gui/{TaskSketcherConstrains.h => TaskSketcherConstraints.h} (91%) rename src/Mod/Sketcher/Gui/{TaskSketcherConstrains.ui => TaskSketcherConstraints.ui} (99%) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index dc165de246..e737bf4925 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -277,7 +277,7 @@ int SketchObject::solve(bool updateGeoAfterSolving/*=true*/) // Some examples: // Redundant: a vertical line, a horizontal line and an angle constraint of 90 degrees between the two lines // Conflicting: a 80 degrees angle between a vertical line and another line, then adding a horizontal constraint to that other line - // OverConstrained: a conflicting constraint when all other DoF are already constraint (it has more constrains than parameters and the extra constraints are not redundant) + // OverConstrained: a conflicting constraint when all other DoF are already constrained (it has more constraints than parameters and the extra constraints are not redundant) solverNeedsUpdate=false; diff --git a/src/Mod/Sketcher/Gui/CMakeLists.txt b/src/Mod/Sketcher/Gui/CMakeLists.txt index 317d91c208..9e55568413 100644 --- a/src/Mod/Sketcher/Gui/CMakeLists.txt +++ b/src/Mod/Sketcher/Gui/CMakeLists.txt @@ -32,7 +32,7 @@ else() endif() set(SketcherGui_UIC_SRCS - TaskSketcherConstrains.ui + TaskSketcherConstraints.ui TaskSketcherElements.ui TaskSketcherGeneral.ui TaskSketcherMessages.ui @@ -78,10 +78,10 @@ SET(SketcherGui_SRCS SoDatumLabel.h PropertyConstraintListItem.h PropertyConstraintListItem.cpp - TaskSketcherConstrains.ui - TaskSketcherConstrains.cpp + TaskSketcherConstraints.ui + TaskSketcherConstraints.cpp ConstraintFilters.h - TaskSketcherConstrains.h + TaskSketcherConstraints.h TaskSketcherElements.ui TaskSketcherElements.cpp TaskSketcherElements.h diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index e0c931fa81..8e43658060 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -4812,7 +4812,7 @@ public: // autoconstraints were added to the circles of the poles, which is ok because they must go to the // right position, or the user will freak-out if they appear out of the autoconstrained position. - // However, autoconstrains on the first and last pole, in normal non-periodic b-splines (with appropriate endpoint knot multiplicity) + // However, autoconstraints on the first and last pole, in normal non-periodic b-splines (with appropriate endpoint knot multiplicity) // as the ones created by this tool are intended for the b-spline endpoints, and not for the poles, // so here we retrieve any autoconstraint on those poles' center and mangle it to the endpoint. if (ConstrMethod == 0) { diff --git a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp index 8fbdf26459..f522949c9e 100644 --- a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp +++ b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp @@ -43,7 +43,7 @@ TaskDlgEditSketch::TaskDlgEditSketch(ViewProviderSketch *sketchView) : TaskDialog(),sketchView(sketchView) { assert(sketchView); - Constraints = new TaskSketcherConstrains(sketchView); + Constraints = new TaskSketcherConstraints(sketchView); Elements = new TaskSketcherElements(sketchView); General = new TaskSketcherGeneral(sketchView); Messages = new TaskSketcherMessages(sketchView); diff --git a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h index e99900e13d..924ce5737a 100644 --- a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h +++ b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h @@ -27,7 +27,7 @@ #include #include "ViewProviderSketch.h" -#include "TaskSketcherConstrains.h" +#include "TaskSketcherConstraints.h" #include "TaskSketcherElements.h" #include "TaskSketcherGeneral.h" #include "TaskSketcherMessages.h" @@ -72,7 +72,7 @@ protected: protected: ViewProviderSketch *sketchView; - TaskSketcherConstrains *Constraints; + TaskSketcherConstraints *Constraints; TaskSketcherElements *Elements; TaskSketcherGeneral *General; TaskSketcherMessages *Messages; diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp similarity index 93% rename from src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp rename to src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index 5dda156b78..acfb48c942 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -37,8 +37,8 @@ # include #endif -#include "TaskSketcherConstrains.h" -#include "ui_TaskSketcherConstrains.h" +#include "TaskSketcherConstraints.h" +#include "ui_TaskSketcherConstraints.h" #include "EditDatumDialog.h" #include "ViewProviderSketch.h" @@ -565,7 +565,7 @@ void ConstraintView::modifyCurrentItem() void ConstraintView::renameCurrentItem() { - // See also TaskSketcherConstrains::on_listWidgetConstraints_itemChanged + // See also TaskSketcherConstraints::on_listWidgetConstraints_itemChanged QListWidgetItem* item = currentItem(); if (item) editItem(item); @@ -631,10 +631,10 @@ void ConstraintView::swapNamedOfSelectedItems() // ---------------------------------------------------------------------------- -TaskSketcherConstrains::TaskSketcherConstrains(ViewProviderSketch *sketchView) : +TaskSketcherConstraints::TaskSketcherConstraints(ViewProviderSketch *sketchView) : TaskBox(Gui::BitmapFactory().pixmap("document-new"), tr("Constraints"), true, 0), sketchView(sketchView), inEditMode(false), - ui(new Ui_TaskSketcherConstrains) + ui(new Ui_TaskSketcherConstraints) { // we need a separate container widget to add all controls to proxy = new QWidget(this); @@ -709,7 +709,7 @@ TaskSketcherConstrains::TaskSketcherConstrains(ViewProviderSketch *sketchView) : ); connectionConstraintsChanged = sketchView->signalConstraintsChanged.connect( - boost::bind(&SketcherGui::TaskSketcherConstrains::slotConstraintsChanged, this)); + boost::bind(&SketcherGui::TaskSketcherConstraints::slotConstraintsChanged, this)); this->groupLayout()->addWidget(proxy); @@ -718,13 +718,13 @@ TaskSketcherConstrains::TaskSketcherConstrains(ViewProviderSketch *sketchView) : slotConstraintsChanged(); } -TaskSketcherConstrains::~TaskSketcherConstrains() +TaskSketcherConstraints::~TaskSketcherConstraints() { connectionConstraintsChanged.disconnect(); } -void TaskSketcherConstrains::createVisibilityButtonActions() +void TaskSketcherConstraints::createVisibilityButtonActions() { QAction* action = new QAction(QString::fromLatin1("Show only filtered Constraints"),this); @@ -741,7 +741,7 @@ void TaskSketcherConstrains::createVisibilityButtonActions() ui->visibilityButton->addAction(action); } -void TaskSketcherConstrains::updateSelectionFilter() +void TaskSketcherConstraints::updateSelectionFilter() { // Snapshot current selection auto items = ui->listWidgetConstraints->selectedItems(); @@ -752,7 +752,7 @@ void TaskSketcherConstrains::updateSelectionFilter() selectionFilter.push_back(static_cast(item)->ConstraintNbr); } -void TaskSketcherConstrains::updateAssociatedConstraintsFilter() +void TaskSketcherConstraints::updateAssociatedConstraintsFilter() { associatedConstraintsFilter.clear(); @@ -793,7 +793,7 @@ void TaskSketcherConstrains::updateAssociatedConstraintsFilter() updateList(); } -void TaskSketcherConstrains::updateList() +void TaskSketcherConstraints::updateList() { // enforce constraint visibility ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); @@ -805,7 +805,7 @@ void TaskSketcherConstrains::updateList() slotConstraintsChanged(); } -void TaskSketcherConstrains::on_multipleFilterButton_clicked(bool) +void TaskSketcherConstraints::on_multipleFilterButton_clicked(bool) { ConstraintMultiFilterDialog mf; @@ -825,7 +825,7 @@ void TaskSketcherConstrains::on_multipleFilterButton_clicked(bool) } } -void TaskSketcherConstrains::on_settingsDialogButton_clicked(bool) +void TaskSketcherConstraints::on_settingsDialogButton_clicked(bool) { ConstraintSettingsDialog cs; @@ -845,7 +845,7 @@ void TaskSketcherConstrains::on_settingsDialogButton_clicked(bool) cs.exec(); // The dialog reacted on any change, so the result of running the dialog is already reflected on return. } -void TaskSketcherConstrains::changeFilteredVisibility(bool show, ActionTarget target) +void TaskSketcherConstraints::changeFilteredVisibility(bool show, ActionTarget target) { assert(sketchView); const Sketcher::SketchObject * sketch = sketchView->getSketchObject(); @@ -918,27 +918,27 @@ void TaskSketcherConstrains::changeFilteredVisibility(bool show, ActionTarget ta } -void TaskSketcherConstrains::on_showAllButton_clicked(bool) +void TaskSketcherConstraints::on_showAllButton_clicked(bool) { changeFilteredVisibility(true); } -void TaskSketcherConstrains::on_hideAllButton_clicked(bool) +void TaskSketcherConstraints::on_hideAllButton_clicked(bool) { changeFilteredVisibility(false); } -void TaskSketcherConstrains::on_listWidgetConstraints_emitHideSelection3DVisibility() +void TaskSketcherConstraints::on_listWidgetConstraints_emitHideSelection3DVisibility() { changeFilteredVisibility(false, ActionTarget::Selected); } -void TaskSketcherConstrains::on_listWidgetConstraints_emitShowSelection3DVisibility() +void TaskSketcherConstraints::on_listWidgetConstraints_emitShowSelection3DVisibility() { changeFilteredVisibility(true, ActionTarget::Selected); } -void TaskSketcherConstrains::onSelectionChanged(const Gui::SelectionChanges& msg) +void TaskSketcherConstraints::onSelectionChanged(const Gui::SelectionChanges& msg) { assert(sketchView); @@ -1014,7 +1014,7 @@ void TaskSketcherConstrains::onSelectionChanged(const Gui::SelectionChanges& msg } } -void TaskSketcherConstrains::getSelectionGeoId(QString expr, int & geoid, Sketcher::PointPos & pointpos) +void TaskSketcherConstraints::getSelectionGeoId(QString expr, int & geoid, Sketcher::PointPos & pointpos) { QRegExp rxEdge(QString::fromLatin1("^Edge(\\d+)$")); int pos = expr.indexOf(rxEdge); @@ -1043,7 +1043,7 @@ void TaskSketcherConstrains::getSelectionGeoId(QString expr, int & geoid, Sketch } } -void TaskSketcherConstrains::on_comboBoxFilter_currentIndexChanged(int filterindex) +void TaskSketcherConstraints::on_comboBoxFilter_currentIndexChanged(int filterindex) { selectionFilter.clear(); // reset the stored selection filter associatedConstraintsFilter.clear(); @@ -1058,13 +1058,13 @@ void TaskSketcherConstrains::on_comboBoxFilter_currentIndexChanged(int filterind updateList(); } -void TaskSketcherConstrains::on_filterInternalAlignment_stateChanged(int state) +void TaskSketcherConstraints::on_filterInternalAlignment_stateChanged(int state) { Q_UNUSED(state); slotConstraintsChanged(); } -void TaskSketcherConstrains::on_visualisationTrackingFilter_stateChanged(int state) +void TaskSketcherConstraints::on_visualisationTrackingFilter_stateChanged(int state) { // Synchronise button drop state { @@ -1078,7 +1078,7 @@ void TaskSketcherConstrains::on_visualisationTrackingFilter_stateChanged(int sta change3DViewVisibilityToTrackFilter(); } -void TaskSketcherConstrains::on_visibilityButton_trackingaction_changed() +void TaskSketcherConstraints::on_visibilityButton_trackingaction_changed() { // synchronise VisualisationTrackingFilter parameter ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); @@ -1095,23 +1095,23 @@ void TaskSketcherConstrains::on_visibilityButton_trackingaction_changed() change3DViewVisibilityToTrackFilter(); } -void TaskSketcherConstrains::on_visibilityButton_clicked(bool) +void TaskSketcherConstraints::on_visibilityButton_clicked(bool) { change3DViewVisibilityToTrackFilter(); } -void TaskSketcherConstrains::on_extendedInformation_stateChanged(int state) +void TaskSketcherConstraints::on_extendedInformation_stateChanged(int state) { Q_UNUSED(state); slotConstraintsChanged(); } -void TaskSketcherConstrains::on_listWidgetConstraints_emitCenterSelectedItems() +void TaskSketcherConstraints::on_listWidgetConstraints_emitCenterSelectedItems() { sketchView->centerSelection(); } -void TaskSketcherConstrains::on_listWidgetConstraints_itemSelectionChanged(void) +void TaskSketcherConstraints::on_listWidgetConstraints_itemSelectionChanged(void) { std::string doc_name = sketchView->getSketchObject()->getDocument()->getName(); std::string obj_name = sketchView->getSketchObject()->getNameInDocument(); @@ -1132,7 +1132,7 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemSelectionChanged(void) this->blockConnection(block); } -void TaskSketcherConstrains::on_listWidgetConstraints_itemActivated(QListWidgetItem *item) +void TaskSketcherConstraints::on_listWidgetConstraints_itemActivated(QListWidgetItem *item) { ConstraintItem *it = dynamic_cast(item); if (!it) return; @@ -1145,7 +1145,7 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemActivated(QListWidgetI } } -void TaskSketcherConstrains::on_listWidgetConstraints_updateDrivingStatus(QListWidgetItem *item, bool status) +void TaskSketcherConstraints::on_listWidgetConstraints_updateDrivingStatus(QListWidgetItem *item, bool status) { Q_UNUSED(status); ConstraintItem *citem = dynamic_cast(item); @@ -1155,7 +1155,7 @@ void TaskSketcherConstrains::on_listWidgetConstraints_updateDrivingStatus(QListW slotConstraintsChanged(); } -void TaskSketcherConstrains::on_listWidgetConstraints_updateActiveStatus(QListWidgetItem *item, bool status) +void TaskSketcherConstraints::on_listWidgetConstraints_updateActiveStatus(QListWidgetItem *item, bool status) { Q_UNUSED(status); ConstraintItem *citem = dynamic_cast(item); @@ -1165,7 +1165,7 @@ void TaskSketcherConstrains::on_listWidgetConstraints_updateActiveStatus(QListWi slotConstraintsChanged(); } -void TaskSketcherConstrains::on_listWidgetConstraints_itemChanged(QListWidgetItem *item) +void TaskSketcherConstraints::on_listWidgetConstraints_itemChanged(QListWidgetItem *item) { const ConstraintItem *it = dynamic_cast(item); if (!it || inEditMode) @@ -1224,7 +1224,7 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemChanged(QListWidgetIte inEditMode = false; } -void TaskSketcherConstrains::change3DViewVisibilityToTrackFilter() +void TaskSketcherConstraints::change3DViewVisibilityToTrackFilter() { assert(sketchView); // Build up ListView with the constraints @@ -1302,7 +1302,7 @@ void TaskSketcherConstrains::change3DViewVisibilityToTrackFilter() } -bool TaskSketcherConstrains::isConstraintFiltered(QListWidgetItem * item) +bool TaskSketcherConstraints::isConstraintFiltered(QListWidgetItem * item) { assert(sketchView); const Sketcher::SketchObject * sketch = sketchView->getSketchObject(); @@ -1407,7 +1407,7 @@ bool TaskSketcherConstrains::isConstraintFiltered(QListWidgetItem * item) return !visible; } -void TaskSketcherConstrains::slotConstraintsChanged(void) +void TaskSketcherConstraints::slotConstraintsChanged(void) { assert(sketchView); // Build up ListView with the constraints @@ -1460,7 +1460,7 @@ void TaskSketcherConstrains::slotConstraintsChanged(void) } } -void TaskSketcherConstrains::changeEvent(QEvent *e) +void TaskSketcherConstraints::changeEvent(QEvent *e) { TaskBox::changeEvent(e); if (e->type() == QEvent::LanguageChange) { @@ -1469,10 +1469,10 @@ void TaskSketcherConstrains::changeEvent(QEvent *e) } template -bool TaskSketcherConstrains::isFilter(T filterValue) { +bool TaskSketcherConstraints::isFilter(T filterValue) { return isFilterMatch(filterValue, ui->comboBoxFilter->currentIndex()); } -#include "moc_TaskSketcherConstrains.cpp" +#include "moc_TaskSketcherConstraints.cpp" diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.h b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h similarity index 91% rename from src/Mod/Sketcher/Gui/TaskSketcherConstrains.h rename to src/Mod/Sketcher/Gui/TaskSketcherConstraints.h index 8a04dbfe87..5f2941aa35 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h @@ -21,8 +21,8 @@ ***************************************************************************/ -#ifndef GUI_TASKVIEW_TaskSketcherConstrains_H -#define GUI_TASKVIEW_TaskSketcherConstrains_H +#ifndef GUI_TASKVIEW_TaskSketcherConstraints_H +#define GUI_TASKVIEW_TaskSketcherConstraints_H #include #include @@ -40,7 +40,7 @@ class Property; namespace SketcherGui { class ViewProviderSketch; -class Ui_TaskSketcherConstrains; +class Ui_TaskSketcherConstraints; class ConstraintView : public QListWidget { @@ -73,7 +73,7 @@ protected Q_SLOTS: void hideConstraints(); }; -class TaskSketcherConstrains : public Gui::TaskView::TaskBox, public Gui::SelectionObserver +class TaskSketcherConstraints : public Gui::TaskView::TaskBox, public Gui::SelectionObserver { Q_OBJECT @@ -83,8 +83,8 @@ class TaskSketcherConstrains : public Gui::TaskView::TaskBox, public Gui::Select }; public: - TaskSketcherConstrains(ViewProviderSketch *sketchView); - ~TaskSketcherConstrains(); + TaskSketcherConstraints(ViewProviderSketch *sketchView); + ~TaskSketcherConstraints(); /// Observer message from the Selection void onSelectionChanged(const Gui::SelectionChanges& msg); @@ -133,7 +133,7 @@ protected: private: QWidget* proxy; bool inEditMode; - std::unique_ptr ui; + std::unique_ptr ui; ConstraintFilter::FilterValueBitset multiFilterStatus; // Stores the filters to be aggregated to form the multifilter. std::vector selectionFilter; // holds the constraint ids of the selected constraints std::vector associatedConstraintsFilter; // holds the constraint ids of the constraints associated with the selected geometry diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.ui b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.ui similarity index 99% rename from src/Mod/Sketcher/Gui/TaskSketcherConstrains.ui rename to src/Mod/Sketcher/Gui/TaskSketcherConstraints.ui index 7730655b4a..eb9fb18c50 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.ui +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.ui @@ -1,7 +1,7 @@ - SketcherGui::TaskSketcherConstrains - + SketcherGui::TaskSketcherConstraints + 0 From 2808c4bc9c2be7a7ec467580907f124fea9981be Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 23 Oct 2021 18:58:43 +0200 Subject: [PATCH 030/138] Mesh: Mesh.createBox() now accepts a bounding box as argument --- src/Mod/Mesh/App/AppMeshPy.cpp | 35 +++++++++++++++++++++++----------- src/Mod/Mesh/App/Mesh.cpp | 32 +++++++++++++++++++++++++++++++ src/Mod/Mesh/App/Mesh.h | 1 + 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/Mod/Mesh/App/AppMeshPy.cpp b/src/Mod/Mesh/App/AppMeshPy.cpp index 92a6524e7f..ccc80fe665 100644 --- a/src/Mod/Mesh/App/AppMeshPy.cpp +++ b/src/Mod/Mesh/App/AppMeshPy.cpp @@ -308,19 +308,32 @@ private: } Py::Object createBox(const Py::Tuple& args) { - float length = 10.0f; - float width = 10.0f; - float height = 10.0f; - float edgelen = -1.0f; - if (!PyArg_ParseTuple(args.ptr(), "|ffff",&length,&width,&height,&edgelen)) - throw Py::Exception(); + MeshObject* mesh = nullptr; - MeshObject* mesh; - if (edgelen < 0.0f) - mesh = MeshObject::createCube(length, width, height); - else - mesh = MeshObject::createCube(length, width, height, edgelen); + do { + float length = 10.0f; + float width = 10.0f; + float height = 10.0f; + float edgelen = -1.0f; + if (PyArg_ParseTuple(args.ptr(), "|ffff",&length,&width,&height,&edgelen)) { + if (edgelen < 0.0f) + mesh = MeshObject::createCube(length, width, height); + else + mesh = MeshObject::createCube(length, width, height, edgelen); + break; + } + PyErr_Clear(); + PyObject* box; + if (PyArg_ParseTuple(args.ptr(), "O!",&Base::BoundBoxPy::Type, &box)) { + Py::BoundingBox bbox(box, false); + mesh = MeshObject::createCube(bbox.getValue()); + break; + } + + throw Py::TypeError("Must be real numbers or BoundBox"); + } + while (false); if (!mesh) { throw Py::Exception(Base::BaseExceptionFreeCADError, "Creation of box failed"); } diff --git a/src/Mod/Mesh/App/Mesh.cpp b/src/Mod/Mesh/App/Mesh.cpp index e64d463218..463d6f19ba 100644 --- a/src/Mod/Mesh/App/Mesh.cpp +++ b/src/Mod/Mesh/App/Mesh.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1833,6 +1834,37 @@ MeshObject* MeshObject::createCube(float length, float width, float height, floa return nullptr; } +MeshObject* MeshObject::createCube(const Base::BoundBox3d& bbox) +{ + std::vector facets; + auto createFacet = [&bbox](int i, int j, int k) { + MeshCore::MeshGeomFacet facet; + facet._aclPoints[0] = Base::convertTo(bbox.CalcPoint(i)); + facet._aclPoints[1] = Base::convertTo(bbox.CalcPoint(j)); + facet._aclPoints[2] = Base::convertTo(bbox.CalcPoint(k)); + facet.CalcNormal(); + return facet; + }; + + facets.push_back(createFacet(0, 1, 2)); + facets.push_back(createFacet(0, 2, 3)); + facets.push_back(createFacet(0, 5, 1)); + facets.push_back(createFacet(0, 4, 5)); + facets.push_back(createFacet(0, 3, 7)); + facets.push_back(createFacet(0, 7, 4)); + facets.push_back(createFacet(4, 6, 5)); + facets.push_back(createFacet(4, 7, 6)); + facets.push_back(createFacet(1, 6, 2)); + facets.push_back(createFacet(1, 5, 6)); + facets.push_back(createFacet(2, 7, 3)); + facets.push_back(createFacet(2, 6, 7)); + + Base::EmptySequencer seq; + std::unique_ptr mesh(new MeshObject); + mesh->getKernel() = facets; + return mesh.release(); +} + void MeshObject::addSegment(const Segment& s) { addSegment(s.getIndices()); diff --git a/src/Mod/Mesh/App/Mesh.h b/src/Mod/Mesh/App/Mesh.h index 6d531802db..ea3f54ca1a 100644 --- a/src/Mod/Mesh/App/Mesh.h +++ b/src/Mod/Mesh/App/Mesh.h @@ -338,6 +338,7 @@ public: static MeshObject* createTorus(float, float, int); static MeshObject* createCube(float, float, float); static MeshObject* createCube(float, float, float, float); + static MeshObject* createCube(const Base::BoundBox3d&); //@} public: From 9f2dd4a363053e87e1b1def0d005ed441d378796 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 23 Oct 2021 22:56:45 +0200 Subject: [PATCH 031/138] Base: [skip ci] Modify GetASCII to include empty strings --- src/Base/Parameter.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Base/Parameter.cpp b/src/Base/Parameter.cpp index 53236af1cc..b115e4e02f 100644 --- a/src/Base/Parameter.cpp +++ b/src/Base/Parameter.cpp @@ -732,11 +732,8 @@ std::string ParameterGrp::GetASCII(const char* Name, const char * pPreset) const DOMNode *pcElem2 = pcElem->getFirstChild(); if (pcElem2) return std::string(StrXUTF8(pcElem2->getNodeValue()).c_str()); - else if (pPreset==0) - return std::string(""); - else - return std::string(pPreset); + return std::string(""); } std::vector ParameterGrp::GetASCIIs(const char * sFilter) const From ca6d49d080dea0abc23d954743eca7c46f33469b Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 24 Oct 2021 21:36:44 +0200 Subject: [PATCH 032/138] Mesh: embed Evaluation dialog into a scroll area --- src/Mod/Mesh/Gui/DlgEvaluateMeshImp.cpp | 16 ++++++++++++++-- src/Mod/Mesh/Gui/DlgEvaluateMeshImp.h | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.cpp b/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.cpp index a14fc796e6..cdac9bd8e1 100644 --- a/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.cpp +++ b/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include # include +# include #endif #include "DlgEvaluateMeshImp.h" @@ -1273,11 +1274,18 @@ bool DockEvaluateMeshImp::hasInstance() DockEvaluateMeshImp::DockEvaluateMeshImp( QWidget* parent, Qt::WindowFlags fl ) : DlgEvaluateMeshImp( parent, fl ) { + scrollArea = new QScrollArea(); + scrollArea->setObjectName(QLatin1String("scrollArea")); + scrollArea->setFrameShape(QFrame::NoFrame); + scrollArea->setFrameShadow(QFrame::Plain); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(this); + // embed this dialog into a dockable widget container Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); // use Qt macro for preparing for translation stuff (but not translating yet) QDockWidget* dw = pDockMgr->addDockWindow("Evaluate & Repair Mesh", - this, Qt::RightDockWidgetArea); + scrollArea, Qt::RightDockWidgetArea); //dw->setAttribute(Qt::WA_DeleteOnClose); dw->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); dw->show(); @@ -1298,7 +1306,11 @@ void DockEvaluateMeshImp::closeEvent(QCloseEvent*) { // closes the dock window Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); - pDockMgr->removeDockWindow(this); + pDockMgr->removeDockWindow(scrollArea); + + // make sure to also delete the scroll area + scrollArea->setWidget(nullptr); + scrollArea->deleteLater(); } /** diff --git a/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.h b/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.h index e663e795ae..fffd94e34e 100644 --- a/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.h +++ b/src/Mod/Mesh/Gui/DlgEvaluateMeshImp.h @@ -34,6 +34,7 @@ #include class QAbstractButton; +class QScrollArea; namespace Gui { class View3DInventor; @@ -159,6 +160,7 @@ public: QSize sizeHint () const; private: + QScrollArea* scrollArea; static DockEvaluateMeshImp* _instance; }; From 67e8fd0b6a44593adb1085889098043add9c9787 Mon Sep 17 00:00:00 2001 From: donovaly Date: Sun, 13 Dec 2020 01:53:14 +0100 Subject: [PATCH 033/138] [GUI] fix tree object context menu issues - fix bug that you can select several objects across different document to make them a group - fix bug that when more than one object is selected you get a context menu to rename, despite it is unclear what object should be renamed - add feature to select also the child objects of the selection The latter was also requested long time ago: https://tracker.freecadweb.org/view.php?id=2397 --- src/Gui/Tree.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++-- src/Gui/Tree.h | 5 +++ 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 196ea983f2..6d400dbf66 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -472,6 +472,10 @@ TreeWidget::TreeWidget(const char *name, QWidget* parent) connect(this->finishEditingAction, SIGNAL(triggered()), this, SLOT(onFinishEditing())); + this->selectDependentsAction = new QAction(this); + connect(this->selectDependentsAction, SIGNAL(triggered()), + this, SLOT(onSelectDependents())); + this->closeDocAction = new QAction(this); connect(this->closeDocAction, SIGNAL(triggered()), this, SLOT(onCloseDoc())); @@ -843,6 +847,7 @@ void TreeWidget::contextMenuEvent (QContextMenuEvent * e) break; } } + contextMenu.addAction(this->selectDependentsAction); this->skipRecomputeAction->setChecked(doc->testStatus(App::Document::SkipRecompute)); contextMenu.addAction(this->skipRecomputeAction); this->allowPartialRecomputeAction->setChecked(doc->testStatus(App::Document::AllowPartialRecompute)); @@ -857,24 +862,43 @@ void TreeWidget::contextMenuEvent (QContextMenuEvent * e) DocumentObjectItem* objitem = static_cast (this->contextItem); + // check that the selection is not across several documents + bool acrossDocuments = false; + auto SelectedObjectsList = Selection().getCompleteSelection(); + // get the object's document as reference App::Document* doc = objitem->object()->getObject()->getDocument(); + for (auto it = SelectedObjectsList.begin(); it != SelectedObjectsList.end(); ++it) { + if ((*it).pDoc != doc) { + acrossDocuments = true; + break; + } + } + showHiddenAction->setChecked(doc->ShowHidden.getValue()); contextMenu.addAction(this->showHiddenAction); hideInTreeAction->setChecked(!objitem->object()->showInTree()); contextMenu.addAction(this->hideInTreeAction); - if (objitem->object()->getObject()->isDerivedFrom(App::DocumentObjectGroup::getClassTypeId())) - contextMenu.addAction(this->createGroupAction); + if (!acrossDocuments) { // is only sensible for selections within one document + if (objitem->object()->getObject()->isDerivedFrom(App::DocumentObjectGroup::getClassTypeId())) + contextMenu.addAction(this->createGroupAction); + // if there are dependent objects in the selection, add context menu to add them to selection + if (CheckForDependents()) + contextMenu.addAction(this->selectDependentsAction); + } contextMenu.addSeparator(); contextMenu.addAction(this->markRecomputeAction); contextMenu.addAction(this->recomputeObjectAction); contextMenu.addSeparator(); - contextMenu.addAction(this->relabelObjectAction); + + // relabeling is only possible for a single selected document + if (SelectedObjectsList.size() == 1) + contextMenu.addAction(this->relabelObjectAction); auto selItems = this->selectedItems(); - // if only one item is selected setup the edit menu + // if only one item is selected, setup the edit menu if (selItems.size() == 1) { objitem->object()->setupContextMenu(&editMenu, this, SLOT(onStartEditing())); QList editAct = editMenu.actions(); @@ -1037,6 +1061,73 @@ void TreeWidget::onFinishEditing() } } +// check if selection has dependent objects +bool TreeWidget::CheckForDependents() +{ + // if the selected object is a document + if (this->contextItem && this->contextItem->type() == DocumentType) { + return true; + } + // it can be an object + else { + QList items = this->selectedItems(); + for (QList::iterator it = items.begin(); it != items.end(); ++it) { + if ((*it)->type() == ObjectType) { + DocumentObjectItem* objitem = static_cast(*it); + App::DocumentObject* obj = objitem->object()->getObject(); + // get dependents + auto subObjectList = obj->getOutList(); + if (subObjectList.size() > 0) + return true; + } + } + } + + return false; +} + +// adds an App::DocumentObject* and its dependent objects to the selection +void TreeWidget::addDependentToSelection(App::Document* doc, App::DocumentObject* docObject) +{ + // add the docObject to the selection + Selection().addSelection(doc->getName(), docObject->getNameInDocument()); + // get the dependent + auto subObjectList = docObject->getOutList(); + // the dependent can in turn have dependents, thus add them recursively + for (auto itDepend = subObjectList.begin(); itDepend != subObjectList.end(); ++itDepend) + addDependentToSelection(doc, (*itDepend)); +} + +// add dependents of the selected tree object to selection +void TreeWidget::onSelectDependents() +{ + // We only have this context menu entry if the selection is within one document but it + // might be not the active document. Therefore get the document not here but later by casting. + App::Document* doc; + + // if the selected object is a document + if (this->contextItem && this->contextItem->type() == DocumentType) { + DocumentItem* docitem = static_cast(this->contextItem); + doc = docitem->document()->getDocument(); + std::vector obj = doc->getObjects(); + for (std::vector::iterator it = obj.begin(); it != obj.end(); ++it) + Selection().addSelection(doc->getName(), (*it)->getNameInDocument()); + } + // it can be an object + else { + QList items = this->selectedItems(); + for (QList::iterator it = items.begin(); it != items.end(); ++it) { + if ((*it)->type() == ObjectType) { + DocumentObjectItem* objitem = static_cast(*it); + doc = objitem->object()->getObject()->getDocument(); + App::DocumentObject* obj = objitem->object()->getObject(); + // the dependents can also have dependents, thus add them recursively via a separate void + addDependentToSelection(doc, obj); + } + } + } +} + void TreeWidget::onSkipRecompute(bool on) { // if a document item is selected then touch all objects @@ -2666,6 +2757,9 @@ void TreeWidget::setupText() this->finishEditingAction->setText(tr("Finish editing")); this->finishEditingAction->setStatusTip(tr("Finish editing object")); + this->selectDependentsAction->setText(tr("Add dependent objects to selection")); + this->selectDependentsAction->setStatusTip(tr("Adds all dependent objects to the selection")); + this->closeDocAction->setText(tr("Close document")); this->closeDocAction->setStatusTip(tr("Close the document")); diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index aa9e4cc9e2..f644f81a64 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -164,6 +164,7 @@ protected Q_SLOTS: void onActivateDocument(QAction*); void onStartEditing(); void onFinishEditing(); + void onSelectDependents(); void onSkipRecompute(bool on); void onAllowPartialRecompute(bool on); void onReloadDoc(); @@ -208,10 +209,14 @@ private: void updateChildren(App::DocumentObject *obj, const std::set &data, bool output, bool force); + bool CheckForDependents(); + void addDependentToSelection(App::Document* doc, App::DocumentObject* docObject); + private: QAction* createGroupAction; QAction* relabelObjectAction; QAction* finishEditingAction; + QAction* selectDependentsAction; QAction* skipRecomputeAction; QAction* allowPartialRecomputeAction; QAction* markRecomputeAction; From f3ca27e18e92e947995206ed0a75efff3b0f6c93 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 25 Oct 2021 13:29:42 +0200 Subject: [PATCH 034/138] Gui: add search box to text editor --- src/Gui/EditorView.cpp | 189 ++++++++++++++++++++++++++++++++++++++++- src/Gui/EditorView.h | 41 +++++++++ src/Gui/TextEdit.cpp | 15 ++++ src/Gui/TextEdit.h | 5 ++ 4 files changed, 249 insertions(+), 1 deletion(-) diff --git a/src/Gui/EditorView.cpp b/src/Gui/EditorView.cpp index 75f010da77..196a077085 100644 --- a/src/Gui/EditorView.cpp +++ b/src/Gui/EditorView.cpp @@ -25,9 +25,12 @@ #ifndef _PreComp_ # include # include +# include # include # include # include +# include +# include # include # include # include @@ -35,10 +38,15 @@ # include # include # include +# include +# include # include # include +# include +# include # include # include +# include #endif #include "EditorView.h" @@ -59,6 +67,7 @@ namespace Gui { class EditorViewP { public: QPlainTextEdit* textEdit; + SearchBar* searchBar; QString fileName; EditorView::DisplayName displayName; QTimer* activityTimer; @@ -90,19 +99,28 @@ EditorView::EditorView(QPlainTextEdit* editor, QWidget* parent) d->textEdit = editor; d->textEdit->setLineWrapMode(QPlainTextEdit::NoWrap); + d->searchBar = new SearchBar(); + d->searchBar->setEditor(editor); + // update editor actions on request Gui::MainWindow* mw = Gui::getMainWindow(); connect(editor, SIGNAL(undoAvailable(bool)), mw, SLOT(updateEditorActions())); connect(editor, SIGNAL(redoAvailable(bool)), mw, SLOT(updateEditorActions())); connect(editor, SIGNAL(copyAvailable(bool)), mw, SLOT(updateEditorActions())); + connect(editor, SIGNAL(showSearchBar()), d->searchBar, SLOT(activate())); + connect(editor, SIGNAL(findNext()), d->searchBar, SLOT(findNext())); + connect(editor, SIGNAL(findPrevious()), d->searchBar, SLOT(findPrevious())); + // Create the layout containing the workspace and a tab bar QFrame* hbox = new QFrame(this); hbox->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); - QHBoxLayout* layout = new QHBoxLayout(); + QVBoxLayout* layout = new QVBoxLayout(); layout->setMargin(1); layout->addWidget(d->textEdit); + layout->addWidget(d->searchBar); d->textEdit->setParent(hbox); + d->searchBar->setParent(hbox); hbox->setLayout(layout); setCentralWidget(hbox); @@ -650,4 +668,173 @@ void PythonEditorView::hideDebugMarker() _pye->hideDebugMarker(); } +// ---------------------------------------------------------------------------- + +SearchBar::SearchBar(QWidget* parent) + : QWidget(parent) + , textEditor(nullptr) +{ + horizontalLayout = new QHBoxLayout(this); + horizontalLayout->setSpacing(3); + + closeButton = new QToolButton(this); + closeButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton)); + closeButton->setAutoRaise(true); + connect(closeButton, &QToolButton::clicked, this, &SearchBar::deactivate); + + horizontalLayout->addWidget(closeButton); + + searchText = new QLineEdit(this); + searchText->setClearButtonEnabled(true); + horizontalLayout->addWidget(searchText); + connect(searchText, &QLineEdit::returnPressed, this, &SearchBar::findNext); + connect(searchText, &QLineEdit::textChanged, this, &SearchBar::findCurrent); + connect(searchText, &QLineEdit::textChanged, this, &SearchBar::updateButtons); + + prevButton = new QToolButton(this); + prevButton->setIcon(style()->standardIcon(QStyle::SP_ArrowBack)); + prevButton->setAutoRaise(true); + prevButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + horizontalLayout->addWidget(prevButton); + connect(prevButton, &QToolButton::clicked, this, &SearchBar::findPrevious); + + nextButton = new QToolButton(this); + nextButton->setIcon(style()->standardIcon(QStyle::SP_ArrowForward)); + nextButton->setAutoRaise(true); + nextButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + horizontalLayout->addWidget(nextButton); + connect(nextButton, &QToolButton::clicked, this, &SearchBar::findNext); + + matchCase = new QCheckBox(this); + horizontalLayout->addWidget(matchCase); + connect(matchCase, &QCheckBox::toggled, this, &SearchBar::findCurrent); + + matchWord = new QCheckBox(this); + horizontalLayout->addWidget(matchWord); + connect(matchWord, &QCheckBox::toggled, this, &SearchBar::findCurrent); + + horizontalSpacer = new QSpacerItem(192, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayout->addItem(horizontalSpacer); + + retranslateUi(); + + setMinimumWidth(minimumSizeHint().width()); + updateButtons(); + hide(); +} + +void SearchBar::setEditor(QPlainTextEdit* textEdit) +{ + textEditor = textEdit; +} + +void SearchBar::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Escape) { + hide(); + return; + } + + QWidget::keyPressEvent(event); +} + +void SearchBar::retranslateUi() +{ + prevButton->setText(tr("Previous")); + nextButton->setText(tr("Next")); + matchCase->setText(tr("Case sensitive")); + matchWord->setText(tr("Whole words")); +} + +void SearchBar::activate() +{ + show(); + searchText->selectAll(); + searchText->setFocus(Qt::ShortcutFocusReason); +} + +void SearchBar::deactivate() +{ + if (textEditor) + textEditor->setFocus(); + hide(); +} + +void SearchBar::findPrevious() +{ + findText(true, false, searchText->text()); +} + +void SearchBar::findNext() +{ + findText(true, true, searchText->text()); +} + +void SearchBar::findCurrent() +{ + findText(false, true, searchText->text()); +} + +void SearchBar::findText(bool skip, bool next, const QString& str) +{ + if (!textEditor) + return; + + QTextCursor cursor = textEditor->textCursor(); + QTextDocument *doc = textEditor->document(); + if (!doc || cursor.isNull()) + return; + + if (cursor.hasSelection()) + cursor.setPosition((skip && next) ? cursor.position() : cursor.anchor()); + + bool found = true; + QTextCursor newCursor = cursor; + if (!str.isEmpty()) { + QTextDocument::FindFlags options; + if (!next) + options |= QTextDocument::FindBackward; + if (matchCase->isChecked()) + options |= QTextDocument::FindCaseSensitively; + if (matchWord->isChecked()) + options |= QTextDocument::FindWholeWords; + + newCursor = doc->find(str, cursor, options); + if (newCursor.isNull()) { + QTextCursor ac(doc); + ac.movePosition(options & QTextDocument::FindBackward ? QTextCursor::End : QTextCursor::Start); + newCursor = doc->find(str, ac, options); + if (newCursor.isNull()) { + found = false; + newCursor = cursor; + } + } + } + + if (!isVisible()) + show(); + + textEditor->setTextCursor(newCursor); + + QPalette palette; + palette.setColor(QPalette::Active, QPalette::Base, found ? Qt::white : QColor(255, 80, 80)); + searchText->setPalette(palette); +} + +void SearchBar::updateButtons() +{ + bool empty = searchText->text().isEmpty(); + prevButton->setDisabled(empty); + nextButton->setDisabled(empty); +} + +void SearchBar::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::LanguageChange) { + retranslateUi(); + } + + QWidget::changeEvent(event); +} + #include "moc_EditorView.cpp" diff --git a/src/Gui/EditorView.h b/src/Gui/EditorView.h index 54f6ccc5e7..80882adb41 100644 --- a/src/Gui/EditorView.h +++ b/src/Gui/EditorView.h @@ -30,6 +30,10 @@ QT_BEGIN_NAMESPACE class QPlainTextEdit; class QPrinter; +class QHBoxLayout; +class QToolButton; +class QCheckBox; +class QSpacerItem; QT_END_NAMESPACE namespace Gui { @@ -132,6 +136,43 @@ private: PythonEditor* _pye; }; +class SearchBar : public QWidget +{ + Q_OBJECT + +public: + SearchBar(QWidget* parent = nullptr); + + void setEditor(QPlainTextEdit *textEdit); + +protected: + void keyPressEvent(QKeyEvent*); + void changeEvent(QEvent*); + +public Q_SLOTS: + void activate(); + void deactivate(); + void findPrevious(); + void findNext(); + void findCurrent(); + +private: + void retranslateUi(); + void findText(bool skip, bool next, const QString& str); + void updateButtons(); + +private: + QPlainTextEdit* textEditor; + QHBoxLayout* horizontalLayout; + QSpacerItem* horizontalSpacer; + QToolButton* closeButton; + QLineEdit* searchText; + QToolButton* prevButton; + QToolButton* nextButton; + QCheckBox* matchCase; + QCheckBox* matchWord; +}; + } // namespace Gui #endif // GUI_EDITORVIEW_H diff --git a/src/Gui/TextEdit.cpp b/src/Gui/TextEdit.cpp index d9cbda44bd..219a3d72fc 100644 --- a/src/Gui/TextEdit.cpp +++ b/src/Gui/TextEdit.cpp @@ -48,6 +48,21 @@ TextEdit::TextEdit(QWidget* parent) shortcut->setKey(Qt::CTRL+Qt::Key_Space); shortcut->setContext(Qt::WidgetShortcut); connect(shortcut, SIGNAL(activated()), this, SLOT(complete())); + + QShortcut* shortcutFind = new QShortcut(this); + shortcutFind->setKey(QKeySequence::Find); + shortcutFind->setContext(Qt::WidgetShortcut); + connect(shortcutFind, SIGNAL(activated()), this, SIGNAL(showSearchBar())); + + QShortcut* shortcutNext = new QShortcut(this); + shortcutNext->setKey(QKeySequence::FindNext); + shortcutNext->setContext(Qt::WidgetShortcut); + connect(shortcutNext, SIGNAL(activated()), this, SIGNAL(findNext())); + + QShortcut* shortcutPrev = new QShortcut(this); + shortcutPrev->setKey(QKeySequence::FindPrevious); + shortcutPrev->setContext(Qt::WidgetShortcut); + connect(shortcutPrev, SIGNAL(activated()), this, SIGNAL(findPrevious())); } /** Destroys the object and frees any allocated resources */ diff --git a/src/Gui/TextEdit.h b/src/Gui/TextEdit.h index ee85626323..ad7bded512 100644 --- a/src/Gui/TextEdit.h +++ b/src/Gui/TextEdit.h @@ -64,6 +64,11 @@ public: private Q_SLOTS: void complete(); +Q_SIGNALS: + void showSearchBar(); + void findNext(); + void findPrevious(); + protected: void keyPressEvent(QKeyEvent *); From a1c9ab658caf5c8deb98f37a358d29eb1bfa84ee Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 26 Oct 2021 00:21:31 +0200 Subject: [PATCH 035/138] Gui: add support of OpenSCAD navigation style --- src/Gui/CMakeLists.txt | 1 + src/Gui/NavigationStyle.h | 17 ++ src/Gui/OpenSCADNavigationStyle.cpp | 347 ++++++++++++++++++++++++++++ src/Gui/SoFCDB.cpp | 1 + 4 files changed, 366 insertions(+) create mode 100644 src/Gui/OpenSCADNavigationStyle.cpp diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 3c6a607e9c..b901e97e8d 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -838,6 +838,7 @@ SET(View3D_CPP_SRCS BlenderNavigationStyle.cpp MayaGestureNavigationStyle.cpp OpenCascadeNavigationStyle.cpp + OpenSCADNavigationStyle.cpp TouchpadNavigationStyle.cpp GestureNavigationStyle.cpp SplitView3DInventor.cpp diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 2903ac0caa..659d8f9e03 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -410,6 +410,23 @@ private: SoMouseButtonEvent mouseDownConsumedEvent; }; +class GuiExport OpenSCADNavigationStyle : public UserNavigationStyle { + typedef UserNavigationStyle inherited; + + TYPESYSTEM_HEADER(); + +public: + OpenSCADNavigationStyle(); + ~OpenSCADNavigationStyle(); + const char* mouseButtons(ViewerMode); + +protected: + SbBool processSoEvent(const SoEvent * const ev); + +private: + SoMouseButtonEvent mouseDownConsumedEvent; +}; + } // namespace Gui Q_DECLARE_OPERATORS_FOR_FLAGS(Gui::NavigationStyle::RotationCenterModes) diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp new file mode 100644 index 0000000000..0148e46eb3 --- /dev/null +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -0,0 +1,347 @@ +/*************************************************************************** + * Copyright (c) 2021 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include "InventorAll.h" +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include "NavigationStyle.h" +#include "View3DInventorViewer.h" +#include "Application.h" +#include "MenuManager.h" +#include "MouseSelection.h" + +using namespace Gui; + +// ---------------------------------------------------------------------------------- + +/* TRANSLATOR Gui::OpenSCADNavigationStyle */ + +TYPESYSTEM_SOURCE(Gui::OpenSCADNavigationStyle, Gui::UserNavigationStyle) + +OpenSCADNavigationStyle::OpenSCADNavigationStyle() +{ +} + +OpenSCADNavigationStyle::~OpenSCADNavigationStyle() +{ +} + +const char* OpenSCADNavigationStyle::mouseButtons(ViewerMode mode) +{ + switch (mode) { + case NavigationStyle::SELECTION: + return QT_TR_NOOP("Press left mouse button"); + case NavigationStyle::PANNING: + return QT_TR_NOOP("Press right mouse button and move mouse"); + case NavigationStyle::DRAGGING: + return QT_TR_NOOP("Press left mouse button and move mouse"); + case NavigationStyle::ZOOMING: + return QT_TR_NOOP("Press SHIFT and middle or right mouse button"); + default: + return "No description"; + } +} + +SbBool OpenSCADNavigationStyle::processSoEvent(const SoEvent * const ev) +{ + // Events when in "ready-to-seek" mode are ignored, except those + // which influence the seek mode itself -- these are handled further + // up the inheritance hierarchy. + if (this->isSeekMode()) { return inherited::processSoEvent(ev); } + // Switch off viewing mode + if (!this->isSeekMode() && !this->isAnimating() && this->isViewing()) + this->setViewing(false); // by default disable viewing mode to render the scene + + const SoType type(ev->getTypeId()); + + const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); + const SbVec2s size(vp.getViewportSizePixels()); + const SbVec2f prevnormalized = this->lastmouseposition; + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), + (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + + this->lastmouseposition = posn; + + // Set to true if any event processing happened. Note that it is not + // necessary to restrict ourselves to only do one "action" for an + // event, we only need this flag to see if any processing happened + // at all. + SbBool processed = false; + + const ViewerMode curmode = this->currentmode; + ViewerMode newmode = curmode; + + // Mismatches in state of the modifier keys happens if the user + // presses or releases them outside the viewer window. + if (this->ctrldown != ev->wasCtrlDown()) { + this->ctrldown = ev->wasCtrlDown(); + } + if (this->shiftdown != ev->wasShiftDown()) { + this->shiftdown = ev->wasShiftDown(); + } + if (this->altdown != ev->wasAltDown()) { + this->altdown = ev->wasAltDown(); + } + + // give the nodes in the foreground root the chance to handle events (e.g color bar) + if (!viewer->isEditing()) { + processed = handleEventInForeground(ev); + if (processed) + return true; + } + + // Keyboard handling + if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { + const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; + const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; + switch (event->getKey()) { + case SoKeyboardEvent::LEFT_CONTROL: + case SoKeyboardEvent::RIGHT_CONTROL: + this->ctrldown = press; + break; + case SoKeyboardEvent::LEFT_SHIFT: + case SoKeyboardEvent::RIGHT_SHIFT: + this->shiftdown = press; + break; + case SoKeyboardEvent::LEFT_ALT: + case SoKeyboardEvent::RIGHT_ALT: + this->altdown = press; + break; + case SoKeyboardEvent::H: + processed = true; + viewer->saveHomePosition(); + break; + case SoKeyboardEvent::S: + case SoKeyboardEvent::HOME: + case SoKeyboardEvent::LEFT_ARROW: + case SoKeyboardEvent::UP_ARROW: + case SoKeyboardEvent::RIGHT_ARROW: + case SoKeyboardEvent::DOWN_ARROW: + if (!this->isViewing()) + this->setViewing(true); + break; + default: + break; + } + } + + // Mouse Button / Spaceball Button handling + if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { + const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev; + const int button = event->getButton(); + const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; + + switch (button) { + case SoMouseButtonEvent::BUTTON1: + this->lockrecenter = true; + this->button1down = press; + if (press && (curmode == NavigationStyle::SEEK_WAIT_MODE)) { + newmode = NavigationStyle::SEEK_MODE; + this->seekToPoint(pos); // implicitly calls interactiveCountInc() + processed = true; + } + else if (!press && (curmode == NavigationStyle::ZOOMING)) { + newmode = NavigationStyle::IDLE; + processed = true; + } + else if (!press && (curmode == NavigationStyle::DRAGGING)) { + this->setViewing(false); + processed = true; + } + else if (viewer->isEditing() && (curmode == NavigationStyle::SPINNING)) { + processed = true; + } + // issue #0002433: avoid to swallow the UP event if down the + // scene graph somewhere a dialog gets opened + else if (press) { + SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); + float dci = (float)QApplication::doubleClickInterval()/1000.0f; + // a double-click? + if (tmp.getValue() < dci) { + mouseDownConsumedEvent = *event; + mouseDownConsumedEvent.setTime(ev->getTime()); + processed = true; + } + else { + mouseDownConsumedEvent.setTime(ev->getTime()); + // 'ANY' is used to mark that we don't know yet if it will + // be a double-click event. + mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); + } + } + else if (!press) { + if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { + // now handle the postponed event + inherited::processSoEvent(&mouseDownConsumedEvent); + mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); + } + } + break; + case SoMouseButtonEvent::BUTTON2: + // If we are in edit mode then simply ignore the RMB events + // to pass the event to the base class. + this->lockrecenter = true; + this->button2down = press; + if (!viewer->isEditing()) { + // If we are in zoom or pan mode ignore RMB events otherwise + // the canvas doesn't get any release events + if (curmode != NavigationStyle::ZOOMING && + curmode != NavigationStyle::PANNING && + curmode != NavigationStyle::DRAGGING) { + if (this->isPopupMenuEnabled()) { + if (!press) { // release right mouse button + this->openPopupMenu(event->getPosition()); + } + } + } + } + // Alternative way of rotating & zooming + if (press && (curmode == NavigationStyle::PANNING || + curmode == NavigationStyle::ZOOMING)) { + newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); + this->centerTime = ev->getTime(); + processed = true; + } + else if (!press && (curmode == NavigationStyle::DRAGGING)) { + newmode = NavigationStyle::IDLE; + processed = true; + } + break; + case SoMouseButtonEvent::BUTTON3: + this->button3down = press; + if (press) { + this->centerTime = ev->getTime(); + float ratio = vp.getViewportAspectRatio(); + SbViewVolume vv = viewer->getSoRenderManager()->getCamera()->getViewVolume(ratio); + this->panningplane = vv.getPlane(viewer->getSoRenderManager()->getCamera()->focalDistance.getValue()); + this->lockrecenter = false; + } + else if (curmode == NavigationStyle::PANNING) { + newmode = NavigationStyle::IDLE; + processed = true; + } + break; + default: + break; + } + } + + // Mouse Movement handling + if (type.isDerivedFrom(SoLocation2Event::getClassTypeId())) { + this->lockrecenter = true; + const SoLocation2Event * const event = (const SoLocation2Event *) ev; + if (curmode == NavigationStyle::SELECTION) { + newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); + this->centerTime = ev->getTime(); + } + else if (curmode == NavigationStyle::ZOOMING) { + // OpenSCAD uses vertical mouse position, not horizontal + // this->zoomByCursor(posn, prevnormalized); + float value = (posn[1] - prevnormalized[1]) * 10.0f; + if (this->invertZoom) + value = -value; + zoom(viewer->getSoRenderManager()->getCamera(), value); + processed = true; + } + else if (curmode == NavigationStyle::PANNING) { + float ratio = vp.getViewportAspectRatio(); + panCamera(viewer->getSoRenderManager()->getCamera(), ratio, this->panningplane, posn, prevnormalized); + processed = true; + } + else if (curmode == NavigationStyle::DRAGGING) { + this->addToLog(event->getPosition(), event->getTime()); + this->spin(posn); + moveCursorPosition(); + processed = true; + } + } + + // Spaceball & Joystick handling + if (type.isDerivedFrom(SoMotion3Event::getClassTypeId())) { + const SoMotion3Event * const event = static_cast(ev); + if (event) + this->processMotionEvent(event); + processed = true; + } + + enum { + BUTTON1DOWN = 1 << 0, + BUTTON3DOWN = 1 << 1, + CTRLDOWN = 1 << 2, + SHIFTDOWN = 1 << 3, + BUTTON2DOWN = 1 << 4 + }; + unsigned int combo = + (this->button1down ? BUTTON1DOWN : 0) | + (this->button2down ? BUTTON2DOWN : 0) | + (this->button3down ? BUTTON3DOWN : 0) | + (this->ctrldown ? CTRLDOWN : 0) | + (this->shiftdown ? SHIFTDOWN : 0); + + switch (combo) { + case 0: + if (curmode == NavigationStyle::SPINNING) { break; } + newmode = NavigationStyle::IDLE; + break; + case BUTTON1DOWN: + if (newmode != NavigationStyle::DRAGGING) + newmode = NavigationStyle::SELECTION; + break; + case BUTTON2DOWN: + newmode = NavigationStyle::PANNING; + break; + case BUTTON3DOWN: + case SHIFTDOWN|BUTTON2DOWN: + case SHIFTDOWN|BUTTON3DOWN: + newmode = NavigationStyle::ZOOMING; + break; + default: + break; + } + + if (newmode != curmode) { + this->setViewingMode(newmode); + } + + // If not handled in this class, pass on upwards in the inheritance + // hierarchy. + if (!processed) + processed = inherited::processSoEvent(ev); + return processed; +} diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 3d3214962e..066cb3e6d8 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -184,6 +184,7 @@ void Gui::SoFCDB::init() TouchpadNavigationStyle ::init(); GestureNavigationStyle ::init(); OpenCascadeNavigationStyle ::init(); + OpenSCADNavigationStyle ::init(); GLGraphicsItem ::init(); GLFlagWindow ::init(); From 55851e93eb893c9ff2e80f3598851e0c57f47f94 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 26 Oct 2021 11:24:12 +0200 Subject: [PATCH 036/138] Arch: import IFC, fix merge material preference import --- src/Mod/Arch/importIFC.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 6162d3dfcd..2f88c7e65d 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1277,7 +1277,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): add_material = True if preferences['MERGE_MATERIALS']: for key in list(fcmats.keys()): - if key.startswith(name) \ + if fcmats[key].Label == name \ and "DiffuseColor" in mdict and "DiffuseColor" in fcmats[key].Material \ and mdict["DiffuseColor"] == fcmats[key].Material["DiffuseColor"]: mat = fcmats[key] From 326157cae81860ae5774a1a627f783ac6df355c7 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 26 Oct 2021 12:25:51 +0200 Subject: [PATCH 037/138] Arch: import IFC, materials, improve code but no changes on what is done --- src/Mod/Arch/importIFC.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 2f88c7e65d..956a6c04a7 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1252,7 +1252,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # print("colors:",colors) # print("mattable:",mattable) # print("materials:",materials) - fcmats = {} + added_mats = [] for material in materials: # print(material.id()) # get and set material name @@ -1273,27 +1273,34 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if m == material.id(): if o in colors and colors[o] is not None: mdict["DiffuseColor"] = str(colors[o]) + # on ifc import only the "DiffuseColor" of the material dictionary is initialized + # on editing material in Gui a lot more keys of the material dictionary are initialized even with empty values + # TODO: there should be a generic material obj init method which will be used by Arch Gui, import IFC, FEM, etc # merge materials with same name and color if setting in prefs is True add_material = True if preferences['MERGE_MATERIALS']: - for key in list(fcmats.keys()): - if fcmats[key].Label == name \ - and "DiffuseColor" in mdict and "DiffuseColor" in fcmats[key].Material \ - and mdict["DiffuseColor"] == fcmats[key].Material["DiffuseColor"]: - mat = fcmats[key] + for added_mat in added_mats: + if ( + # added_mat.Material["Name"] == name + # above does not work because Name is not initialized in material dict + added_mat.Label == name + and ("DiffuseColor" in mdict and "DiffuseColor" in added_mat.Material) + and mdict["DiffuseColor"] == added_mat.Material["DiffuseColor"] + ): + matobj = added_mat add_material = False # add a new material object if add_material is True: - mat = Arch.makeMaterial(name=name) + matobj = Arch.makeMaterial(name=name) if mdict: - mat.Material = mdict - fcmats[mat.Name] = mat + matobj.Material = mdict + added_mats.append(matobj) # fill material attribute of the objects for o,m in mattable.items(): if m == material.id(): if o in objects: if hasattr(objects[o],"Material"): - objects[o].Material = mat + objects[o].Material = matobj if FreeCAD.GuiUp: # the reason behind ... # there are files around in which the material color is different from the shape color @@ -1312,13 +1319,12 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): print("\nobject color != material color for object: ", o) print(" material color is used (most software uses shape color)") print(" obj: ", o, "label: ", objects[o].Label, " col: ", sh_color) - print(" mat: ", m, "label: ", mat.Label, " col: ", ma_color) + print(" mat: ", m, "label: ", matobj.Label, " col: ", ma_color) # print(" ", ifcfile[o]) # print(" ", ifcfile[m]) # print(" colors:") # print(" ", o, ": ", colors[o]) # print(" ", m, ": ", colors[m]) - if preferences['DEBUG'] and materials: print("done") # Layers @@ -1336,6 +1342,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences['DEBUG'] and layers: print("done") # restore links from full parametric definitions + for p in parametrics: l = FreeCAD.ActiveDocument.getObject(p[2]) if l: From 0685afec7ff5aaad249d6205c1909717631165e9 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 26 Oct 2021 12:36:37 +0200 Subject: [PATCH 038/138] Arch: import IFC, some comments --- src/Mod/Arch/importIFC.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 956a6c04a7..7ff503cd67 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -170,7 +170,7 @@ def getPreferences(): 'MERGE_MODE_STRUCT': p.GetInt("ifcImportModeStruct", 1), 'CREATE_CLONES': p.GetBool("ifcCreateClones", True), 'IMPORT_PROPERTIES': p.GetBool("ifcImportProperties", False), - 'SPLIT_LAYERS': p.GetBool("ifcSplitLayers", False), + 'SPLIT_LAYERS': p.GetBool("ifcSplitLayers", False), # wall layer, not layer for visual props 'FITVIEW_ONIMPORT': p.GetBool("ifcFitViewOnImport", False), 'ALLOW_INVALID': p.GetBool("ifcAllowInvalid", False), 'REPLACE_PROJECT': p.GetBool("ifcReplaceProject", False), @@ -1333,6 +1333,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # print(layers) for layer_name, layer_objects in layers.items(): lay = Draft.make_layer(layer_name) + # the method make_layer does some nasty debug prints lay_grp = [] for lobj_id in layer_objects: if lobj_id in objects: From e702c5d234d85261b03c7e491dc29b0d567dc264 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 26 Oct 2021 13:31:55 +0200 Subject: [PATCH 039/138] Gui: [skip ci] fix minor memory leak --- src/Gui/Application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index fa233c9ce9..e0ce35be61 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -170,6 +170,7 @@ struct ApplicationP ~ApplicationP() { delete macroMngr; + delete prefPackManager; } /// list of all handled documents From fc14f67bf63bfe670221062469395ee8c0ebb9c1 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 26 Oct 2021 13:52:50 +0200 Subject: [PATCH 040/138] Arch: import IFC, add preference to not import Layer --- src/Mod/Arch/importIFC.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 7ff503cd67..7d130c3674 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -174,7 +174,8 @@ def getPreferences(): 'FITVIEW_ONIMPORT': p.GetBool("ifcFitViewOnImport", False), 'ALLOW_INVALID': p.GetBool("ifcAllowInvalid", False), 'REPLACE_PROJECT': p.GetBool("ifcReplaceProject", False), - 'MULTICORE': p.GetInt("ifcMulticore", 0) + 'MULTICORE': p.GetInt("ifcMulticore", 0), + 'IMPORT_LAYER': p.GetBool("ifcImportLayer", True) } if preferences['MERGE_MODE_ARCH'] > 0: @@ -1332,6 +1333,8 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences['DEBUG'] and layers: print("Creating layers...", end="") # print(layers) for layer_name, layer_objects in layers.items(): + if preferences['IMPORT_LAYER']: + continue lay = Draft.make_layer(layer_name) # the method make_layer does some nasty debug prints lay_grp = [] From ea355833fda913943cbc4745b71223ca85a19bd9 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 26 Oct 2021 14:02:58 +0200 Subject: [PATCH 041/138] Arch: import IFC, fix new preference for not importing Layer --- src/Mod/Arch/importIFC.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 7d130c3674..d6ab9d2e58 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1333,7 +1333,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences['DEBUG'] and layers: print("Creating layers...", end="") # print(layers) for layer_name, layer_objects in layers.items(): - if preferences['IMPORT_LAYER']: + if preferences['IMPORT_LAYER'] is False: continue lay = Draft.make_layer(layer_name) # the method make_layer does some nasty debug prints From f3631c8ff1ff7fbc65ea7e9fb07e245b67fd266f Mon Sep 17 00:00:00 2001 From: luz paz Date: Tue, 26 Oct 2021 07:51:06 -0400 Subject: [PATCH 042/138] PD: Expose missing commands to translation in Measure dropdown menu Fixes https://github.com/FreeCAD/FreeCAD-translations/issues/6 --- src/Mod/PartDesign/Gui/Workbench.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 94cd370dc7..4ae7939dda 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -65,6 +65,8 @@ namespace bp = boost::placeholders; qApp->translate("Gui::TaskView::TaskWatcherCommands", "Create Geometry"); // qApp->translate("Workbench", "Measure"); + qApp->translate("Workbench", "Refresh"); + qApp->translate("Workbench", "Toggle 3D"); qApp->translate("Workbench", "Part Design Helper"); qApp->translate("Workbench", "Part Design Modeling"); #endif From 8f9ea8701db5360cb2e1a2394452e0a5279d3563 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 26 Oct 2021 16:42:07 +0200 Subject: [PATCH 043/138] Arch: import IFC, Layers, do not overwrite the imported object color and line color with the default layer values --- src/Mod/Arch/importIFC.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index d6ab9d2e58..7b240cc09f 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1335,8 +1335,13 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): for layer_name, layer_objects in layers.items(): if preferences['IMPORT_LAYER'] is False: continue - lay = Draft.make_layer(layer_name) # the method make_layer does some nasty debug prints + lay = Draft.make_layer(layer_name) + # ShapeColor and LineColor are not set, thus some some default values are used + # do not override the imported ShapeColor and LineColor with default layer values + if FreeCAD.GuiUp: + lay.ViewObject.OverrideLineColorChildren = False + lay.ViewObject.OverrideShapeColorChildren = False lay_grp = [] for lobj_id in layer_objects: if lobj_id in objects: From 4061c047a0b30fdf4661719d4f38f1ec49e74dfd Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 26 Oct 2021 16:58:42 +0200 Subject: [PATCH 044/138] Base: implement a way to test translator for testing purposes --- src/Base/Translate.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/Base/Translate.h | 9 +++++++++ 2 files changed, 50 insertions(+) diff --git a/src/Base/Translate.cpp b/src/Base/Translate.cpp index 50eeddfed5..2c6db05c5f 100644 --- a/src/Base/Translate.cpp +++ b/src/Base/Translate.cpp @@ -24,6 +24,7 @@ #include "Translate.h" #include +#include using namespace Base; @@ -62,6 +63,12 @@ Translate::Translate() &Translate::trNoop, "QT_TR_NOOP_UTF8(sourcetext)\n" "Same as QT_TR_NOOP"); + add_varargs_method("installTranslator", + &Translate::installTranslator, + "Install a translator for testing purposes"); + add_varargs_method("removeTranslators", + &Translate::removeTranslators, + "Remove test translators"); initialize("This module is the Translate module"); // register with Python } @@ -109,3 +116,37 @@ Py::Object Translate::trNoop(const Py::Tuple& args) throw Py::Exception(); return Py::Object(arg1); } + +Py::Object Translate::installTranslator(const Py::Tuple& args) +{ + char* Name; + if (!PyArg_ParseTuple(args.ptr(), "et","utf-8",&Name)) + throw Py::Exception(); + QString filename = QString::fromUtf8(Name); + PyMem_Free(Name); + + bool ok = false; + QFileInfo fi(filename); + std::shared_ptr translator(std::make_shared(nullptr)); + translator->setObjectName(fi.fileName()); + if (translator->load(filename)) { + qApp->installTranslator(translator.get()); + translators.push_back(translator); + ok = true; + } + + return Py::Boolean(ok); +} + +Py::Object Translate::removeTranslators(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + bool ok = true; + for (const auto& it : translators) { + ok &= QCoreApplication::removeTranslator(it.get()); + } + + translators.clear(); + return Py::Boolean(ok); +} diff --git a/src/Base/Translate.h b/src/Base/Translate.h index 1b75a2fe26..bb73095db8 100644 --- a/src/Base/Translate.h +++ b/src/Base/Translate.h @@ -26,10 +26,14 @@ #include #include +#include +#include +#include #ifndef FC_GLOBAL_H #include #endif + namespace Base { class BaseExport Translate : public Py::ExtensionModule @@ -43,6 +47,11 @@ private: Py::Object translateNoop(const Py::Tuple& args); Py::Object translateNoop3(const Py::Tuple& args); Py::Object trNoop(const Py::Tuple& args); + Py::Object installTranslator(const Py::Tuple& args); + Py::Object removeTranslators(const Py::Tuple& args); + +private: + std::list> translators; }; } // namespace Base From c2fdeae7aafc16bfeaf38138e8dd44e2eb3bdd16 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 26 Oct 2021 17:40:21 +0200 Subject: [PATCH 045/138] Spreadsheet: [skip ci] add sub-menu text for translation --- src/Mod/Spreadsheet/Gui/Workbench.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Spreadsheet/Gui/Workbench.cpp b/src/Mod/Spreadsheet/Gui/Workbench.cpp index ec6cec5e77..b7c2a4a978 100644 --- a/src/Mod/Spreadsheet/Gui/Workbench.cpp +++ b/src/Mod/Spreadsheet/Gui/Workbench.cpp @@ -47,6 +47,9 @@ using namespace Spreadsheet; #if 0 // needed for Qt's lupdate utility qApp->translate("Workbench", "Spreadsheet"); + qApp->translate("Workbench", "&Spreadsheet"); + qApp->translate("Workbench", "&Alignment"); + qApp->translate("Workbench", "&Styles"); #endif /// @namespace ImageGui @class Workbench From b60ed6140f37d8d6ff99567a1d741fe6cebe1b3c Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 26 Oct 2021 17:40:48 +0200 Subject: [PATCH 046/138] Spreadsheet: [skip ci] update translation --- .../Resources/translations/Spreadsheet_de.qm | Bin 14385 -> 14597 bytes .../Resources/translations/Spreadsheet_de.ts | 12 ++++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/Mod/Spreadsheet/Gui/Resources/translations/Spreadsheet_de.qm b/src/Mod/Spreadsheet/Gui/Resources/translations/Spreadsheet_de.qm index 74beda1985bb46bf8d2186de8ed6672066991d8a..955e330f2d70d8fc78d0905020f4fc0105483c1f 100644 GIT binary patch delta 169 zcmdm3&{`yFvm-^~)cZM8&nn97PguD3t`h?Ti^WEnLPmDx$t4U7#9bMo?3_z3fI1&cs8=wFH diff --git a/src/Mod/Spreadsheet/Gui/Resources/translations/Spreadsheet_de.ts b/src/Mod/Spreadsheet/Gui/Resources/translations/Spreadsheet_de.ts index 3ba265ee4c..6ac480beef 100644 --- a/src/Mod/Spreadsheet/Gui/Resources/translations/Spreadsheet_de.ts +++ b/src/Mod/Spreadsheet/Gui/Resources/translations/Spreadsheet_de.ts @@ -630,5 +630,17 @@ Tabelle.my_Alias_name anstelle von Tabelle.B1 Spreadsheet Kalkulationstabelle + + &Spreadsheet + &Kalkulationstabelle + + + &Alignment + &Ausrichtung + + + &Styles + &Stile + From 8aa340b9e72c0cf2d3a27a363f62448aeb0c98ab Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 26 Oct 2021 21:55:15 +0200 Subject: [PATCH 047/138] Gui: [skip ci] fix regression not to allow to have selected several objects when opening the placement dialog --- src/Gui/CommandDoc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp index 0f1a533b0e..b52f323731 100644 --- a/src/Gui/CommandDoc.cpp +++ b/src/Gui/CommandDoc.cpp @@ -1574,7 +1574,7 @@ void StdCmdPlacement::activated(int iMsg) bool StdCmdPlacement::isActive(void) { - return Gui::Selection().countObjectsOfType(App::GeoFeature::getClassTypeId()) == 1; + return Gui::Selection().countObjectsOfType(App::GeoFeature::getClassTypeId()) >= 1; } //=========================================================================== From f9da862dcc1f58f28a3370b696a38996fb70686d Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 26 Oct 2021 22:13:09 +0200 Subject: [PATCH 048/138] Arch: import IFC, fix import if replace project and only one building or one storey --- src/Mod/Arch/importIFC.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 7b240cc09f..0250422507 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1328,6 +1328,21 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # print(" ", m, ": ", colors[m]) if preferences['DEBUG'] and materials: print("done") + # Grouping everything if required + + # has to be before the Layer + # if REPLACE_PROJECT and only one storey and one building both are omitted + # the pure objects do not belong to any container, they will be added here + # if after Layer they are linked by Layer and will not be added here + if preferences['REPLACE_PROJECT'] and filename: + rootgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Group") + rootgroup.Label = os.path.basename(filename) + print(objects) + for key,obj in objects.items(): + # only add top-level objects + if not obj.InList: + rootgroup.addObject(obj) + # Layers if preferences['DEBUG'] and layers: print("Creating layers...", end="") @@ -1357,15 +1372,6 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if l: setattr(p[0],p[1],l) - # Grouping everything if required - if preferences['REPLACE_PROJECT'] and filename: - rootgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Group") - rootgroup.Label = os.path.basename(filename) - for key,obj in objects.items(): - # only add top-level objects - if not obj.InList: - rootgroup.addObject(obj) - # Save colordict in non-GUI mode if colordict and not FreeCAD.GuiUp: import json From 4c754f7af7800ec158d60ba1bd62b98b40dbbe60 Mon Sep 17 00:00:00 2001 From: luz paz Date: Tue, 26 Oct 2021 12:43:47 -0400 Subject: [PATCH 049/138] Arch: add missing paranthesis to strings Found while approving translations in crowdin --- src/Mod/Arch/ArchStructure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index e7f3e385b5..0b08fc4a73 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -677,9 +677,9 @@ class _Structure(ArchComponent.Component): if not "ComputedLength" in pl: obj.addProperty("App::PropertyDistance", "ComputedLength", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "The computed length of the extrusion path"), 1) if not "ToolOffsetFirst" in pl: - obj.addProperty("App::PropertyDistance", "ToolOffsetFirst", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Start offset distance along the extrusion path (positive: extend, negative: trim")) + obj.addProperty("App::PropertyDistance", "ToolOffsetFirst", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Start offset distance along the extrusion path (positive: extend, negative: trim)")) if not "ToolOffsetLast" in pl: - obj.addProperty("App::PropertyDistance", "ToolOffsetLast", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "End offset distance along the extrusion path (positive: extend, negative: trim")) + obj.addProperty("App::PropertyDistance", "ToolOffsetLast", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "End offset distance along the extrusion path (positive: extend, negative: trim)")) if not "BasePerpendicularToTool" in pl: obj.addProperty("App::PropertyBool", "BasePerpendicularToTool", "ExtrusionPath", QT_TRANSLATE_NOOP("App::Property", "Automatically align the Base of the Structure perpendicular to the Tool axis")) if not "BaseOffsetX" in pl: From 951a0be9c769e698a07bc903305c8be48a5306b1 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 27 Oct 2021 00:34:36 +0200 Subject: [PATCH 050/138] Base: [skip ci] expose Rotation.fromEuler() to Python --- src/Base/RotationPy.xml | 17 ++++++++++++++++- src/Base/RotationPyImp.cpp | 30 +++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Base/RotationPy.xml b/src/Base/RotationPy.xml index bfb60e2a70..bd06e47d57 100644 --- a/src/Base/RotationPy.xml +++ b/src/Base/RotationPy.xml @@ -83,13 +83,28 @@ + + + + fromEuler(angle1, angle2, angle3) + Set the Euler angles of this rotation + as yaw-pitch-roll in XY'Z'' convention. + or: + fromEuler(seq, angle1, angle2, angle3) + Get the Euler angles in a given sequence for this rotation. + + NOTE: The angles are in degree + + + toEuler() -> list Get the Euler angles of this rotation as yaw-pitch-roll in XY'Z'' convention - + NOTE: The angles are in degree + diff --git a/src/Base/RotationPyImp.cpp b/src/Base/RotationPyImp.cpp index a66997f3b9..556d2731ad 100644 --- a/src/Base/RotationPyImp.cpp +++ b/src/Base/RotationPyImp.cpp @@ -279,10 +279,38 @@ PyObject* RotationPy::slerp(PyObject * args) return new RotationPy(new Rotation(sl)); } +PyObject* RotationPy::fromEuler(PyObject * args) +{ + double A,B,C; + if (PyArg_ParseTuple(args, "ddd", &A, &B, &C)) { + this->getRotationPtr()->setYawPitchRoll(A,B,C); + Py_Return; + } + + PyErr_Clear(); + const char *seq; + if (PyArg_ParseTuple(args, "sddd", &seq, &A, &B, &C)) { + try { + getRotationPtr()->setEulerAngles( + Rotation::eulerSequenceFromName(seq), A, B, C); + Py_Return; + } + catch (const Base::Exception& e) { + e.setPyException(); + return nullptr; + } + } + + PyErr_SetString(PyExc_TypeError, "Expected arguments:\n" + "- float, float, float or\n" + "- string, float, float, float"); + return nullptr; +} + PyObject* RotationPy::toEuler(PyObject * args) { if (!PyArg_ParseTuple(args, "")) - return NULL; + return nullptr; double A,B,C; this->getRotationPtr()->getYawPitchRoll(A,B,C); From 7d21029ff5b6df4393c198ecd9654601457c98a8 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:55:38 +0200 Subject: [PATCH 051/138] Std: Fix URLs in CommandStd.cpp Updated all URLs to the new freecad.org domain. --- src/Gui/CommandStd.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Gui/CommandStd.cpp b/src/Gui/CommandStd.cpp index acb60bb89a..12d62f95ef 100644 --- a/src/Gui/CommandStd.cpp +++ b/src/Gui/CommandStd.cpp @@ -502,7 +502,7 @@ StdCmdOnlineHelpWebsite::StdCmdOnlineHelpWebsite() void StdCmdOnlineHelpWebsite::activated(int iMsg) { Q_UNUSED(iMsg); - std::string defaulturl = QCoreApplication::translate(this->className(),"http://www.freecadweb.org/wiki/Online_Help_Toc").toStdString(); + std::string defaulturl = QCoreApplication::translate(this->className(),"https://wiki.freecad.org/Online_Help_Toc").toStdString(); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Websites"); std::string url = hURLGrp->GetASCII("OnlineHelp", defaulturl.c_str()); hURLGrp->SetASCII("OnlineHelp", url.c_str()); @@ -529,9 +529,9 @@ StdCmdFreeCADDonation::StdCmdFreeCADDonation() void StdCmdFreeCADDonation::activated(int iMsg) { - Q_UNUSED(iMsg); + Q_UNUSED(iMsg); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Websites"); - std::string url = hURLGrp->GetASCII("DonatePage", "https://wiki.freecadweb.org/Donate"); + std::string url = hURLGrp->GetASCII("DonatePage", "https://wiki.freecad.org/Donate"); hURLGrp->SetASCII("DonatePage", url.c_str()); OpenURLInBrowser(url.c_str()); } @@ -557,7 +557,7 @@ StdCmdFreeCADWebsite::StdCmdFreeCADWebsite() void StdCmdFreeCADWebsite::activated(int iMsg) { Q_UNUSED(iMsg); - std::string defaulturl = QCoreApplication::translate(this->className(),"http://www.freecadweb.org").toStdString(); + std::string defaulturl = QCoreApplication::translate(this->className(),"https://www.freecad.org").toStdString(); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Websites"); std::string url = hURLGrp->GetASCII("WebPage", defaulturl.c_str()); hURLGrp->SetASCII("WebPage", url.c_str()); @@ -585,7 +585,7 @@ StdCmdFreeCADUserHub::StdCmdFreeCADUserHub() void StdCmdFreeCADUserHub::activated(int iMsg) { Q_UNUSED(iMsg); - std::string defaulturl = QCoreApplication::translate(this->className(),"http://www.freecadweb.org/wiki/User_hub").toStdString(); + std::string defaulturl = QCoreApplication::translate(this->className(),"https://wiki.freecad.org/User_hub").toStdString(); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Websites"); std::string url = hURLGrp->GetASCII("Documentation", defaulturl.c_str()); hURLGrp->SetASCII("Documentation", url.c_str()); @@ -613,7 +613,7 @@ StdCmdFreeCADPowerUserHub::StdCmdFreeCADPowerUserHub() void StdCmdFreeCADPowerUserHub::activated(int iMsg) { Q_UNUSED(iMsg); - std::string defaulturl = QCoreApplication::translate(this->className(),"http://www.freecadweb.org/wiki/Power_users_hub").toStdString(); + std::string defaulturl = QCoreApplication::translate(this->className(),"https://wiki.freecad.org/Power_users_hub").toStdString(); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Websites"); std::string url = hURLGrp->GetASCII("PowerUsers", defaulturl.c_str()); hURLGrp->SetASCII("PowerUsers", url.c_str()); @@ -641,7 +641,7 @@ StdCmdFreeCADForum::StdCmdFreeCADForum() void StdCmdFreeCADForum::activated(int iMsg) { Q_UNUSED(iMsg); - std::string defaulturl = QCoreApplication::translate(this->className(),"http://forum.freecadweb.org").toStdString(); + std::string defaulturl = QCoreApplication::translate(this->className(),"https://forum.freecad.org").toStdString(); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Websites"); std::string url = hURLGrp->GetASCII("UserForum", defaulturl.c_str()); hURLGrp->SetASCII("UserForum", url.c_str()); @@ -669,7 +669,7 @@ StdCmdFreeCADFAQ::StdCmdFreeCADFAQ() void StdCmdFreeCADFAQ::activated(int iMsg) { Q_UNUSED(iMsg); - std::string defaulturl = QCoreApplication::translate(this->className(),"http://www.freecadweb.org/wiki/FAQ").toStdString(); + std::string defaulturl = QCoreApplication::translate(this->className(),"https://wiki.freecad.org/Frequently_asked_questions").toStdString(); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Websites"); std::string url = hURLGrp->GetASCII("FAQ", defaulturl.c_str()); hURLGrp->SetASCII("FAQ", url.c_str()); @@ -697,7 +697,7 @@ StdCmdPythonWebsite::StdCmdPythonWebsite() void StdCmdPythonWebsite::activated(int iMsg) { Q_UNUSED(iMsg); - OpenURLInBrowser("http://python.org"); + OpenURLInBrowser("https://www.python.org"); } //=========================================================================== @@ -852,7 +852,7 @@ Gui::Action * StdCmdUserEditMode::createAction(void) pcAction->setDropDownMenu(true); pcAction->setIsMode(true); applyCommandData(this->className(), pcAction); - + for (auto const &uem : Gui::Application::Instance->listUserEditModes()) { QAction* act = pcAction->addAction(QString()); auto modeName = QString::fromStdString(uem.second); @@ -860,7 +860,7 @@ Gui::Action * StdCmdUserEditMode::createAction(void) act->setIcon(BitmapFactory().iconFromTheme(qPrintable(QString::fromLatin1("Std_UserEditMode")+modeName))); act->setObjectName(QString::fromLatin1("Std_UserEditMode")+modeName); act->setWhatsThis(QString::fromLatin1(getWhatsThis())); - + if (uem.first == 0) { pcAction->setIcon(act->icon()); act->setChecked(true); From 082989ac864dde96b657ca42e629acf89721195d Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Wed, 27 Oct 2021 08:17:48 +0200 Subject: [PATCH 052/138] Arch: import IFC, replace project, comment debug print --- src/Mod/Arch/importIFC.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 0250422507..d94c2d4227 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1337,7 +1337,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences['REPLACE_PROJECT'] and filename: rootgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Group") rootgroup.Label = os.path.basename(filename) - print(objects) + # print(objects) for key,obj in objects.items(): # only add top-level objects if not obj.InList: From 8b7718f8231f60c24f06d6178fc2b4a615e13375 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Wed, 27 Oct 2021 08:32:10 +0200 Subject: [PATCH 053/138] Draft: Fix merge layers The "Merge layer duplicates" Tree view context menu option for LayerContainers did not work. Additionally: - Using `openTransaction` and `commitTransaction` is required for the `merge_by_name` function but also for the `add_layer` function. Otherwise undo-redo does not work properly. - Have replaced `translate("Draft", ...)` with `_tr(...)`. --- .../Draft/draftviewproviders/view_layer.py | 103 +++++++++--------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/src/Mod/Draft/draftviewproviders/view_layer.py b/src/Mod/Draft/draftviewproviders/view_layer.py index b7231ae1f1..1f88700105 100644 --- a/src/Mod/Draft/draftviewproviders/view_layer.py +++ b/src/Mod/Draft/draftviewproviders/view_layer.py @@ -1,6 +1,7 @@ # *************************************************************************** # * Copyright (c) 2014 Yorik van Havre * # * Copyright (c) 2020 Eliud Cabrera Castillo * +# * Copyright (c) 2021 FreeCAD Developers * # * * # * This file is part of the FreeCAD CAx development system. * # * * @@ -37,7 +38,7 @@ import FreeCAD as App import FreeCADGui as Gui from draftutils.messages import _msg -from draftutils.translate import translate +from draftutils.translate import _tr from draftobjects.layer import Layer @@ -355,13 +356,13 @@ class ViewProviderLayer: def setupContextMenu(self, vobj, menu): """Set up actions to perform in the context menu.""" action1 = QtGui.QAction(QtGui.QIcon(":/icons/button_right.svg"), - translate("draft", "Activate this layer"), + _tr("Activate this layer"), menu) action1.triggered.connect(self.activate) menu.addAction(action1) action2 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_SelectGroup.svg"), - translate("draft", "Select layer contents"), + _tr("Select layer contents"), menu) action2.triggered.connect(self.select_contents) menu.addAction(action2) @@ -399,80 +400,76 @@ class ViewProviderLayerContainer: def setupContextMenu(self, vobj, menu): """Set up actions to perform in the context menu.""" action1 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Layer.svg"), - translate("Draft", "Merge layer duplicates"), + _tr("Merge layer duplicates"), menu) action1.triggered.connect(self.merge_by_name) menu.addAction(action1) action2 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_NewLayer.svg"), - translate("Draft", "Add new layer"), + _tr("Add new layer"), menu) action2.triggered.connect(self.add_layer) menu.addAction(action2) def merge_by_name(self): - """Merge the layers that have the same name.""" + """Merge the layers that have the same base label.""" if not hasattr(self, "Object") or not hasattr(self.Object, "Group"): return - obj = self.Object + doc = App.ActiveDocument + doc.openTransaction(_tr("Merge layer duplicates")) - layers = list() - for iobj in obj.Group: - if hasattr(iobj, "Proxy") and isinstance(iobj.Proxy, Layer): - layers.append(iobj) + layer_container = self.Object + layers = [] + for obj in layer_container.Group: + if hasattr(obj, "Proxy") and isinstance(obj.Proxy, Layer): + layers.append(obj) - to_delete = list() + to_delete = [] for layer in layers: - # Test the last three characters of the layer's Label to see - # if it's a number, like `'Layer017'` - if (layer.Label[-1].isdigit() - and layer.Label[-2].isdigit() - and layer.Label[-3].isdigit()): - # If the object inside the layer has the same Label - # as the layer, save this object - orig = None - for ol in layer.OutList: - if ol.Label == layer.Label[:-3].strip(): - orig = ol - break + # Remove trailing digits (usually 3 but there might be more) and + # trailing spaces from Label before comparing: + base_label = layer.Label.rstrip("0123456789 ") - # Go into the objects that reference this layer object - # and set the layer property with the previous `orig` - # object found - # Editor: when is this possible? Maybe if a layer is inside - # another layer? Currently the code doesn't allow this - # so maybe this was a previous behavior that was disabled - # in `ViewProviderLayer`. - if orig: - for par in layer.InList: - for prop in par.PropertiesList: - if getattr(par, prop) == layer: - _msg("Changed property '" + prop - + "' of object " + par.Label - + " from " + layer.Label - + " to " + orig.Label) - setattr(par, prop, orig) - to_delete.append(layer) + # Try to find the `'base'` layer: + base = None + for other_layer in layers: + if ((not other_layer in to_delete) # Required if there are duplicate labels. + and other_layer != layer + and other_layer.Label.upper() == base_label.upper()): + base = other_layer + break + + if base: + if layer.Group: + base_group = base.Group + for obj in layer.Group: + if not obj in base_group: + base_group.append(obj) + base.Group = base_group + to_delete.append(layer) + elif layer.Label != base_label: + _msg(_tr("Relabeling layer:") + + " '{}' -> '{}'".format(layer.Label, base_label)) + layer.Label = base_label for layer in to_delete: - if not layer.InList: - _msg("Merging duplicate layer: " + layer.Label) - App.ActiveDocument.removeObject(layer.Name) - elif len(layer.InList) == 1: - first = layer.InList[0] + _msg(_tr("Merging layer:") + " '{}'".format(layer.Label)) + doc.removeObject(layer.Name) - if first.isDerivedFrom("App::DocumentObjectGroup"): - _msg("Merging duplicate layer: " + layer.Label) - App.ActiveDocument.removeObject(layer.Name) - else: - _msg("InList not empty. " - "Unable to delete layer: " + layer.Label) + doc.recompute() + doc.commitTransaction() def add_layer(self): """Creates a new layer""" import Draft + + doc = App.ActiveDocument + doc.openTransaction(_tr("Add new layer")) + Draft.make_layer() - App.ActiveDocument.recompute() + + doc.recompute() + doc.commitTransaction() def __getstate__(self): """Return a tuple of objects to save or None.""" From 4c886771d1d0661270ed7eb8b52821c317cad71c Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 27 Oct 2021 10:40:35 +0200 Subject: [PATCH 054/138] Base: harmonize Python API of Rotation class --- src/Base/RotationPy.xml | 20 ++++++++----- src/Base/RotationPyImp.cpp | 57 ++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/src/Base/RotationPy.xml b/src/Base/RotationPy.xml index bd06e47d57..35e804d310 100644 --- a/src/Base/RotationPy.xml +++ b/src/Base/RotationPy.xml @@ -83,30 +83,36 @@ - + - fromEuler(angle1, angle2, angle3) + setYawPitchRoll(angle1, angle2, angle3) Set the Euler angles of this rotation as yaw-pitch-roll in XY'Z'' convention. - or: - fromEuler(seq, angle1, angle2, angle3) - Get the Euler angles in a given sequence for this rotation. NOTE: The angles are in degree - + - toEuler() -> list + getYawPitchRoll() -> list Get the Euler angles of this rotation as yaw-pitch-roll in XY'Z'' convention NOTE: The angles are in degree + + + + setEulerAngles(seq, angle1, angle2, angle3) + Set the Euler angles in a given sequence for this rotation. + 'seq' is the Euler sequence name. You get all possible values with toEulerAngles() + + + diff --git a/src/Base/RotationPyImp.cpp b/src/Base/RotationPyImp.cpp index 556d2731ad..1449f5ebe4 100644 --- a/src/Base/RotationPyImp.cpp +++ b/src/Base/RotationPyImp.cpp @@ -279,35 +279,16 @@ PyObject* RotationPy::slerp(PyObject * args) return new RotationPy(new Rotation(sl)); } -PyObject* RotationPy::fromEuler(PyObject * args) +PyObject* RotationPy::setYawPitchRoll(PyObject * args) { double A,B,C; - if (PyArg_ParseTuple(args, "ddd", &A, &B, &C)) { - this->getRotationPtr()->setYawPitchRoll(A,B,C); - Py_Return; - } - - PyErr_Clear(); - const char *seq; - if (PyArg_ParseTuple(args, "sddd", &seq, &A, &B, &C)) { - try { - getRotationPtr()->setEulerAngles( - Rotation::eulerSequenceFromName(seq), A, B, C); - Py_Return; - } - catch (const Base::Exception& e) { - e.setPyException(); - return nullptr; - } - } - - PyErr_SetString(PyExc_TypeError, "Expected arguments:\n" - "- float, float, float or\n" - "- string, float, float, float"); - return nullptr; + if (!PyArg_ParseTuple(args, "ddd", &A, &B, &C)) + return nullptr; + this->getRotationPtr()->setYawPitchRoll(A,B,C); + Py_Return; } -PyObject* RotationPy::toEuler(PyObject * args) +PyObject* RotationPy::getYawPitchRoll(PyObject * args) { if (!PyArg_ParseTuple(args, "")) return nullptr; @@ -321,11 +302,29 @@ PyObject* RotationPy::toEuler(PyObject * args) return Py::new_reference_to(tuple); } +PyObject* RotationPy::setEulerAngles(PyObject * args) +{ + const char *seq; + double A,B,C; + if (!PyArg_ParseTuple(args, "sddd", &seq, &A, &B, &C)) + return nullptr; + + try { + getRotationPtr()->setEulerAngles( + Rotation::eulerSequenceFromName(seq), A, B, C); + Py_Return; + } + catch (const Base::Exception& e) { + e.setPyException(); + return nullptr; + } +} + PyObject* RotationPy::toEulerAngles(PyObject * args) { const char *seq = nullptr; if (!PyArg_ParseTuple(args, "|s", &seq)) - return NULL; + return nullptr; if (!seq) { Py::List res; for (int i=1; igetRotationPtr()->getYawPitchRoll(A,B,C); return PyFloat_FromDouble(C); } - return 0; + else if (strcmp(attr, "toEuler") == 0) { + Py::Object self(const_cast(this), false); + return Py::new_reference_to(self.getAttr("getYawPitchRoll")); + } + return nullptr; } int RotationPy::setCustomAttributes(const char* attr, PyObject* obj) From cd91c73382ad078f1857296faeef1f97f71b9e61 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 27 Oct 2021 10:59:34 +0200 Subject: [PATCH 055/138] Base: [skip ci] in Rotation class allow to set angle in radian App.Rotation(axis, angle) still defines the angle in degree App.Rotation(axis, Degree=angle) does the same as above App.Rotation(axis, Radian=angle) defines the angle in radian --- src/Base/RotationPyImp.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Base/RotationPyImp.cpp b/src/Base/RotationPyImp.cpp index 1449f5ebe4..f62c745681 100644 --- a/src/Base/RotationPyImp.cpp +++ b/src/Base/RotationPyImp.cpp @@ -60,7 +60,7 @@ PyObject *RotationPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // P } // constructor method -int RotationPy::PyInit(PyObject* args, PyObject* /*kwd*/) +int RotationPy::PyInit(PyObject* args, PyObject* kwds) { PyObject* o; if (PyArg_ParseTuple(args, "")) { @@ -76,10 +76,18 @@ int RotationPy::PyInit(PyObject* args, PyObject* /*kwd*/) PyErr_Clear(); double angle; - if (PyArg_ParseTuple(args, "O!d", &(Base::VectorPy::Type), &o, &angle)) { - // NOTE: The last parameter defines the rotation angle in degree. - getRotationPtr()->setValue(static_cast(o)->value(), Base::toRadians(angle)); - return 0; + static char *kw_deg[] = {"Axis", "Degree", nullptr}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "O!d", kw_deg, &(Base::VectorPy::Type), &o, &angle)) { + // NOTE: The last parameter defines the rotation angle in degree. + getRotationPtr()->setValue(static_cast(o)->value(), Base::toRadians(angle)); + return 0; + } + + PyErr_Clear(); + static char *kw_rad[] = {"Axis", "Radian", nullptr}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "O!d", kw_rad, &(Base::VectorPy::Type), &o, &angle)) { + getRotationPtr()->setValue(static_cast(o)->value(), angle); + return 0; } PyErr_Clear(); From b159144e130f786202b80f8e1b5b8b0f9c923334 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 27 Oct 2021 17:02:07 +0200 Subject: [PATCH 056/138] Base: [skip ci] handle gimbal lock when computing Euler angles --- src/Base/Rotation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Base/Rotation.cpp b/src/Base/Rotation.cpp index 646731561d..67a6e5d525 100644 --- a/src/Base/Rotation.cpp +++ b/src/Base/Rotation.cpp @@ -683,7 +683,7 @@ void Rotation::getYawPitchRoll(double& y, double& p, double& r) const // south pole y = 0.0; p = -D_PI/2.0; - r = -2.0 * atan2(quat[0],quat[3]); + r = 2.0 * atan2(quat[0],quat[3]); } else { y = atan2(2.0*(q01+q23),(q00+q33)-(q11+q22)); From cd8ce95f53d4bf95d906912d8a93665b47cd592f Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 27 Oct 2021 19:26:26 +0200 Subject: [PATCH 057/138] Base: [skip ci] C++11: modernize use nullptr (replaces NULL or 0) --- src/Base/RotationPyImp.cpp | 86 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/src/Base/RotationPyImp.cpp b/src/Base/RotationPyImp.cpp index f62c745681..ea467a678b 100644 --- a/src/Base/RotationPyImp.cpp +++ b/src/Base/RotationPyImp.cpp @@ -35,7 +35,7 @@ using namespace Base; // returns a string which represents the object e.g. when printed in python -std::string RotationPy::representation(void) const +std::string RotationPy::representation() const { RotationPy::PointerType ptr = reinterpret_cast(_pcTwinPointer); Py::Float q0(ptr->getValue()[0]); @@ -44,10 +44,10 @@ std::string RotationPy::representation(void) const Py::Float q3(ptr->getValue()[3]); std::stringstream str; str << "Rotation ("; - str << (std::string)q0.repr() << ", " - << (std::string)q1.repr() << ", " - << (std::string)q2.repr() << ", " - << (std::string)q3.repr(); + str << static_cast(q0.repr()) << ", " + << static_cast(q1.repr()) << ", " + << static_cast(q2.repr()) << ", " + << static_cast(q3.repr()); str << ")"; return str.str(); @@ -170,7 +170,7 @@ int RotationPy::PyInit(PyObject* args, PyObject* kwds) PyErr_Clear(); PyObject *v3; - char *priority = nullptr; + const char *priority = nullptr; if (PyArg_ParseTuple(args, "O!O!O!|s", &(Base::VectorPy::Type), &v1, &(Base::VectorPy::Type), &v2, &(Base::VectorPy::Type), &v3, @@ -216,11 +216,11 @@ PyObject* RotationPy::richCompare(PyObject *v, PyObject *w, int op) Base::Rotation r1 = *static_cast(v)->getRotationPtr(); Base::Rotation r2 = *static_cast(w)->getRotationPtr(); - PyObject *res=0; + PyObject *res=nullptr; if (op != Py_EQ && op != Py_NE) { PyErr_SetString(PyExc_TypeError, "no ordering relation is defined for Rotation"); - return 0; + return nullptr; } else if (op == Py_EQ) { res = (r1 == r2) ? Py_True : Py_False; @@ -243,7 +243,7 @@ PyObject* RotationPy::richCompare(PyObject *v, PyObject *w, int op) PyObject* RotationPy::invert(PyObject * args) { if (!PyArg_ParseTuple(args, "")) - return 0; + return nullptr; this->getRotationPtr()->invert(); Py_Return; } @@ -251,7 +251,7 @@ PyObject* RotationPy::invert(PyObject * args) PyObject* RotationPy::inverted(PyObject * args) { if (!PyArg_ParseTuple(args, "")) - return 0; + return nullptr; Rotation mult = this->getRotationPtr()->inverse(); return new RotationPy(new Rotation(mult)); } @@ -260,7 +260,7 @@ PyObject* RotationPy::multiply(PyObject * args) { PyObject *rot; if (!PyArg_ParseTuple(args, "O!", &(RotationPy::Type), &rot)) - return NULL; + return nullptr; Rotation mult = (*getRotationPtr()) * (*static_cast(rot)->getRotationPtr()); return new RotationPy(new Rotation(mult)); } @@ -269,7 +269,7 @@ PyObject* RotationPy::multVec(PyObject * args) { PyObject *obj; if (!PyArg_ParseTuple(args, "O!", &(VectorPy::Type), &obj)) - return NULL; + return nullptr; Base::Vector3d vec(static_cast(obj)->value()); getRotationPtr()->multVec(vec, vec); return new VectorPy(new Vector3d(vec)); @@ -368,7 +368,7 @@ PyObject* RotationPy::isSame(PyObject *args) PyObject *rot; double tol = 0.0; if (!PyArg_ParseTuple(args, "O!|d", &(RotationPy::Type), &rot, &tol)) - return NULL; + return nullptr; Base::Rotation rot1 = * getRotationPtr(); Base::Rotation rot2 = * static_cast(rot)->getRotationPtr(); bool same = tol > 0.0 ? rot1.isSame(rot2, tol) : rot1.isSame(rot2); @@ -378,7 +378,7 @@ PyObject* RotationPy::isSame(PyObject *args) PyObject* RotationPy::isIdentity(PyObject *args) { if (!PyArg_ParseTuple(args, "")) - return NULL; + return nullptr; bool null = getRotationPtr()->isIdentity(); return Py_BuildValue("O", (null ? Py_True : Py_False)); } @@ -386,12 +386,12 @@ PyObject* RotationPy::isIdentity(PyObject *args) PyObject* RotationPy::isNull(PyObject *args) { if (!PyArg_ParseTuple(args, "")) - return NULL; + return nullptr; bool null = getRotationPtr()->isNull(); return Py_BuildValue("O", (null ? Py_True : Py_False)); } -Py::Tuple RotationPy::getQ(void) const +Py::Tuple RotationPy::getQ() const { double q0, q1, q2, q3; this->getRotationPtr()->getValue(q0,q1,q2,q3); @@ -406,14 +406,14 @@ Py::Tuple RotationPy::getQ(void) const void RotationPy::setQ(Py::Tuple arg) { - double q0 = (double)Py::Float(arg.getItem(0)); - double q1 = (double)Py::Float(arg.getItem(1)); - double q2 = (double)Py::Float(arg.getItem(2)); - double q3 = (double)Py::Float(arg.getItem(3)); + double q0 = static_cast(Py::Float(arg.getItem(0))); + double q1 = static_cast(Py::Float(arg.getItem(1))); + double q2 = static_cast(Py::Float(arg.getItem(2))); + double q3 = static_cast(Py::Float(arg.getItem(3))); this->getRotationPtr()->setValue(q0,q1,q2,q3); } -Py::Object RotationPy::getRawAxis(void) const +Py::Object RotationPy::getRawAxis() const { Base::Vector3d axis; double angle; this->getRotationPtr()->getRawValue(axis, angle); @@ -435,7 +435,7 @@ void RotationPy::setAxis(Py::Object arg) this->getRotationPtr()->setValue(axis, angle); } -Py::Float RotationPy::getAngle(void) const +Py::Float RotationPy::getAngle() const { Base::Vector3d axis; double angle; this->getRotationPtr()->getValue(axis, angle); @@ -560,19 +560,17 @@ PyObject* RotationPy::number_multiply_handler(PyObject *self, PyObject *other) } PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_power_handler (PyObject* self, PyObject* other, PyObject* arg) { if (!PyObject_TypeCheck(self, &(RotationPy::Type)) || - - !PyLong_Check(other) - || arg != Py_None - ) + !PyLong_Check(other) || + arg != Py_None) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } Rotation a = static_cast(self)->value(); @@ -591,49 +589,49 @@ PyObject * RotationPy::number_power_handler (PyObject* self, PyObject* other, Py PyObject* RotationPy::number_add_handler(PyObject * /*self*/, PyObject * /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject* RotationPy::number_subtract_handler(PyObject * /*self*/, PyObject * /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_divide_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_remainder_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_divmod_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_negative_handler (PyObject* /*self*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_positive_handler (PyObject* /*self*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_absolute_handler (PyObject* /*self*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } int RotationPy::number_nonzero_handler (PyObject* /*self*/) @@ -644,48 +642,48 @@ int RotationPy::number_nonzero_handler (PyObject* /*self*/) PyObject * RotationPy::number_invert_handler (PyObject* /*self*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_lshift_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_rshift_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_and_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_xor_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_or_handler (PyObject* /*self*/, PyObject* /*other*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_int_handler (PyObject * /*self*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } PyObject * RotationPy::number_float_handler (PyObject * /*self*/) { PyErr_SetString(PyExc_NotImplementedError, "Not implemented"); - return 0; + return nullptr; } From 5f82ec99fc2e33686ded99762ce4b028cdb8e963 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Wed, 27 Oct 2021 23:07:46 +0200 Subject: [PATCH 058/138] Arch: IFC export and import, some code formating (only very clearly ones) --- src/Mod/Arch/exportIFC.py | 36 +++++++++++++++++---------------- src/Mod/Arch/exportIFCHelper.py | 7 +++---- src/Mod/Arch/importIFC.py | 21 ++++++++++--------- src/Mod/Arch/importIFCHelper.py | 7 ++++--- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/Mod/Arch/exportIFC.py b/src/Mod/Arch/exportIFC.py index 3043103f40..6c4fc01e52 100644 --- a/src/Mod/Arch/exportIFC.py +++ b/src/Mod/Arch/exportIFC.py @@ -961,7 +961,7 @@ def export(exportList, filename, colors=None, preferences=None): for c in objs: if c.Name in products and c.Name not in treated: prod = products[c.Name] - if prod.is_a()=='IfcSpace': + if prod.is_a() == 'IfcSpace': spaces.append(prod) else: buildingelements.append(prod) @@ -1193,7 +1193,7 @@ def export(exportList, filename, colors=None, preferences=None): if defaulthost: spaces, buildingelements = [],[] for entity in untreated: - if entity.is_a()=="IfcSpace": + if entity.is_a() == "IfcSpace": spaces.append(entity) else: buildingelements.append(entity) @@ -1214,7 +1214,7 @@ def export(exportList, filename, colors=None, preferences=None): '', buildingelements, defaulthost - ) + ) else: # no default host: aggregate unassigned objects directly under the IfcProject - WARNING: NON STANDARD if preferences['DEBUG']: print("WARNING - Default building generation is disabled. You are producing a non-standard file.") @@ -1258,7 +1258,7 @@ def export(exportList, filename, colors=None, preferences=None): rgb = tuple([float(f) for f in m.Material[colorslot].strip("()").split(",")]) break if rgb: - psa = ifcbin.createIfcPresentationStyleAssignment(l,rgb[0],rgb[1],rgb[2],ifc4=(preferences["SCHEMA"]=="IFC4")) + psa = ifcbin.createIfcPresentationStyleAssignment(l,rgb[0],rgb[1],rgb[2],ifc4=(preferences["SCHEMA"] == "IFC4")) isi = ifcfile.createIfcStyledItem(None,[psa],None) isr = ifcfile.createIfcStyledRepresentation(context,"Style","Material",[isi]) imd = ifcfile.createIfcMaterialDefinitionRepresentation(None,None,[isr],mat) @@ -1648,7 +1648,7 @@ def getIfcTypeFromObj(obj): if ifctype in translationtable.keys(): ifctype = translationtable[ifctype] - if not "::" in ifctype: + if "::" not in ifctype: ifctype = "Ifc" + ifctype elif ifctype == "IfcApp::DocumentObjctGroup": ifctype = "IfcGroup" @@ -1737,7 +1737,6 @@ def buildAddress(obj,ifcfile): def createCurve(ifcfile,wire,scaling=1.0): - "creates an IfcCompositeCurve from a shape" segments = [] @@ -1810,7 +1809,6 @@ def createCurve(ifcfile,wire,scaling=1.0): def getEdgesAngle(edge1, edge2): - """ getEdgesAngle(edge1, edge2): returns a angle between two edges.""" vec1 = vec(edge1) @@ -1821,7 +1819,6 @@ def getEdgesAngle(edge1, edge2): def checkRectangle(edges): - """ checkRectangle(edges=[]): This function checks whether the given form is a rectangle or not. It will return True when edges form a rectangular shape or return False when edges do not form a rectangular shape.""" @@ -1841,7 +1838,6 @@ def checkRectangle(edges): def getProfile(ifcfile,p): - """returns an IFC profile definition from a shape""" import Part @@ -1905,8 +1901,18 @@ def getProfile(ifcfile,p): return profile -def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1,colors=None,preferences=None,forceclone=False,skipshape=False): - +def getRepresentation( + ifcfile, + context, + obj, + forcebrep=False, + subtraction=False, + tessellation=1, + colors=None, + preferences=None, + forceclone=False, + skipshape=False +): """returns an IfcShapeRepresentation object or None. forceclone can be False (does nothing), "store" or True (stores the object as clone base) or a Vector (creates a clone)""" @@ -1924,7 +1930,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess if ((not subtraction) and (not forcebrep)) or forceclone: if forceclone: - if not obj.Name in clones: + if obj.Name not in clones: clones[obj.Name] = [] for k,v in clones.items(): if (obj.Name == k) or (obj.Name in v): @@ -2051,7 +2057,6 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess solidType = "SweptSolid" shapetype = "extrusion" - if (not shapes) and (not skipshape): # check if we keep a null shape (additions-only object) @@ -2382,7 +2387,6 @@ def getText(field,obj): def getAxisContext(ifcfile): - """gets or creates an axis context""" contexts = ifcfile.by_type("IfcGeometricRepresentationContext") @@ -2393,12 +2397,11 @@ def getAxisContext(ifcfile): if ctx.ContextIdentifier == "Axis": return ctx ctx = contexts[0] # arbitrarily take the first one... - nctx = ifcfile.createIfcGeometricRepresentationSubContext('Axis','Model',None,None,None,None,ctx,None,"MODEL_VIEW",None); + nctx = ifcfile.createIfcGeometricRepresentationSubContext('Axis','Model',None,None,None,None,ctx,None,"MODEL_VIEW",None) return nctx def createAxis(ifcfile,obj,preferences): - """Creates an axis for a given wall, if applicable""" if hasattr(obj,"Base") and hasattr(obj.Base,"Shape") and obj.Base.Shape: @@ -2412,7 +2415,6 @@ def createAxis(ifcfile,obj,preferences): def writeJson(filename,ifcfile): - """writes an .ifcjson file""" import json diff --git a/src/Mod/Arch/exportIFCHelper.py b/src/Mod/Arch/exportIFCHelper.py index c9479d66b4..fabf327516 100644 --- a/src/Mod/Arch/exportIFCHelper.py +++ b/src/Mod/Arch/exportIFCHelper.py @@ -38,18 +38,17 @@ def getObjectsOfIfcType(objects, ifcType): def writeUnits(ifcfile,unit="metre"): - """adds additional units settings to the given ifc file if needed""" # so far, only metre or foot possible (which is all revit knows anyway) if unit == "foot": - d1 = ifcfile.createIfcDimensionalExponents(1,0,0,0,0,0,0); + d1 = ifcfile.createIfcDimensionalExponents(1,0,0,0,0,0,0) d2 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.3048),ifcfile[13]) d3 = ifcfile.createIfcConversionBasedUnit(d1,'LENGTHUNIT','FOOT',d2) - d4 = ifcfile.createIfcDimensionalExponents(2,0,0,0,0,0,0); + d4 = ifcfile.createIfcDimensionalExponents(2,0,0,0,0,0,0) d5 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.09290304000000001),ifcfile[14]) d6 = ifcfile.createIfcConversionBasedUnit(d4,'AREAUNIT','SQUARE FOOT',d5) - d7 = ifcfile.createIfcDimensionalExponents(3,0,0,0,0,0,0); + d7 = ifcfile.createIfcDimensionalExponents(3,0,0,0,0,0,0) d8 = ifcfile.createIfcMeasureWithUnit(ifcfile.createIfcRatioMeasure(0.028316846592),ifcfile[15]) d9 = ifcfile.createIfcConversionBasedUnit(d7,'VOLUMEUNIT','CUBIC FOOT',d8) ifcfile.createIfcUnitAssignment((d3,d6,d9,ifcfile[18])) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index d94c2d4227..99531f14e2 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -415,7 +415,6 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): else: if preferences['DEBUG']: print(" no layer found", ptype,end="") - # checking for full FreeCAD parametric definition, overriding everything else if psets and FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("IfcImportFreeCADProperties",False): if "FreeCADPropertySet" in [ifcfile[pset].Name for pset in psets.keys()]: @@ -465,7 +464,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if ptype in preferences['SKIP']: # preferences-set type skip list if preferences['DEBUG']: print(" skipped.") continue - if preferences['REPLACE_PROJECT']: # options-enabled project/site/building skip + if preferences['REPLACE_PROJECT']: # options-enabled project/site/building skip if ptype in ['IfcProject','IfcSite']: if preferences['DEBUG']: print(" skipped.") continue @@ -522,7 +521,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if shape.isNull() and (not preferences['ALLOW_INVALID']): if preferences['DEBUG']: print("null shape ",end="") - elif not shape.isValid() and (not preferences['ALLOW_INVALID']): + elif not shape.isValid() and (not preferences['ALLOW_INVALID']): if preferences['DEBUG']: print("invalid shape ",end="") else: @@ -915,13 +914,13 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if (pid in colors) and colors[pid]: colordict[obj.Name] = colors[pid] if FreeCAD.GuiUp: - # if preferences['DEBUG']: print(" setting color: ",int(colors[pid][0]*255),"/",int(colors[pid][1]*255),"/",int(colors[pid][2]*255)) + # if preferences['DEBUG']: + # print(" setting color: ",int(colors[pid][0]*255),"/",int(colors[pid][1]*255),"/",int(colors[pid][2]*255)) if hasattr(obj.ViewObject,"ShapeColor"): obj.ViewObject.ShapeColor = tuple(colors[pid][0:3]) if hasattr(obj.ViewObject,"Transparency"): obj.ViewObject.Transparency = colors[pid][3] - # if preferences['DEBUG'] is on, recompute after each shape if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute() @@ -962,7 +961,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # But that would actually be an invalid IFC file, because the magnitude # of the (twodimensional) direction vector for TrueNorth shall be greater than zero. (x, y) = modelRC.TrueNorth.DirectionRatios[:2] - obj.Declination = ((math.degrees(math.atan2(y,x))-90+180)%360)-180 + obj.Declination = ((math.degrees(math.atan2(y,x))-90+180) % 360)-180 if (FreeCAD.GuiUp): obj.ViewObject.CompassRotation.Value = obj.Declination @@ -1123,7 +1122,11 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences['DEBUG'] and first: print("") first = False - if preferences['DEBUG'] and (len(cobs) > 10) and (not(Draft.getType(objects[host]) in ["Site","Building","Floor","BuildingPart","Project"])): + if ( + preferences['DEBUG'] + and (len(cobs) > 10) + and (not(Draft.getType(objects[host]) in ["Site","Building","Floor","BuildingPart","Project"])) + ): # avoid huge fusions print("more than 10 shapes to add: skipping.") else: @@ -1334,7 +1337,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # if REPLACE_PROJECT and only one storey and one building both are omitted # the pure objects do not belong to any container, they will be added here # if after Layer they are linked by Layer and will not be added here - if preferences['REPLACE_PROJECT'] and filename: + if preferences["REPLACE_PROJECT"] and filename: rootgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Group") rootgroup.Label = os.path.basename(filename) # print(objects) @@ -1348,7 +1351,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences['DEBUG'] and layers: print("Creating layers...", end="") # print(layers) for layer_name, layer_objects in layers.items(): - if preferences['IMPORT_LAYER'] is False: + if preferences["IMPORT_LAYER"] is False: continue # the method make_layer does some nasty debug prints lay = Draft.make_layer(layer_name) diff --git a/src/Mod/Arch/importIFCHelper.py b/src/Mod/Arch/importIFCHelper.py index 22e15360c5..e71cccd60a 100644 --- a/src/Mod/Arch/importIFCHelper.py +++ b/src/Mod/Arch/importIFCHelper.py @@ -396,6 +396,7 @@ def getColorFromProduct(product): if color: return color + def getColorFromMaterial(material): if material.HasRepresentation: @@ -540,6 +541,8 @@ def predefined_to_rgb(rgb_color): # ************************************************************************************************ # property related methods + + def buildRelProperties(ifcfile): """ Builds and returns a dictionary of {object:[properties]} from an IFC file @@ -814,6 +817,7 @@ def get2DShape(representation,scaling=1000): result.append(e) elif el.is_a("IfcIndexedPolyCurve"): coords = el.Points.CoordList + def index2points(segment): pts = [] for i in segment.wrappedValue: @@ -898,7 +902,6 @@ def isRectangle(verts): def createFromProperties(propsets,ifcfile,parametrics): - """ Creates a FreeCAD parametric object from a set of properties. """ @@ -986,7 +989,6 @@ def createFromProperties(propsets,ifcfile,parametrics): def applyColorDict(doc,colordict=None): - """applies the contents of a color dict to the objects in the given doc. If no colordict is given, the doc Meta property is searched for a "colordict" entry.""" @@ -1007,7 +1009,6 @@ def applyColorDict(doc,colordict=None): def getParents(ifcobj): - """finds the parent entities of an IFC entity""" parentlist = [] From cd9055c396b4a563a660c1fefbc36912dfc7e7e9 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 28 Oct 2021 09:49:51 +0200 Subject: [PATCH 059/138] Arch: import IFC, use doc identifier instead of FreeCAD.ActiveDocument --- src/Mod/Arch/importIFC.py | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 99531f14e2..8f7c5d021f 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -613,7 +613,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): baseface = Draft.makeCircle(ex[0].Edges[0]) else: # curves or holes? We just make a Part face - baseface = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_footprint") + baseface = doc.addObject("Part::Feature",name+"_footprint") # bug/feature in ifcopenshell? Some faces of a shell may have non-null placement # workaround to remove the bad placement: exporting/reimporting as step if not ex[0].Placement.isNull(): @@ -639,7 +639,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if profileid: # store for possible shared use profiles[profileid] = baseface - baseobj = FreeCAD.ActiveDocument.addObject("Part::Extrusion",name+"_body") + baseobj = doc.addObject("Part::Extrusion",name+"_body") baseobj.Base = baseface if addplacement: # apply delta placement (stored profile) @@ -650,7 +650,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if FreeCAD.GuiUp: baseface.ViewObject.hide() if (not baseobj): - baseobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + baseobj = doc.addObject("Part::Feature",name+"_body") baseobj.Shape = shape else: # this object has no shape (storeys, etc...) @@ -702,7 +702,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): obj.Height = baseobj.Dir.Length obj.Normal = FreeCAD.Vector(baseobj.Dir).normalize() bn = baseobj.Name - FreeCAD.ActiveDocument.removeObject(bn) + doc.removeObject(bn) if (freecadtype in ["Structure","Wall"]) and not baseobj: # remove sizes to prevent auto shape creation for types that don't require a base object obj.Height = 0 @@ -823,7 +823,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if product.Elevation: obj.Placement.Base.z = product.Elevation * ifcscale elif baseobj: - obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + obj = doc.addObject("Part::Feature",name) obj.Shape = shape elif pid in additions: # no baseobj but in additions, thus we make a BuildingPart container @@ -922,7 +922,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): obj.ViewObject.Transparency = colors[pid][3] # if preferences['DEBUG'] is on, recompute after each shape - if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute() + if preferences['DEBUG']: doc.recompute() # attached 2D elements @@ -970,11 +970,11 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): except(RuntimeError): print("Aborted.") progressbar.stop() - FreeCAD.ActiveDocument.recompute() + doc.recompute() return progressbar.stop() - FreeCAD.ActiveDocument.recompute() + doc.recompute() if preferences['MERGE_MODE_STRUCT'] == 2: @@ -990,11 +990,11 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if compound: name = ifcfile[host].Name or "AnalysisModel" if preferences['PREFIX_NUMBERS']: name = "ID" + str(host) + " " + name - obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + obj = doc.addObject("Part::Feature",name) obj.Label = name obj.Shape = Part.makeCompound(compound) if structshapes: # remaining Structural shapes - obj = FreeCAD.ActiveDocument.addObject("Part::Feature","UnclaimedStruct") + obj = doc.addObject("Part::Feature","UnclaimedStruct") obj.Shape = Part.makeCompound(structshapes.values()) if preferences['DEBUG']: print("done") @@ -1008,7 +1008,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): for host,children in groups.items(): if ifcfile[host].is_a("IfcStructuralAnalysisModel"): # print(host, ' --> ', children) - obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","AnalysisModel") + obj = doc.addObject("App::DocumentObjectGroup","AnalysisModel") objects[host] = obj if host in objects.keys(): cobs = [] @@ -1022,12 +1022,12 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if cobs: if preferences['DEBUG']: print("adding ",len(cobs), " object(s) to ", objects[host].Label) Arch.addComponents(cobs,objects[host]) - if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute() + if preferences['DEBUG']: doc.recompute() if preferences['DEBUG']: print("done") if preferences['MERGE_MODE_ARCH'] > 2: # if ArchObj is compound or ArchObj not imported - FreeCAD.ActiveDocument.recompute() + doc.recompute() # cleaning bad shapes for obj in objects.values(): @@ -1048,7 +1048,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): grp_name = ifcfile[host].is_a() + "_" + str(ifcfile[host].id()) if six.PY2: grp_name = grp_name.encode("utf8") - grp = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",grp_name) + grp = doc.addObject("App::DocumentObjectGroup",grp_name) grp.Label = grp_name objects[host] = grp for child in children: @@ -1079,11 +1079,11 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if compound: name = ifcfile[host].Name or "Floor" if preferences['PREFIX_NUMBERS']: name = "ID" + str(host) + " " + name - obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + obj = doc.addObject("Part::Feature",name) obj.Label = name obj.Shape = Part.makeCompound(compound) if shapes: # remaining Arch shapes - obj = FreeCAD.ActiveDocument.addObject("Part::Feature","UnclaimedArch") + obj = doc.addObject("Part::Feature","UnclaimedArch") obj.Shape = Part.makeCompound(shapes.values()) if preferences['DEBUG']: print("done") @@ -1103,7 +1103,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): first = False if preferences['DEBUG']: print(" subtracting",objects[subtraction[0]].Label, "from", objects[subtraction[1]].Label) Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]]) - if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute() + if preferences['DEBUG']: doc.recompute() # additions @@ -1132,11 +1132,11 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): else: if preferences['DEBUG']: print(" adding",len(cobs), "object(s) to", objects[host].Label) Arch.addComponents(cobs,objects[host]) - if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute() + if preferences['DEBUG']: doc.recompute() if preferences['DEBUG'] and first: print("done.") - FreeCAD.ActiveDocument.recompute() + doc.recompute() # cleaning bad shapes @@ -1145,7 +1145,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if obj.Shape.isNull() and not(Draft.getType(obj) in ["Site","Project"]): Arch.rebuildArchShape(obj) - FreeCAD.ActiveDocument.recompute() + doc.recompute() # 2D elements @@ -1221,7 +1221,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): for rep in annotation.Representation.Representations: if rep.RepresentationIdentifier in ["Annotation","FootPrint","Axis"]: sh = importIFCHelper.get2DShape(rep,ifcscale) - if sh in FreeCAD.ActiveDocument.Objects: + if sh in doc.Objects: # dirty hack: get2DShape might return an object directly if non-shape based (texts for ex) anno = sh else: @@ -1229,7 +1229,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if shapes2d: sh = Part.makeCompound(shapes2d) if preferences['DEBUG']: print(" shape") - anno = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + anno = doc.addObject("Part::Feature",name) anno.Shape = sh p = importIFCHelper.getPlacement(annotation.ObjectPlacement,ifcscale) if p: # and annotation.is_a("IfcAnnotation"): @@ -1247,7 +1247,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if (aid in children) and (host in objects.keys()): Arch.addComponents(anno,objects[host]) - FreeCAD.ActiveDocument.recompute() + doc.recompute() # Materials @@ -1338,7 +1338,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # the pure objects do not belong to any container, they will be added here # if after Layer they are linked by Layer and will not be added here if preferences["REPLACE_PROJECT"] and filename: - rootgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Group") + rootgroup = doc.addObject("App::DocumentObjectGroup","Group") rootgroup.Label = os.path.basename(filename) # print(objects) for key,obj in objects.items(): @@ -1365,13 +1365,13 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if lobj_id in objects: lay_grp.append(objects[lobj_id]) lay.Group = lay_grp - FreeCAD.ActiveDocument.recompute() + doc.recompute() if preferences['DEBUG'] and layers: print("done") # restore links from full parametric definitions for p in parametrics: - l = FreeCAD.ActiveDocument.getObject(p[2]) + l = doc.getObject(p[2]) if l: setattr(p[0],p[1],l) @@ -1382,7 +1382,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): d["colordict"] = json.dumps(colordict) doc.Meta = d - FreeCAD.ActiveDocument.recompute() + doc.recompute() if FreeCAD.GuiUp and ZOOMOUT: Gui.SendMsgToActiveView("ViewFit") From 0a820e92fded3fb56d0c18e72cb0ff32887c9130 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 28 Oct 2021 15:43:35 +0200 Subject: [PATCH 060/138] Arch: import IFC, Materials, small code improvements, no changes --- src/Mod/Arch/importIFC.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 8f7c5d021f..66a50ec110 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1259,34 +1259,45 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): added_mats = [] for material in materials: # print(material.id()) - # get and set material name + + mdict = {} + # on ifc import only the "DiffuseColor" of the material dictionary is initialized + # on editing material in Arch Gui a lot more keys of the material dictionary are initialized even with empty values + # TODO: there should be a generic material obj init method which will be used by Arch Gui, import IFC, FEM, etc + + # get the material name name = "Material" if material.Name: name = material.Name if six.PY2: name = name.encode("utf8") + mdict["Name"] = name + # get material color - # the "DiffuseColor" of a material should never be 'None' + # the "DiffuseColor" of a material should never be "None" # values in colors are None if something went wrong # thus the "DiffuseColor" will only be set if the color is not None - mdict = {} + mat_color = None if material.id() in colors and colors[material.id()] is not None: - mdict["DiffuseColor"] = str(colors[material.id()]) + mat_color = str(colors[material.id()]) else: for o,m in mattable.items(): if m == material.id(): if o in colors and colors[o] is not None: - mdict["DiffuseColor"] = str(colors[o]) - # on ifc import only the "DiffuseColor" of the material dictionary is initialized - # on editing material in Gui a lot more keys of the material dictionary are initialized even with empty values - # TODO: there should be a generic material obj init method which will be used by Arch Gui, import IFC, FEM, etc + mat_color = str(colors[o]) + if mat_color is not None: + mdict["DiffuseColor"] = mat_color + else: + if preferences['DEBUG']: print("/n no color for material: {}, ".format(str(material.id)),end="") + # merge materials with same name and color if setting in prefs is True add_material = True - if preferences['MERGE_MATERIALS']: + if preferences["MERGE_MATERIALS"]: for added_mat in added_mats: if ( # added_mat.Material["Name"] == name - # above does not work because Name is not initialized in material dict + # above does not work because FreeCAD would crash in some circumstances + # https://forum.freecadweb.org/viewtopic.php?f=23&t=63260 added_mat.Label == name and ("DiffuseColor" in mdict and "DiffuseColor" in added_mat.Material) and mdict["DiffuseColor"] == added_mat.Material["DiffuseColor"] @@ -1296,8 +1307,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # add a new material object if add_material is True: matobj = Arch.makeMaterial(name=name) - if mdict: - matobj.Material = mdict + matobj.Material = mdict added_mats.append(matobj) # fill material attribute of the objects for o,m in mattable.items(): From 226e0adab99ade53532d4c99e19709f04a243c53 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 28 Oct 2021 15:45:15 +0200 Subject: [PATCH 061/138] Arch: import IFC, Material, small improvement, no changes --- src/Mod/Arch/importIFC.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 66a50ec110..6ddc2a14e0 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1304,6 +1304,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): ): matobj = added_mat add_material = False + break # add a new material object if add_material is True: matobj = Arch.makeMaterial(name=name) From 5e2bb15999895ec1b04954fa3dfaecedcbf301b8 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Thu, 28 Oct 2021 16:02:06 +0200 Subject: [PATCH 062/138] Arch: import IFC, Materials, make merge materials working on duplicate label setting --- src/Mod/Arch/importIFC.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 6ddc2a14e0..718a43e5bf 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1261,7 +1261,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): # print(material.id()) mdict = {} - # on ifc import only the "DiffuseColor" of the material dictionary is initialized + # on ifc import only the "Description" and "DiffuseColor" (if it was read) of the material dictionary will be initialized # on editing material in Arch Gui a lot more keys of the material dictionary are initialized even with empty values # TODO: there should be a generic material obj init method which will be used by Arch Gui, import IFC, FEM, etc @@ -1271,7 +1271,10 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): name = material.Name if six.PY2: name = name.encode("utf8") - mdict["Name"] = name + # mdict["Name"] = name on duplicate material names in IFC this could result in crash + # https://forum.freecadweb.org/viewtopic.php?f=23&t=63260 + # thus use "Description" + mdict["Description"] = name # get material color # the "DiffuseColor" of a material should never be "None" @@ -1295,16 +1298,16 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences["MERGE_MATERIALS"]: for added_mat in added_mats: if ( - # added_mat.Material["Name"] == name - # above does not work because FreeCAD would crash in some circumstances - # https://forum.freecadweb.org/viewtopic.php?f=23&t=63260 - added_mat.Label == name - and ("DiffuseColor" in mdict and "DiffuseColor" in added_mat.Material) - and mdict["DiffuseColor"] == added_mat.Material["DiffuseColor"] + "Description" in added_mat.Material + and "DiffuseColor" in added_mat.Material + and "DiffuseColor" in mdict # Description has been set thus it is in mdict + and added_mat.Material["Description"] == mdict["Description"] + and added_mat.Material["DiffuseColor"] == mdict["DiffuseColor"] ): matobj = added_mat add_material = False break + # add a new material object if add_material is True: matobj = Arch.makeMaterial(name=name) From 09e2e7acfa291ae262f59bc15f79a452dba21851 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 28 Oct 2021 17:03:11 +0200 Subject: [PATCH 063/138] Sketcher: [skip ci] handle coincident points when trying to create arc/circle --- src/Mod/Sketcher/Gui/CommandCreateGeo.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 8e43658060..f7d00610ee 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -107,6 +107,9 @@ Base::Vector2d GetCircleCenter (const Base::Vector2d &p1, const Base::Vector2d & double vv = v*v; double ww = w*w; + if (uu * vv * ww == 0) + THROWM(Base::ValueError,"Two points are coincident"); + double uv = -(u*v); double vw = -(v*w); double uw = -(u*w); From a624f6a50c873648ff0d653a21ba67417fd77253 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Thu, 28 Oct 2021 19:29:59 +0200 Subject: [PATCH 064/138] Draft: Fix angle dimension issue If the end points of the selected edges for an angular dimension were (almost) coincident with their intersection, random angles could be calculated. In essence a floating point inaccuracy issue. --- src/Mod/Draft/draftguitools/gui_dimensions.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_dimensions.py b/src/Mod/Draft/draftguitools/gui_dimensions.py index 45f12e8991..d091568ea8 100644 --- a/src/Mod/Draft/draftguitools/gui_dimensions.py +++ b/src/Mod/Draft/draftguitools/gui_dimensions.py @@ -106,7 +106,7 @@ class Dimension(gui_base_original.Creator): self.arctrack = trackers.arcTracker() self.link = None self.edges = [] - self.pts = [] + self.angles = [] self.angledata = None self.indices = [] self.center = None @@ -397,7 +397,7 @@ class Dimension(gui_base_original.Creator): r = self.point.sub(self.center) self.arctrack.setRadius(r.Length) a = self.arctrack.getAngle(self.point) - pair = DraftGeomUtils.getBoundaryAngles(a, self.pts) + pair = DraftGeomUtils.getBoundaryAngles(a, self.angles) if not (pair[0] < a < pair[1]): self.angledata = [4 * math.pi - pair[0], 2 * math.pi - pair[1]] @@ -504,8 +504,15 @@ class Dimension(gui_base_original.Creator): self.arctrack.setCenter(self.center) self.arctrack.on() for e in self.edges: - for v in e.Vertexes: - self.pts.append(self.arctrack.getAngle(v.Point)) + if e.Length < 0.00003: # Edge must be long enough for the tolerance of 0.00001mm to make sense. + _msg(translate("draft", "Edge too short!")) + self.finish() + return + for i in [0, 1]: + pt = e.Vertexes[i].Point + if pt.isEqual(self.center, 0.00001): # A relatively high tolerance is required. + pt = e.Vertexes[i - 1].Point # Use the other point instead. + self.angles.append(self.arctrack.getAngle(pt)) self.link = [self.link[0], ob] else: _msg(translate("draft", "Edges don't intersect!")) From 16ae568549d7c06215fe0e9dba2ddeea960cee11 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 29 Oct 2021 12:40:32 +0200 Subject: [PATCH 065/138] App: add class PropertyRotation --- src/App/Application.cpp | 1 + src/App/PropertyGeo.cpp | 178 ++++++++++++++++++++++++++++++++++++++++ src/App/PropertyGeo.h | 73 +++++++++++++++- 3 files changed, 250 insertions(+), 2 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 2cdda7d8f5..fb2755571f 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1810,6 +1810,7 @@ void Application::initTypes(void) App ::PropertyPlacement ::init(); App ::PropertyPlacementList ::init(); App ::PropertyPlacementLink ::init(); + App ::PropertyRotation ::init(); App ::PropertyGeometry ::init(); App ::PropertyComplexGeoData ::init(); App ::PropertyColor ::init(); diff --git a/src/App/PropertyGeo.cpp b/src/App/PropertyGeo.cpp index 02909b32d5..062e78a7b9 100644 --- a/src/App/PropertyGeo.cpp +++ b/src/App/PropertyGeo.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include "Document.h" #include "DocumentObject.h" @@ -938,6 +939,183 @@ void PropertyPlacementLink::Paste(const Property &from) hasSetValue(); } +//************************************************************************** +//************************************************************************** +// PropertyRotation +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TYPESYSTEM_SOURCE(App::PropertyRotation , App::Property) + +PropertyRotation::PropertyRotation() +{ + +} + + +PropertyRotation::~PropertyRotation() +{ + +} + +void PropertyRotation::setValue(const Base::Rotation &rot) +{ + aboutToSetValue(); + _rot = rot; + hasSetValue(); +} + +bool PropertyRotation::setValueIfChanged(const Base::Rotation &rot, double atol) +{ + if (_rot.isSame(rot, atol)) { + return false; + } + + setValue(rot); + return true; +} + + +const Base::Rotation & PropertyRotation::getValue() const +{ + return _rot; +} + +void PropertyRotation::getPaths(std::vector &paths) const +{ + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Angle"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z"))); +} + +void PropertyRotation::setPathValue(const ObjectIdentifier &path, const boost::any &value) +{ + if (path.getSubPathStr() == ".Angle") { + double avalue; + + if (value.type() == typeid(Base::Quantity)) + avalue = boost::any_cast(value).getValue(); + else if (value.type() == typeid(double)) + avalue = boost::any_cast(value); + else if (value.type() == typeid(int)) + avalue = boost::any_cast(value); + else if (value.type() == typeid(unsigned int)) + avalue = boost::any_cast(value); + else if (value.type() == typeid(short)) + avalue = boost::any_cast(value); + else if (value.type() == typeid(unsigned short)) + avalue = boost::any_cast(value); + else if (value.type() == typeid(long)) + avalue = boost::any_cast(value); + else if (value.type() == typeid(unsigned long)) + avalue = boost::any_cast(value); + else + throw std::bad_cast(); + + Property::setPathValue(path, Base::toRadians(avalue)); + } + else { + Property::setPathValue(path, value); + } +} + +const boost::any PropertyRotation::getPathValue(const ObjectIdentifier &path) const +{ + std::string p = path.getSubPathStr(); + + if (p == ".Angle") { + // Convert angle to degrees + return Base::Quantity(Base::toDegrees(boost::any_cast(Property::getPathValue(path))), Unit::Angle); + } + else { + return Property::getPathValue(path); + } +} + +bool PropertyRotation::getPyPathValue(const ObjectIdentifier &path, Py::Object &res) const +{ + std::string p = path.getSubPathStr(); + if (p == ".Angle") { + Base::Vector3d axis; double angle; + _rot.getValue(axis,angle); + res = Py::asObject(new QuantityPy(new Quantity(Base::toDegrees(angle),Unit::Angle))); + return true; + } + + return false; +} + +PyObject *PropertyRotation::getPyObject() +{ + return new Base::RotationPy(new Base::Rotation(_rot)); +} + +void PropertyRotation::setPyObject(PyObject *value) +{ + if (PyObject_TypeCheck(value, &(Base::MatrixPy::Type))) { + Base::MatrixPy *object = static_cast(value); + Base::Matrix4D mat = object->value(); + Base::Rotation p; + p.setValue(mat); + setValue(p); + } + else if (PyObject_TypeCheck(value, &(Base::RotationPy::Type))) { + setValue(*static_cast(value)->getRotationPtr()); + } + else { + std::string error = std::string("type must be 'Matrix' or 'Rotation', not "); + error += value->ob_type->tp_name; + throw Base::TypeError(error); + } +} + +void PropertyRotation::Save (Base::Writer &writer) const +{ + Vector3d axis; + double rfAngle; + _rot.getValue(axis, rfAngle); + + writer.Stream() << writer.ind() << "\n"; +} + +void PropertyRotation::Restore(Base::XMLReader &reader) +{ + reader.readElement("PropertyRotation"); + aboutToSetValue(); + + _rot = Rotation(Vector3d(reader.getAttributeAsFloat("Ox"), + reader.getAttributeAsFloat("Oy"), + reader.getAttributeAsFloat("Oz")), + reader.getAttributeAsFloat("A")); + hasSetValue(); +} + +Property *PropertyRotation::Copy() const +{ + PropertyRotation *p = new PropertyRotation(); + p->_rot = _rot; + return p; +} + +void PropertyRotation::Paste(const Property &from) +{ + aboutToSetValue(); + _rot = dynamic_cast(from)._rot; + hasSetValue(); +} + // ------------------------------------------------------------ TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyGeometry , App::Property) diff --git a/src/App/PropertyGeo.h b/src/App/PropertyGeo.h index 99103b7b29..f22be71507 100644 --- a/src/App/PropertyGeo.h +++ b/src/App/PropertyGeo.h @@ -35,6 +35,7 @@ #include "Property.h" #include "PropertyLinks.h" #include "ComplexGeoData.h" +#include namespace Base { class Writer; @@ -281,8 +282,9 @@ private: Base::Matrix4D _cMat; }; -/** Vector properties - * This is the father of all properties handling Integers. +/// Property representing a placement +/*! + * Encapsulates a Base::Placement in a Property */ class AppExport PropertyPlacement: public Property { @@ -408,6 +410,73 @@ protected: }; +/// Property representing a rotation +/*! + * Encapsulates a Base::Rotation in a Property + */ +class AppExport PropertyRotation : public Property +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + /** + * A constructor. + * A more elaborate description of the constructor. + */ + PropertyRotation(); + + /** + * A destructor. + * A more elaborate description of the destructor. + */ + virtual ~PropertyRotation(); + + /** Sets the property + */ + void setValue(const Base::Rotation &rot); + + /** Sets property only if changed + * @param pos: input placement + * @param tol: position tolerance + * @param atol: angular tolerance + */ + bool setValueIfChanged(const Base::Rotation &rot, double atol=1e-12); + + /** This method returns a string representation of the property + */ + const Base::Rotation &getValue() const; + + /// Get valid paths for this property; used by auto completer + void getPaths(std::vector &paths) const override; + + void setPathValue(const ObjectIdentifier &path, const boost::any &value) override; + + virtual const boost::any getPathValue(const ObjectIdentifier &path) const override; + + virtual bool getPyPathValue(const ObjectIdentifier &path, Py::Object &res) const override; + + const char* getEditorName() const override { + return "Gui::PropertyEditor::PropertyRotationItem"; + } + + virtual PyObject *getPyObject() override; + virtual void setPyObject(PyObject *) override; + + virtual void Save (Base::Writer &writer) const override; + virtual void Restore(Base::XMLReader &reader) override; + + virtual Property *Copy() const override; + virtual void Paste(const Property &from) override; + + virtual unsigned int getMemSize () const override { + return sizeof(Base::Placement); + } + +private: + Base::Rotation _rot; +}; + + /** The base class for all basic geometry properties. * @author Werner Mayer */ From 61de191771ede8199b3c52486201938d4b24b027 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 29 Oct 2021 12:41:03 +0200 Subject: [PATCH 066/138] Gui: implement editor for PropertyRotation --- src/Gui/MetaTypes.h | 1 + src/Gui/SoFCDB.cpp | 1 + src/Gui/propertyeditor/PropertyItem.cpp | 226 ++++++++++++++++++++++++ src/Gui/propertyeditor/PropertyItem.h | 41 +++++ 4 files changed, 269 insertions(+) diff --git a/src/Gui/MetaTypes.h b/src/Gui/MetaTypes.h index 75047bca9c..99b69ed431 100644 --- a/src/Gui/MetaTypes.h +++ b/src/Gui/MetaTypes.h @@ -34,6 +34,7 @@ Q_DECLARE_METATYPE(Base::Vector3d) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Base::Matrix4D) Q_DECLARE_METATYPE(Base::Placement) +Q_DECLARE_METATYPE(Base::Rotation) Q_DECLARE_METATYPE(Base::Quantity) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(App::SubObjectT) diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 066cb3e6d8..0dc42462fe 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -161,6 +161,7 @@ void Gui::SoFCDB::init() PropertyDirectionItem ::init(); PropertyMatrixItem ::init(); PropertyPlacementItem ::init(); + PropertyRotationItem ::init(); PropertyEnumItem ::init(); PropertyStringListItem ::init(); PropertyFloatListItem ::init(); diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 8211cca9ad..519816fcab 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -2092,6 +2092,232 @@ void PropertyMatrixItem::setA44(double A44) setData(QVariant::fromValue(Base::Matrix4D(getA11(),getA12(),getA13(),getA14(),getA21(),getA22(),getA23(),getA24(),getA31(),getA32(),getA33(),getA34(),getA41(),getA42(),getA43(),A44 ))); } +// --------------------------------------------------------------- + +PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyRotationItem) + +PropertyRotationItem::PropertyRotationItem() + : init_axis(false) + , changed_value(false) + , rot_angle(0) + , rot_axis(0,0,1) +{ + m_a = static_cast(PropertyUnitItem::create()); + m_a->setParent(this); + m_a->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Angle"))); + this->appendChild(m_a); + m_d = static_cast(PropertyVectorItem::create()); + m_d->setParent(this); + m_d->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Axis"))); + m_d->setReadOnly(true); + this->appendChild(m_d); +} + +PropertyRotationItem::~PropertyRotationItem() +{ +} + +Base::Quantity PropertyRotationItem::getAngle() const +{ + QVariant value = data(1, Qt::EditRole); + if (!value.canConvert()) + return Base::Quantity(0.0); + const Base::Rotation& val = value.value(); + double angle; + Base::Vector3d dir; + val.getRawValue(dir, angle); + if (dir * this->rot_axis < 0.0) + angle = -angle; + return Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle); +} + +void PropertyRotationItem::setAngle(Base::Quantity angle) +{ + QVariant value = data(1, Qt::EditRole); + if (!value.canConvert()) + return; + + Base::Rotation rot; + rot.setValue(this->rot_axis, Base::toRadians(angle.getValue())); + changed_value = true; + rot_angle = angle.getValue(); + setValue(QVariant::fromValue(rot)); +} + +Base::Vector3d PropertyRotationItem::getAxis() const +{ + // We must store the rotation axis in a member because + // if we read the value from the property we would always + // get a normalized vector which makes it quite unhandy + // to work with + return this->rot_axis; +} + +void PropertyRotationItem::setAxis(const Base::Vector3d& axis) +{ + QVariant value = data(1, Qt::EditRole); + if (!value.canConvert()) + return; + this->rot_axis = axis; + Base::Rotation rot = value.value(); + Base::Vector3d dummy; double angle; + rot.getValue(dummy, angle); + if (dummy * axis < 0.0) + angle = -angle; + rot.setValue(axis, angle); + changed_value = true; + setValue(QVariant::fromValue(rot)); +} + +void PropertyRotationItem::assignProperty(const App::Property* prop) +{ + // Choose an adaptive epsilon to avoid changing the axis when they are considered to + // be equal. See https://forum.freecadweb.org/viewtopic.php?f=10&t=24662&start=10 + double eps = std::pow(10.0, -2*(decimals()+1)); + if (prop->getTypeId().isDerivedFrom(App::PropertyRotation::getClassTypeId())) { + const Base::Rotation& value = static_cast(prop)->getValue(); + double angle; + Base::Vector3d dir; + value.getRawValue(dir, angle); + Base::Vector3d cross = this->rot_axis.Cross(dir); + double len2 = cross.Sqr(); + if (angle != 0) { + // vectors are not parallel + if (len2 > eps) + this->rot_axis = dir; + // vectors point into opposite directions + else if (this->rot_axis.Dot(dir) < 0) + this->rot_axis = -this->rot_axis; + } + this->rot_angle = Base::toDegrees(angle); + } +} + +QVariant PropertyRotationItem::value(const App::Property* prop) const +{ + assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyRotation::getClassTypeId())); + + const Base::Rotation& value = static_cast(prop)->getValue(); + double angle; + Base::Vector3d dir; + value.getRawValue(dir, angle); + if (!init_axis) { + if (m_a->hasExpression()) { + QString str = m_a->expressionAsString(); + const_cast(this)->rot_angle = str.toDouble(); + } + else { + const_cast(this)->rot_angle = Base::toDegrees(angle); + } + + PropertyItem* x = m_d->child(0); + PropertyItem* y = m_d->child(1); + PropertyItem* z = m_d->child(2); + if (x->hasExpression()) { + QString str = x->expressionAsString(); + dir.x = str.toDouble(); + } + if (y->hasExpression()) { + QString str = y->expressionAsString(); + dir.y = str.toDouble(); + } + if (z->hasExpression()) { + QString str = z->expressionAsString(); + dir.z = str.toDouble(); + } + const_cast(this)->rot_axis = dir; + const_cast(this)->init_axis = true; + } + return QVariant::fromValue(value); +} + +QVariant PropertyRotationItem::toolTip(const App::Property* prop) const +{ + assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyRotation::getClassTypeId())); + + const Base::Rotation& p = static_cast(prop)->getValue(); + double angle; + Base::Vector3d dir; + p.getRawValue(dir, angle); + angle = Base::toDegrees(angle); + + QLocale loc; + QString data = QString::fromUtf8("Axis: (%1 %2 %3)\n" + "Angle: %4") + .arg(loc.toString(dir.x,'f',decimals()), + loc.toString(dir.y,'f',decimals()), + loc.toString(dir.z,'f',decimals()), + Base::Quantity(angle, Base::Unit::Angle).getUserString()); + return QVariant(data); +} + +QVariant PropertyRotationItem::toString(const QVariant& prop) const +{ + const Base::Rotation& p = prop.value(); + double angle; + Base::Vector3d dir; + p.getRawValue(dir, angle); + angle = Base::toDegrees(angle); + + QLocale loc; + QString data = QString::fromUtf8("[(%1 %2 %3); %4]") + .arg(loc.toString(dir.x,'f',2), + loc.toString(dir.y,'f',2), + loc.toString(dir.z,'f',2), + Base::Quantity(angle, Base::Unit::Angle).getUserString()); + return QVariant(data); +} + +void PropertyRotationItem::setValue(const QVariant& value) +{ + if (!value.canConvert()) + return; + // Accept this only if the user changed the axis, angle or position but + // not if >this< item loses focus + if (!changed_value) + return; + changed_value = false; + + Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); + QString data = QString::fromLatin1("App.Rotation(App.Vector(%1,%2,%3),%4)") + .arg(Base::UnitsApi::toNumber(rot_axis.x, format)) + .arg(Base::UnitsApi::toNumber(rot_axis.y, format)) + .arg(Base::UnitsApi::toNumber(rot_axis.z, format)) + .arg(Base::UnitsApi::toNumber(rot_angle, format)); + setPropertyValue(data); +} + +QWidget* PropertyRotationItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const +{ + Q_UNUSED(parent) + Q_UNUSED(receiver) + Q_UNUSED(method) + return nullptr; +} + +void PropertyRotationItem::setEditorData(QWidget *editor, const QVariant& data) const +{ + Q_UNUSED(editor) + Q_UNUSED(data) +} + +QVariant PropertyRotationItem::editorData(QWidget *editor) const +{ + Q_UNUSED(editor) + return QVariant(); +} + +void PropertyRotationItem::propertyBound() +{ + if (isBound()) { + m_a->bind(App::ObjectIdentifier(getPath())<bind(App::ObjectIdentifier(getPath())<) Q_DECLARE_METATYPE(Base::Matrix4D) Q_DECLARE_METATYPE(Base::Placement) +Q_DECLARE_METATYPE(Base::Rotation) Q_DECLARE_METATYPE(Base::Quantity) Q_DECLARE_METATYPE(QList) #endif @@ -656,6 +657,46 @@ private: PropertyFloatItem* m_a44; }; +/** + * Edit properties of rotation type. + * \author Werner Mayer + */ +class GuiExport PropertyRotationItem: public PropertyItem +{ + Q_OBJECT + Q_PROPERTY(Base::Quantity Angle READ getAngle WRITE setAngle DESIGNABLE true USER true) + Q_PROPERTY(Base::Vector3d Axis READ getAxis WRITE setAxis DESIGNABLE true USER true) + PROPERTYITEM_HEADER + + virtual QWidget* createEditor(QWidget* parent, const QObject* receiver, const char* method) const; + virtual void setEditorData(QWidget *editor, const QVariant& data) const; + virtual QVariant editorData(QWidget *editor) const; + + virtual void propertyBound(); + virtual void assignProperty(const App::Property*); + + Base::Quantity getAngle() const; + void setAngle(Base::Quantity); + Base::Vector3d getAxis() const; + void setAxis(const Base::Vector3d&); + +protected: + PropertyRotationItem(); + ~PropertyRotationItem(); + virtual QVariant toolTip(const App::Property*) const; + virtual QVariant toString(const QVariant&) const; + virtual QVariant value(const App::Property*) const; + virtual void setValue(const QVariant&); + +private: + bool init_axis; + bool changed_value; + double rot_angle; + Base::Vector3d rot_axis; + PropertyUnitItem * m_a; + PropertyVectorItem* m_d; +}; + class PlacementEditor : public Gui::LabelButton { Q_OBJECT From 7a35afdd8ec2d8d12f2eee3d2ac1e6aad03e6cfa Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 29 Oct 2021 15:59:55 +0200 Subject: [PATCH 067/138] Gui: add class RotationHelper to reduce code duplication of PropertyRotationItem and PropertyPlacementItem --- src/Gui/propertyeditor/PropertyItem.cpp | 240 ++++++++++++++---------- src/Gui/propertyeditor/PropertyItem.h | 33 +++- 2 files changed, 168 insertions(+), 105 deletions(-) diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 519816fcab..d3e9cd8076 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -2094,13 +2094,110 @@ void PropertyMatrixItem::setA44(double A44) // --------------------------------------------------------------- -PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyRotationItem) - -PropertyRotationItem::PropertyRotationItem() +RotationHelper::RotationHelper() : init_axis(false) , changed_value(false) , rot_angle(0) , rot_axis(0,0,1) +{ +} + +void RotationHelper::setChanged(bool value) +{ + changed_value = value; +} + +bool RotationHelper::hasChangedAndReset() +{ + if (!changed_value) + return false; + + changed_value = false; + return true; +} + +bool RotationHelper::isAxisInitialized() const +{ + return init_axis; +} + +void RotationHelper::setValue(const Base::Vector3d& axis, double angle) +{ + rot_axis = axis; + rot_angle = angle; + init_axis = true; +} + +void RotationHelper::getValue(Base::Vector3d& axis, double& angle) const +{ + axis = rot_axis; + angle = rot_angle; +} + +double RotationHelper::getAngle(const Base::Rotation& val) const +{ + double angle; + Base::Vector3d dir; + val.getRawValue(dir, angle); + if (dir * this->rot_axis < 0.0) + angle = -angle; + return angle; +} + +Base::Rotation RotationHelper::setAngle(double angle) +{ + Base::Rotation rot; + rot.setValue(this->rot_axis, Base::toRadians(angle)); + changed_value = true; + rot_angle = angle; + return rot; +} + +Base::Vector3d RotationHelper::getAxis() const +{ + // We must store the rotation axis in a member because + // if we read the value from the property we would always + // get a normalized vector which makes it quite unhandy + // to work with + return this->rot_axis; +} + +Base::Rotation RotationHelper::setAxis(const Base::Rotation& value, const Base::Vector3d& axis) +{ + this->rot_axis = axis; + Base::Rotation rot = value; + Base::Vector3d dummy; double angle; + rot.getValue(dummy, angle); + if (dummy * axis < 0.0) + angle = -angle; + rot.setValue(axis, angle); + changed_value = true; + return rot; +} + +void RotationHelper::assignProperty(const Base::Rotation& value, double eps) +{ + double angle; + Base::Vector3d dir; + value.getRawValue(dir, angle); + Base::Vector3d cross = this->rot_axis.Cross(dir); + double len2 = cross.Sqr(); + if (angle != 0) { + // vectors are not parallel + if (len2 > eps) + this->rot_axis = dir; + // vectors point into opposite directions + else if (this->rot_axis.Dot(dir) < 0) + this->rot_axis = -this->rot_axis; + } + this->rot_angle = Base::toDegrees(angle); +} + +// --------------------------------------------------------------- + +PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyRotationItem) + +PropertyRotationItem::PropertyRotationItem() { m_a = static_cast(PropertyUnitItem::create()); m_a->setParent(this); @@ -2122,12 +2219,9 @@ Base::Quantity PropertyRotationItem::getAngle() const QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return Base::Quantity(0.0); + const Base::Rotation& val = value.value(); - double angle; - Base::Vector3d dir; - val.getRawValue(dir, angle); - if (dir * this->rot_axis < 0.0) - angle = -angle; + double angle = h.getAngle(val); return Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle); } @@ -2137,20 +2231,13 @@ void PropertyRotationItem::setAngle(Base::Quantity angle) if (!value.canConvert()) return; - Base::Rotation rot; - rot.setValue(this->rot_axis, Base::toRadians(angle.getValue())); - changed_value = true; - rot_angle = angle.getValue(); + Base::Rotation rot = h.setAngle(angle.getValue()); setValue(QVariant::fromValue(rot)); } Base::Vector3d PropertyRotationItem::getAxis() const { - // We must store the rotation axis in a member because - // if we read the value from the property we would always - // get a normalized vector which makes it quite unhandy - // to work with - return this->rot_axis; + return h.getAxis(); } void PropertyRotationItem::setAxis(const Base::Vector3d& axis) @@ -2158,14 +2245,9 @@ void PropertyRotationItem::setAxis(const Base::Vector3d& axis) QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; - this->rot_axis = axis; + Base::Rotation rot = value.value(); - Base::Vector3d dummy; double angle; - rot.getValue(dummy, angle); - if (dummy * axis < 0.0) - angle = -angle; - rot.setValue(axis, angle); - changed_value = true; + rot = h.setAxis(rot, axis); setValue(QVariant::fromValue(rot)); } @@ -2176,20 +2258,7 @@ void PropertyRotationItem::assignProperty(const App::Property* prop) double eps = std::pow(10.0, -2*(decimals()+1)); if (prop->getTypeId().isDerivedFrom(App::PropertyRotation::getClassTypeId())) { const Base::Rotation& value = static_cast(prop)->getValue(); - double angle; - Base::Vector3d dir; - value.getRawValue(dir, angle); - Base::Vector3d cross = this->rot_axis.Cross(dir); - double len2 = cross.Sqr(); - if (angle != 0) { - // vectors are not parallel - if (len2 > eps) - this->rot_axis = dir; - // vectors point into opposite directions - else if (this->rot_axis.Dot(dir) < 0) - this->rot_axis = -this->rot_axis; - } - this->rot_angle = Base::toDegrees(angle); + h.assignProperty(value, eps); } } @@ -2201,13 +2270,13 @@ QVariant PropertyRotationItem::value(const App::Property* prop) const double angle; Base::Vector3d dir; value.getRawValue(dir, angle); - if (!init_axis) { + if (!h.isAxisInitialized()) { if (m_a->hasExpression()) { QString str = m_a->expressionAsString(); - const_cast(this)->rot_angle = str.toDouble(); + angle = str.toDouble(); } else { - const_cast(this)->rot_angle = Base::toDegrees(angle); + angle = Base::toDegrees(angle); } PropertyItem* x = m_d->child(0); @@ -2225,8 +2294,7 @@ QVariant PropertyRotationItem::value(const App::Property* prop) const QString str = z->expressionAsString(); dir.z = str.toDouble(); } - const_cast(this)->rot_axis = dir; - const_cast(this)->init_axis = true; + h.setValue(dir, angle); } return QVariant::fromValue(value); } @@ -2274,16 +2342,18 @@ void PropertyRotationItem::setValue(const QVariant& value) return; // Accept this only if the user changed the axis, angle or position but // not if >this< item loses focus - if (!changed_value) + if (!h.hasChangedAndReset()) return; - changed_value = false; + Base::Vector3d axis; + double angle; + h.getValue(axis, angle); Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString data = QString::fromLatin1("App.Rotation(App.Vector(%1,%2,%3),%4)") - .arg(Base::UnitsApi::toNumber(rot_axis.x, format)) - .arg(Base::UnitsApi::toNumber(rot_axis.y, format)) - .arg(Base::UnitsApi::toNumber(rot_axis.z, format)) - .arg(Base::UnitsApi::toNumber(rot_angle, format)); + .arg(Base::UnitsApi::toNumber(axis.x, format)) + .arg(Base::UnitsApi::toNumber(axis.y, format)) + .arg(Base::UnitsApi::toNumber(axis.z, format)) + .arg(Base::UnitsApi::toNumber(angle, format)); setPropertyValue(data); } @@ -2394,7 +2464,7 @@ void PlacementEditor::updateValue(const QVariant& v, bool incr, bool data) PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyPlacementItem) -PropertyPlacementItem::PropertyPlacementItem() : init_axis(false), changed_value(false), rot_angle(0), rot_axis(0,0,1) +PropertyPlacementItem::PropertyPlacementItem() { m_a = static_cast(PropertyUnitItem::create()); m_a->setParent(this); @@ -2421,12 +2491,9 @@ Base::Quantity PropertyPlacementItem::getAngle() const QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return Base::Quantity(0.0); + const Base::Placement& val = value.value(); - double angle; - Base::Vector3d dir; - val.getRotation().getRawValue(dir, angle); - if (dir * this->rot_axis < 0.0) - angle = -angle; + double angle = h.getAngle(val.getRotation()); return Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle); } @@ -2437,21 +2504,14 @@ void PropertyPlacementItem::setAngle(Base::Quantity angle) return; Base::Placement val = value.value(); - Base::Rotation rot; - rot.setValue(this->rot_axis, Base::toRadians(angle.getValue())); + Base::Rotation rot = h.setAngle(angle.getValue()); val.setRotation(rot); - changed_value = true; - rot_angle = angle.getValue(); setValue(QVariant::fromValue(val)); } Base::Vector3d PropertyPlacementItem::getAxis() const { - // We must store the rotation axis in a member because - // if we read the value from the property we would always - // get a normalized vector which makes it quite unhandy - // to work with - return this->rot_axis; + return h.getAxis(); } void PropertyPlacementItem::setAxis(const Base::Vector3d& axis) @@ -2459,16 +2519,11 @@ void PropertyPlacementItem::setAxis(const Base::Vector3d& axis) QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; - this->rot_axis = axis; + Base::Placement val = value.value(); Base::Rotation rot = val.getRotation(); - Base::Vector3d dummy; double angle; - rot.getValue(dummy, angle); - if (dummy * axis < 0.0) - angle = -angle; - rot.setValue(axis, angle); + rot = h.setAxis(rot, axis); val.setRotation(rot); - changed_value = true; setValue(QVariant::fromValue(val)); } @@ -2486,9 +2541,10 @@ void PropertyPlacementItem::setPosition(const Base::Vector3d& pos) QVariant value = data(1, Qt::EditRole); if (!value.canConvert()) return; + Base::Placement val = value.value(); val.setPosition(pos); - changed_value = true; + h.setChanged(true); setValue(QVariant::fromValue(val)); } @@ -2499,20 +2555,7 @@ void PropertyPlacementItem::assignProperty(const App::Property* prop) double eps = std::pow(10.0, -2*(decimals()+1)); if (prop->getTypeId().isDerivedFrom(App::PropertyPlacement::getClassTypeId())) { const Base::Placement& value = static_cast(prop)->getValue(); - double angle; - Base::Vector3d dir; - value.getRotation().getRawValue(dir, angle); - Base::Vector3d cross = this->rot_axis.Cross(dir); - double len2 = cross.Sqr(); - if (angle != 0) { - // vectors are not parallel - if (len2 > eps) - this->rot_axis = dir; - // vectors point into opposite directions - else if (this->rot_axis.Dot(dir) < 0) - this->rot_axis = -this->rot_axis; - } - this->rot_angle = Base::toDegrees(angle); + h.assignProperty(value.getRotation(), eps); } } @@ -2524,13 +2567,13 @@ QVariant PropertyPlacementItem::value(const App::Property* prop) const double angle; Base::Vector3d dir; value.getRotation().getRawValue(dir, angle); - if (!init_axis) { + if (!h.isAxisInitialized()) { if (m_a->hasExpression()) { QString str = m_a->expressionAsString(); - const_cast(this)->rot_angle = str.toDouble(); + angle = str.toDouble(); } else { - const_cast(this)->rot_angle = Base::toDegrees(angle); + angle = Base::toDegrees(angle); } PropertyItem* x = m_d->child(0); @@ -2548,8 +2591,7 @@ QVariant PropertyPlacementItem::value(const App::Property* prop) const QString str = z->expressionAsString(); dir.z = str.toDouble(); } - const_cast(this)->rot_axis = dir; - const_cast(this)->init_axis = true; + h.setValue(dir, angle); } return QVariant::fromValue(value); } @@ -2606,12 +2648,16 @@ void PropertyPlacementItem::setValue(const QVariant& value) return; // Accept this only if the user changed the axis, angle or position but // not if >this< item loses focus - if (!changed_value) + if (!h.hasChangedAndReset()) return; - changed_value = false; + const Base::Placement& val = value.value(); Base::Vector3d pos = val.getPosition(); + Base::Vector3d axis; + double angle; + h.getValue(axis, angle); + Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString data = QString::fromLatin1("App.Placement(" "App.Vector(%1,%2,%3)," @@ -2619,10 +2665,10 @@ void PropertyPlacementItem::setValue(const QVariant& value) .arg(Base::UnitsApi::toNumber(pos.x, format)) .arg(Base::UnitsApi::toNumber(pos.y, format)) .arg(Base::UnitsApi::toNumber(pos.z, format)) - .arg(Base::UnitsApi::toNumber(rot_axis.x, format)) - .arg(Base::UnitsApi::toNumber(rot_axis.y, format)) - .arg(Base::UnitsApi::toNumber(rot_axis.z, format)) - .arg(Base::UnitsApi::toNumber(rot_angle, format)); + .arg(Base::UnitsApi::toNumber(axis.x, format)) + .arg(Base::UnitsApi::toNumber(axis.y, format)) + .arg(Base::UnitsApi::toNumber(axis.z, format)) + .arg(Base::UnitsApi::toNumber(angle, format)); setPropertyValue(data); } diff --git a/src/Gui/propertyeditor/PropertyItem.h b/src/Gui/propertyeditor/PropertyItem.h index 20ac165fdb..9483b4d3b6 100644 --- a/src/Gui/propertyeditor/PropertyItem.h +++ b/src/Gui/propertyeditor/PropertyItem.h @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef Q_MOC_RUN Q_DECLARE_METATYPE(Base::Vector3f) @@ -657,6 +658,28 @@ private: PropertyFloatItem* m_a44; }; +class RotationHelper +{ +public: + RotationHelper(); + void setChanged(bool); + bool hasChangedAndReset(); + bool isAxisInitialized() const; + void setValue(const Base::Vector3d& axis, double angle); + void getValue(Base::Vector3d& axis, double& angle) const; + double getAngle(const Base::Rotation& val) const; + Base::Rotation setAngle(double); + Base::Vector3d getAxis() const; + Base::Rotation setAxis(const Base::Rotation& value, const Base::Vector3d& axis); + void assignProperty(const Base::Rotation& value, double eps); + +private: + bool init_axis; + bool changed_value; + double rot_angle; + Base::Vector3d rot_axis; +}; + /** * Edit properties of rotation type. * \author Werner Mayer @@ -689,10 +712,7 @@ protected: virtual void setValue(const QVariant&); private: - bool init_axis; - bool changed_value; - double rot_angle; - Base::Vector3d rot_axis; + mutable RotationHelper h; PropertyUnitItem * m_a; PropertyVectorItem* m_d; }; @@ -752,10 +772,7 @@ protected: virtual void setValue(const QVariant&); private: - bool init_axis; - bool changed_value; - double rot_angle; - Base::Vector3d rot_axis; + mutable RotationHelper h; PropertyUnitItem * m_a; PropertyVectorItem* m_d; PropertyVectorDistanceItem* m_p; From 0bd0ec5ed24320d0db16a39be596b5c3fd2a7560 Mon Sep 17 00:00:00 2001 From: 0penBrain <48731257+0penBrain@users.noreply.github.com> Date: Fri, 29 Oct 2021 17:23:48 +0200 Subject: [PATCH 068/138] [PartDesign] Remove useless function overriding Double-click is already handled by PartDesign::ViewProvider::doubleClicked() Overridings in Loft & Pipe brings no specific behavior By removing overridings, behavioral consistency is improved both in ... ... undo commands & body activation management when PartDesign items ... ... are double-clicked --- src/Mod/PartDesign/Gui/ViewProviderLoft.cpp | 5 ----- src/Mod/PartDesign/Gui/ViewProviderLoft.h | 1 - src/Mod/PartDesign/Gui/ViewProviderPipe.cpp | 5 ----- src/Mod/PartDesign/Gui/ViewProviderPipe.h | 1 - 4 files changed, 12 deletions(-) diff --git a/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp b/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp index 8cbb387b47..6f260f3b78 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderLoft.cpp @@ -81,11 +81,6 @@ void ViewProviderLoft::setupContextMenu(QMenu* menu, QObject* receiver, const ch PartDesignGui::ViewProvider::setupContextMenu(menu, receiver, member); } -bool ViewProviderLoft::doubleClicked(void) -{ - return PartDesignGui::setEdit(pcObject); -} - bool ViewProviderLoft::setEdit(int ModNum) { if (ModNum == ViewProvider::Default) diff --git a/src/Mod/PartDesign/Gui/ViewProviderLoft.h b/src/Mod/PartDesign/Gui/ViewProviderLoft.h index dcec346ee3..01f24e76c5 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderLoft.h +++ b/src/Mod/PartDesign/Gui/ViewProviderLoft.h @@ -41,7 +41,6 @@ public: /// grouping handling std::vector claimChildren(void)const; void setupContextMenu(QMenu*, QObject*, const char*); - bool doubleClicked(); virtual bool onDelete(const std::vector &); void highlightReferences(const bool on, bool auxiliary); diff --git a/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp b/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp index 490bddf704..5c875153be 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderPipe.cpp @@ -89,11 +89,6 @@ void ViewProviderPipe::setupContextMenu(QMenu* menu, QObject* receiver, const ch PartDesignGui::ViewProvider::setupContextMenu(menu, receiver, member); } -bool ViewProviderPipe::doubleClicked(void) -{ - return PartDesignGui::setEdit(pcObject); -} - bool ViewProviderPipe::setEdit(int ModNum) { if (ModNum == ViewProvider::Default ) setPreviewDisplayMode(true); diff --git a/src/Mod/PartDesign/Gui/ViewProviderPipe.h b/src/Mod/PartDesign/Gui/ViewProviderPipe.h index 7fbf6647ed..e3efe3a695 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderPipe.h +++ b/src/Mod/PartDesign/Gui/ViewProviderPipe.h @@ -49,7 +49,6 @@ public: /// grouping handling std::vector claimChildren(void)const; void setupContextMenu(QMenu*, QObject*, const char*); - bool doubleClicked(); virtual bool onDelete(const std::vector &); void highlightReferences(Reference mode, bool on); From 971057539f900aa5592edbe054d2d7719b37fbda Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 29 Oct 2021 20:02:09 +0200 Subject: [PATCH 069/138] Unit test: [skip ci] gimbal lock --- src/Mod/Test/BaseTests.py | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/Mod/Test/BaseTests.py b/src/Mod/Test/BaseTests.py index 61a47a1b44..857e55d41b 100644 --- a/src/Mod/Test/BaseTests.py +++ b/src/Mod/Test/BaseTests.py @@ -244,6 +244,58 @@ class ParameterTestCase(unittest.TestCase): r.invert() self.assertTrue(r.isSame(s)) + # gimbal lock (north pole) + r=FreeCAD.Rotation() + r.setYawPitchRoll(20, 90, 10) + a=r.getYawPitchRoll() + s=FreeCAD.Rotation() + s.setYawPitchRoll(*a) + self.assertAlmostEqual(a[0], 0.0) + self.assertAlmostEqual(a[1], 90.0) + self.assertAlmostEqual(a[2], -10.0) + self.assertTrue(r.isSame(s, 1e-12)) + + # gimbal lock (south pole) + r=FreeCAD.Rotation() + r.setYawPitchRoll(20, -90, 10) + a=r.getYawPitchRoll() + s=FreeCAD.Rotation() + s.setYawPitchRoll(*a) + self.assertAlmostEqual(a[0], 0.0) + self.assertAlmostEqual(a[1], -90.0) + self.assertAlmostEqual(a[2], 30.0) + self.assertTrue(r.isSame(s, 1e-12)) + + def testYawPitchRoll(self): + def getYPR1(yaw, pitch, roll): + r = FreeCAD.Rotation() + r.setYawPitchRoll(yaw, pitch, roll) + return r + def getYPR2(yaw, pitch, roll): + rx = FreeCAD.Rotation() + ry = FreeCAD.Rotation() + rz = FreeCAD.Rotation() + + rx.Axis = FreeCAD.Vector(1,0,0) + ry.Axis = FreeCAD.Vector(0,1,0) + rz.Axis = FreeCAD.Vector(0,0,1) + + rx.Angle = math.radians(roll) + ry.Angle = math.radians(pitch) + rz.Angle = math.radians(yaw) + + return rz.multiply(ry).multiply(rx) + + angles = [] + angles.append((10,10,10)) + angles.append((13,45,-24)) + angles.append((10,-90,20)) + + for i in angles: + r = getYPR1(*i) + s = getYPR2(*i) + self.assertTrue(r.isSame(s, 1e-12)) + def testBounding(self): b=FreeCAD.BoundBox() b.setVoid() From 01d8d26bb4b3c2f8da6717159963a05007149301 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 2 Aug 2021 11:34:42 +0800 Subject: [PATCH 070/138] Gui: fix document 'modified' status on view property change --- src/Gui/ViewProviderDocumentObject.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index 0230117675..ec1e08c199 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -196,12 +196,19 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop) // this is undesired behaviour. So, if this change marks the document as // modified then it must be be reversed. if (!testStatus(Gui::ViewStatus::TouchDocument)) { - bool mod = false; - if (pcDocument) - mod = pcDocument->isModified(); + // Note: reverting document modified status like that is not + // appropreiate because we can't tell if there is any other + // property being changed due to the change of Visibility here. + // Temporary setting the Visibility property as 'NoModify' is + // the proper way. + Base::ObjectStatusLocker guard( + App::Property::NoModify, &Visibility); + // bool mod = false; + // if (pcDocument) + // mod = pcDocument->isModified(); getObject()->Visibility.setValue(Visibility.getValue()); - if (pcDocument) - pcDocument->setModified(mod); + // if (pcDocument) + // pcDocument->setModified(mod); } else { getObject()->Visibility.setValue(Visibility.getValue()); @@ -215,7 +222,10 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop) } } - if (pcDocument && !pcDocument->isModified() && testStatus(Gui::ViewStatus::TouchDocument)) { + if (prop && !prop->testStatus(App::Property::NoModify) + && pcDocument + && !pcDocument->isModified() + && testStatus(Gui::ViewStatus::TouchDocument)) { if (prop) FC_LOG(prop->getFullName() << " changed"); pcDocument->setModified(true); From 414ea71a28f793c4982912a364a3d0b71f8e66f4 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 2 Aug 2021 11:36:00 +0800 Subject: [PATCH 071/138] Part: fix unnecessary document 'modified' status --- src/Mod/Part/Gui/ViewProviderExt.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index daf355e909..64f92b99e9 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -104,6 +104,7 @@ #include #include #include +#include #include #include @@ -396,6 +397,12 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) // if the object was invisible and has been changed, recreate the visual if (prop == &Visibility && (isUpdateForced() || Visibility.getValue()) && VisualTouched) { updateVisual(); + // updateVisual() may not be triggered by any change (e.g. + // triggered by an external object through forceUpdate()). And + // since DiffuseColor is not changed here either, do not falsly set + // the document modified status + Base::ObjectStatusLocker guard( + App::Property::NoModify, &DiffuseColor); // The material has to be checked again (#0001736) onChanged(&DiffuseColor); } @@ -1292,8 +1299,8 @@ void ViewProviderPartExt::updateVisual() void ViewProviderPartExt::forceUpdate(bool enable) { if(enable) { if(++forceUpdateCount == 1) { - if(!isShow()) - Visibility.touch(); + if(!isShow() && VisualTouched) + updateVisual(); } }else if(forceUpdateCount) --forceUpdateCount; From fc9d3547ad3ae30236cb847acf4ef89aa1216683 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 2 Aug 2021 11:38:01 +0800 Subject: [PATCH 072/138] App: fix external document loading The problem happens when partial loading is enabled. If document A contains a link to some object in document B, it will load B as partial document with only that object and its necessary dependencies. But if document A contains another link to some object in document C which also has a link to some object in document B, the link in document C may not be restored, because document B is partially loaded without the linked object. This patch will check for this case and reload document B for more objects. See an example reported in https://forum.freecadweb.org/viewtopic.php?p=495078#p495078 --- src/App/Application.cpp | 345 +++++++++++++++++++++++-------------- src/App/Application.h | 26 ++- src/App/Document.cpp | 20 ++- src/App/Document.h | 7 +- src/App/DocumentObserver.h | 8 + src/App/PropertyLinks.cpp | 69 ++++++-- src/Gui/Document.cpp | 2 +- 7 files changed, 311 insertions(+), 166 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index fb2755571f..97c3bce6b5 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -55,6 +55,8 @@ #include #endif +#include + #include "Application.h" #include "Document.h" @@ -99,6 +101,7 @@ #include "Document.h" #include "DocumentObjectGroup.h" #include "DocumentObjectFileIncluded.h" +#include "DocumentObserver.h" #include "InventorObject.h" #include "VRMLObject.h" #include "Annotation.h" @@ -487,6 +490,7 @@ bool Application::closeDocument(const char* name) setActiveDocument((Document*)0); std::unique_ptr delDoc (pos->second); DocMap.erase( pos ); + DocFileMap.erase(FileInfo(delDoc->FileName.getValue()).filePath()); _objCount = -1; @@ -566,8 +570,10 @@ int Application::addPendingDocument(const char *FileName, const char *objName, b return -1; assert(FileName && FileName[0]); assert(objName && objName[0]); - auto ret = _pendingDocMap.emplace(FileName,std::set()); - ret.first->second.emplace(objName); + if(!_docReloadAttempts[FileName].emplace(objName).second) + return -1; + auto ret = _pendingDocMap.emplace(FileName,std::vector()); + ret.first->second.push_back(objName); if(ret.second) { _pendingDocs.push_back(ret.first->first.c_str()); return 1; @@ -623,6 +629,41 @@ Document* Application::openDocument(const char * FileName, bool createView) { return 0; } +Document *Application::getDocumentByPath(const char *path, int checkCanonical) const { + if(!path || !path[0]) + return nullptr; + if(DocFileMap.empty()) { + for(auto &v : DocMap) { + const auto &file = v.second->FileName.getStrValue(); + if(file.size()) + DocFileMap[FileInfo(file.c_str()).filePath()] = v.second; + } + } + auto it = DocFileMap.find(FileInfo(path).filePath()); + if(it != DocFileMap.end()) + return it->second; + + if (!checkCanonical) + return nullptr; + + std::string filepath = FileInfo(path).filePath(); + QString canonicalPath = QFileInfo(QString::fromUtf8(path)).canonicalFilePath(); + for (auto &v : DocMap) { + QFileInfo fi(QString::fromUtf8(v.second->FileName.getValue())); + if (canonicalPath == fi.canonicalFilePath()) { + if (checkCanonical == 1) + return v.second; + bool samePath = (canonicalPath == QString::fromUtf8(filepath.c_str())); + FC_WARN("Identical physical path '" << canonicalPath.toUtf8().constData() << "'\n" + << (samePath?"":" for file '") << (samePath?"":filepath.c_str()) << (samePath?"":"'\n") + << " with existing document '" << v.second->Label.getValue() + << "' in path: '" << v.second->FileName.getValue() << "'"); + break; + } + } + return nullptr; +} + std::vector Application::openDocuments(const std::vector &filenames, const std::vector *paths, const std::vector *labels, @@ -640,6 +681,7 @@ std::vector Application::openDocuments(const std::vector _pendingDocs.clear(); _pendingDocsReopen.clear(); _pendingDocMap.clear(); + _docReloadAttempts.clear(); signalStartOpenDocument(); @@ -649,118 +691,162 @@ std::vector Application::openDocuments(const std::vector for (auto &name : filenames) _pendingDocs.push_back(name.c_str()); - std::map newDocs; + std::map timings; FC_TIME_INIT(t); - for (std::size_t count=0;; ++count) { - const char *name = _pendingDocs.front(); - _pendingDocs.pop_front(); - bool isMainDoc = count < filenames.size(); + std::vector openedDocs; - try { - _objCount = -1; - std::set objNames; - if (_allowPartial) { - auto it = _pendingDocMap.find(name); - if (it != _pendingDocMap.end()) - objNames.swap(it->second); - } + int pass = 0; + do { + std::set newDocs; + for (std::size_t count=0;; ++count) { + std::string name = std::move(_pendingDocs.front()); + _pendingDocs.pop_front(); + bool isMainDoc = (pass == 0 && count < filenames.size()); - FC_TIME_INIT(t1); - DocTiming timing; + try { + _objCount = -1; + std::vector objNames; + if (_allowPartial) { + auto it = _pendingDocMap.find(name); + if (it != _pendingDocMap.end()) { + if(isMainDoc) + it->second.clear(); + else + objNames.swap(it->second); + _pendingDocMap.erase(it); + } + } - const char *path = name; - const char *label = 0; - if (isMainDoc) { - if (paths && paths->size()>count) - path = (*paths)[count].c_str(); + FC_TIME_INIT(t1); + DocTiming timing; - if (labels && labels->size()>count) - label = (*labels)[count].c_str(); - } + const char *path = name.c_str(); + const char *label = 0; + if (isMainDoc) { + if (paths && paths->size()>count) + path = (*paths)[count].c_str(); - auto doc = openDocumentPrivate(path, name, label, isMainDoc, createView, objNames); - FC_DURATION_PLUS(timing.d1,t1); - if (doc) - newDocs.emplace(doc,timing); + if (labels && labels->size()>count) + label = (*labels)[count].c_str(); + } + + auto doc = openDocumentPrivate(path, name.c_str(), label, isMainDoc, createView, std::move(objNames)); + FC_DURATION_PLUS(timing.d1,t1); + if (doc) { + timings[doc].d1 += timing.d1; + newDocs.emplace(doc); + } - if (isMainDoc) - res[count] = doc; - _objCount = -1; - } - catch (const Base::Exception &e) { - if (!errs && isMainDoc) - throw; - if (errs && isMainDoc) - (*errs)[count] = e.what(); - else - Console().Error("Exception opening file: %s [%s]\n", name, e.what()); - } - catch (const std::exception &e) { - if (!errs && isMainDoc) - throw; - if (errs && isMainDoc) - (*errs)[count] = e.what(); - else - Console().Error("Exception opening file: %s [%s]\n", name, e.what()); - } - catch (...) { - if (errs) { if (isMainDoc) - (*errs)[count] = "unknown error"; + res[count] = doc; + _objCount = -1; } - else { - _pendingDocs.clear(); + catch (const Base::Exception &e) { + e.ReportException(); + if (!errs && isMainDoc) + throw; + if (errs && isMainDoc) + (*errs)[count] = e.what(); + else + Console().Error("Exception opening file: %s [%s]\n", name.c_str(), e.what()); + } + catch (const std::exception &e) { + if (!errs && isMainDoc) + throw; + if (errs && isMainDoc) + (*errs)[count] = e.what(); + else + Console().Error("Exception opening file: %s [%s]\n", name.c_str(), e.what()); + } + catch (...) { + if (errs) { + if (isMainDoc) + (*errs)[count] = "unknown error"; + } + else { + _pendingDocs.clear(); + _pendingDocsReopen.clear(); + _pendingDocMap.clear(); + throw; + } + } + + if (_pendingDocs.empty()) { + if(_pendingDocsReopen.empty()) + break; + _pendingDocs = std::move(_pendingDocsReopen); _pendingDocsReopen.clear(); - _pendingDocMap.clear(); - throw; + for(auto &file : _pendingDocs) { + auto doc = getDocumentByPath(file.c_str()); + if(doc) + closeDocument(doc->getName()); + } } } - if (_pendingDocs.empty()) { - if (_pendingDocsReopen.empty()) - break; - _allowPartial = false; - _pendingDocs.swap(_pendingDocsReopen); + ++pass; + _pendingDocMap.clear(); + + std::vector docs; + docs.reserve(newDocs.size()); + for(auto &d : newDocs) { + auto doc = d.getDocument(); + if(!doc) + continue; + // Notify PropertyXLink to attach newly opened documents and restore + // relevant external links + PropertyXLink::restoreDocument(*doc); + docs.push_back(doc); } - } - _pendingDocs.clear(); - _pendingDocsReopen.clear(); - _pendingDocMap.clear(); + Base::SequencerLauncher seq("Postprocessing...", docs.size()); - Base::SequencerLauncher seq("Postprocessing...", newDocs.size()); - - std::vector docs; - docs.reserve(newDocs.size()); - for (auto &v : newDocs) { - // Notify PropertyXLink to attach newly opened documents and restore - // relevant external links - PropertyXLink::restoreDocument(*v.first); - docs.push_back(v.first); - } - - // After external links has been restored, we can now sort the document - // according to their dependency order. - docs = Document::getDependentDocuments(docs, true); - for (auto it=docs.begin(); it!=docs.end();) { - Document *doc = *it; - // It is possible that the newly opened document depends on an existing - // document, which will be included with the above call to - // Document::getDependentDocuments(). Make sure to exclude that. - auto dit = newDocs.find(doc); - if (dit == newDocs.end()) { - it = docs.erase(it); - continue; + // After external links has been restored, we can now sort the document + // according to their dependency order. + try { + docs = Document::getDependentDocuments(docs, true); + } catch (Base::Exception &e) { + e.ReportException(); } - ++it; - FC_TIME_INIT(t1); - // Finalize document restoring with the correct order - doc->afterRestore(true); - FC_DURATION_PLUS(dit->second.d2,t1); - seq.next(); - } + for(auto it=docs.begin(); it!=docs.end();) { + auto doc = *it; + + // It is possible that the newly opened document depends on an existing + // document, which will be included with the above call to + // Document::getDependentDocuments(). Make sure to exclude that. + if(!newDocs.count(doc)) { + it = docs.erase(it); + continue; + } + + auto &timing = timings[doc]; + FC_TIME_INIT(t1); + // Finalize document restoring with the correct order + if(doc->afterRestore(true)) { + openedDocs.push_back(doc); + it = docs.erase(it); + } else { + ++it; + // Here means this is a partial loaded document, and we need to + // reload it fully because of touched objects. The reason of + // reloading a partial document with touched object is because + // partial document is supposed to be readonly, while a + // 'touched' object requires recomputation. And an object may + // become touched during restoring if externally linked + // document time stamp mismatches with the stamp saved. + _pendingDocs.push_back(doc->FileName.getValue()); + _pendingDocMap.erase(doc->FileName.getValue()); + } + FC_DURATION_PLUS(timing.d2,t1); + seq.next(); + } + // Close the document for reloading + for(auto doc : docs) + closeDocument(doc->getName()); + + }while(!_pendingDocs.empty()); // Set the active document using the first successfully restored main // document (i.e. documents explicitly asked for by caller). @@ -771,14 +857,14 @@ std::vector Application::openDocuments(const std::vector } } - for (auto doc : docs) { - auto &timing = newDocs[doc]; - FC_DURATION_LOG(timing.d1, doc->getName() << " restore"); - FC_DURATION_LOG(timing.d2, doc->getName() << " postprocess"); + for (auto &doc : openedDocs) { + auto &timing = timings[doc]; + FC_DURATION_LOG(timing.d1, doc.getDocumentName() << " restore"); + FC_DURATION_LOG(timing.d2, doc.getDocumentName() << " postprocess"); } FC_TIME_LOG(t,"total"); - _isRestoring = false; + signalFinishOpenDocument(); return res; } @@ -786,7 +872,7 @@ std::vector Application::openDocuments(const std::vector Document* Application::openDocumentPrivate(const char * FileName, const char *propFileName, const char *label, bool isMainDoc, bool createView, - const std::set &objNames) + std::vector &&objNames) { FileInfo File(FileName); @@ -797,55 +883,51 @@ Document* Application::openDocumentPrivate(const char * FileName, } // Before creating a new document we check whether the document is already open - std::string filepath = File.filePath(); - QString canonicalPath = QFileInfo(QString::fromUtf8(FileName)).canonicalFilePath(); - for (std::map::iterator it = DocMap.begin(); it != DocMap.end(); ++it) { - // get unique path separators - std::string fi = FileInfo(it->second->FileName.getValue()).filePath(); - if (filepath != fi) { - if (canonicalPath == QFileInfo(QString::fromUtf8(fi.c_str())).canonicalFilePath()) { - bool samePath = (canonicalPath == QString::fromUtf8(FileName)); - FC_WARN("Identical physical path '" << canonicalPath.toUtf8().constData() << "'\n" - << (samePath?"":" for file '") << (samePath?"":FileName) << (samePath?"":"'\n") - << " with existing document '" << it->second->Label.getValue() - << "' in path: '" << it->second->FileName.getValue() << "'"); - } - continue; - } - if(it->second->testStatus(App::Document::PartialDoc) - || it->second->testStatus(App::Document::PartialRestore)) { + auto doc = getDocumentByPath(File.filePath().c_str(), 2); + if(doc) { + if(doc->testStatus(App::Document::PartialDoc) + || doc->testStatus(App::Document::PartialRestore)) { // Here means a document is already partially loaded, but the document // is requested again, either partial or not. We must check if the // document contains the required object if(isMainDoc) { // Main document must be open fully, so close and reopen - closeDocument(it->first.c_str()); - break; - } - - if(_allowPartial) { + closeDocument(doc->getName()); + doc = nullptr; + } else if(_allowPartial) { bool reopen = false; for(auto &name : objNames) { - auto obj = it->second->getObject(name.c_str()); + auto obj = doc->getObject(name.c_str()); if(!obj || obj->testStatus(App::PartialObject)) { reopen = true; + // NOTE: We are about to reload this document with + // extra objects. However, it is possible to repeat + // this process several times, if it is linked by + // multiple documents and each with a different set of + // objects. To partially solve this problem, we do not + // close and reopen the document immediately here, but + // add it to _pendingDocsReopen to delay reloading. + for(auto obj : doc->getObjects()) + objNames.push_back(obj->getNameInDocument()); + _pendingDocMap[doc->FileName.getValue()] = std::move(objNames); break; } } if(!reopen) return 0; } - auto &names = _pendingDocMap[FileName]; - names.clear(); - _pendingDocsReopen.push_back(FileName); - return 0; + + if(doc) { + _pendingDocsReopen.emplace_back(FileName); + return 0; + } } if(!isMainDoc) return 0; - - return it->second; + else if(doc) + return doc; } std::string name; @@ -867,6 +949,8 @@ Document* Application::openDocumentPrivate(const char * FileName, try { // read the document newDoc->restore(File.filePath().c_str(),true,objNames); + if(DocFileMap.size()) + DocFileMap[FileInfo(newDoc->FileName.getValue()).filePath()] = newDoc; return newDoc; } // if the project file itself is corrupt then @@ -1463,6 +1547,7 @@ void Application::slotStartSaveDocument(const App::Document& doc, const std::str void Application::slotFinishSaveDocument(const App::Document& doc, const std::string& filename) { + DocFileMap.clear(); this->signalFinishSaveDocument(doc, filename); } diff --git a/src/App/Application.h b/src/App/Application.h index 506c3a364e..5042df74db 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -120,6 +120,16 @@ public: App::Document* getActiveDocument(void) const; /// Retrieve a named document App::Document* getDocument(const char *Name) const; + /** Retrieve a document based on file path + * + * @param path: file path + * @param checkCanonical: if zero, only match absolute file path. If 1, + * then match by canonical file path, where any intermediate '.' and '..' + * and symlinks are resolved. If 2, then only print warning message if + * there is identical canonical file path found, but will not return the + * matched document. + */ + App::Document* getDocumentByPath(const char *path, int checkCanonical=0) const; /// gets the (internal) name of the document const char * getDocumentName(const App::Document* ) const; /// get a list of all documents in the application @@ -190,6 +200,8 @@ public: boost::signals2::signal signalStartRestoreDocument; /// signal on restoring Document boost::signals2::signal signalFinishRestoreDocument; + /// signal on pending reloading of a partial Document + boost::signals2::signal signalPendingReloadDocument; /// signal on starting to save Document boost::signals2::signal signalStartSaveDocument; /// signal on saved Document @@ -441,7 +453,7 @@ protected: /// open single document only App::Document* openDocumentPrivate(const char * FileName, const char *propFileName, - const char *label, bool isMainDoc, bool createView, const std::set &objNames); + const char *label, bool isMainDoc, bool createView, std::vector &&objNames); /// Helper class for App::Document to signal on close/abort transaction class AppExport TransactionSignaller { @@ -559,13 +571,19 @@ private: std::vector _mImportTypes; std::vector _mExportTypes; std::map DocMap; + mutable std::map DocFileMap; std::map mpcPramManager; std::map &_mConfig; App::Document* _pActiveDoc; - std::deque _pendingDocs; - std::deque _pendingDocsReopen; - std::map > _pendingDocMap; + std::deque _pendingDocs; + std::deque _pendingDocsReopen; + std::map > _pendingDocMap; + + // To prevent infinite recursion of reloading a partial document due a truely + // missing object + std::map > _docReloadAttempts; + bool _isRestoring; bool _allowPartial; bool _isClosingAll; diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 1f108b8df3..f6ec4f46ac 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -1866,7 +1866,6 @@ void Document::exportObjects(const std::vector& obj, std:: #define FC_ELEMENT_OBJECT_DEPS "ObjectDeps" #define FC_ATTR_DEP_COUNT "Count" #define FC_ATTR_DEP_OBJ_NAME "Name" -#define FC_ATTR_DEP_COUNT "Count" #define FC_ATTR_DEP_ALLOW_PARTIAL "AllowPartial" #define FC_ELEMENT_OBJECT_DEP "Dep" @@ -2693,7 +2692,7 @@ bool Document::isAnyRestoring() { // Open the document void Document::restore (const char *filename, - bool delaySignal, const std::set &objNames) + bool delaySignal, const std::vector &objNames) { clearUndos(); d->activeObject = 0; @@ -2752,8 +2751,7 @@ void Document::restore (const char *filename, d->partialLoadObjects.emplace(name,true); try { Document::Restore(reader); - } - catch (const Base::Exception& e) { + } catch (const Base::Exception& e) { Base::Console().Error("Invalid Document.xml: %s\n", e.what()); setStatus(Document::RestoreError, true); } @@ -2777,15 +2775,16 @@ void Document::restore (const char *filename, afterRestore(true); } -void Document::afterRestore(bool checkPartial) { +bool Document::afterRestore(bool checkPartial) { Base::FlagToggler<> flag(_IsRestoring,false); if(!afterRestore(d->objectArray,checkPartial)) { FC_WARN("Reload partial document " << getName()); - restore(); - return; + GetApplication().signalPendingReloadDocument(*this); + return false; } GetApplication().signalFinishRestoreDocument(*this); setStatus(Document::Restoring, false); + return true; } bool Document::afterRestore(const std::vector &objArray, bool checkPartial) @@ -2861,9 +2860,12 @@ bool Document::afterRestore(const std::vector &objArray, bool std::string errMsg; if(link && (res=link->checkRestore(&errMsg))) { d->touchedObjs.insert(obj); - if(res==1) + if(res==1 || checkPartial) { FC_WARN(obj->getFullName() << '.' << prop->getName() << ": " << errMsg); - else { + setStatus(Document::LinkStampChanged, true); + if(checkPartial) + return false; + } else { FC_ERR(obj->getFullName() << '.' << prop->getName() << ": " << errMsg); d->addRecomputeLog(errMsg,obj); setStatus(Document::PartialRestore, true); diff --git a/src/App/Document.h b/src/App/Document.h index a4b2285cdc..16e8053fb8 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -74,7 +74,8 @@ public: PartialDoc = 7, AllowPartialRecompute = 8, // allow recomputing editing object if SkipRecompute is set TempDoc = 9, // Mark as temporary document without prompt for save - RestoreError = 10 + RestoreError = 10, + LinkStampChanged = 11, // Indicates during restore time if any linked document's time stamp has changed }; /** @name Properties */ @@ -195,8 +196,8 @@ public: bool saveCopy(const char* file) const; /// Restore the document from the file in Property Path void restore (const char *filename=0, - bool delaySignal=false, const std::set &objNames={}); - void afterRestore(bool checkPartial=false); + bool delaySignal=false, const std::vector &objNames={}); + bool afterRestore(bool checkPartial=false); bool afterRestore(const std::vector &, bool checkPartial=false); enum ExportStatus { NotExporting, diff --git a/src/App/DocumentObserver.h b/src/App/DocumentObserver.h index c32c780a01..b93373ae2b 100644 --- a/src/App/DocumentObserver.h +++ b/src/App/DocumentObserver.h @@ -62,6 +62,14 @@ public: /*! Assignment operator */ void operator=(const std::string&); + bool operator==(const DocumentT &other) const { + return document == other.document; + } + + bool operator<(const DocumentT &other) const { + return document < other.document; + } + /*! Get a pointer to the document or 0 if it doesn't exist any more. */ Document* getDocument() const; /*! Get the name of the document. */ diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index e78abd3c22..f92879aa4e 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -2458,6 +2458,7 @@ class App::DocInfo : public: typedef boost::signals2::scoped_connection Connection; Connection connFinishRestoreDocument; + Connection connPendingReloadDocument; Connection connDeleteDocument; Connection connSaveDocument; Connection connDeletedObject; @@ -2589,6 +2590,7 @@ public: FC_LOG("deinit " << (pcDoc?pcDoc->getName():filePath())); assert(links.empty()); connFinishRestoreDocument.disconnect(); + connPendingReloadDocument.disconnect(); connDeleteDocument.disconnect(); connSaveDocument.disconnect(); connDeletedObject.disconnect(); @@ -2606,6 +2608,8 @@ public: App::Application &app = App::GetApplication(); connFinishRestoreDocument = app.signalFinishRestoreDocument.connect( boost::bind(&DocInfo::slotFinishRestoreDocument,this,bp::_1)); + connPendingReloadDocument = app.signalPendingReloadDocument.connect( + boost::bind(&DocInfo::slotFinishRestoreDocument,this,bp::_1)); connDeleteDocument = app.signalDeleteDocument.connect( boost::bind(&DocInfo::slotDeleteDocument,this,bp::_1)); connSaveDocument = app.signalSaveDocument.connect( @@ -2617,6 +2621,8 @@ public: else{ for(App::Document *doc : App::GetApplication().getDocuments()) { if(getFullPath(doc->getFileName()) == fullpath) { + if(doc->testStatus(App::Document::PartialDoc) && !doc->getObject(objName)) + break; attach(doc); return; } @@ -2642,22 +2648,36 @@ public: continue; } auto obj = doc->getObject(link->objectName.c_str()); - if(!obj) + if(obj) + link->restoreLink(obj); + else if (doc->testStatus(App::Document::PartialDoc)) { + App::GetApplication().addPendingDocument( + doc->FileName.getValue(), + link->objectName.c_str(), + false); + FC_WARN("reloading partial document '" << doc->FileName.getValue() + << "' due to object " << link->objectName); + } else FC_WARN("object '" << link->objectName << "' not found in document '" << doc->getName() << "'"); - else - link->restoreLink(obj); } for(auto &v : parentLinks) { v.first->setFlag(PropertyLinkBase::LinkRestoring); v.first->aboutToSetValue(); for(auto link : v.second) { auto obj = doc->getObject(link->objectName.c_str()); - if(!obj) + if(obj) + link->restoreLink(obj); + else if (doc->testStatus(App::Document::PartialDoc)) { + App::GetApplication().addPendingDocument( + doc->FileName.getValue(), + link->objectName.c_str(), + false); + FC_WARN("reloading partial document '" << doc->FileName.getValue() + << "' due to object " << link->objectName); + } else FC_WARN("object '" << link->objectName << "' not found in document '" << doc->getName() << "'"); - else - link->restoreLink(obj); } v.first->hasSetValue(); v.first->setFlag(PropertyLinkBase::LinkRestoring,false); @@ -2723,16 +2743,17 @@ public: } } - // time stamp changed, touch the linking document. Unfortunately, there - // is no way to setModfied() for an App::Document. We don't want to touch - // all PropertyXLink for a document, because the linked object is - // potentially unchanged. So we just touch at most one. + // time stamp changed, touch the linking document. std::set docs; for(auto link : links) { auto linkdoc = static_cast(link->getContainer())->getDocument(); auto ret = docs.insert(linkdoc); - if(ret.second && !linkdoc->isTouched()) - link->touch(); + if(ret.second) { + // This will signal the Gui::Document to call setModified(); + FC_LOG("touch document " << linkdoc->getName() + << " on time stamp change of " << link->getFullName()); + linkdoc->Comment.touch(); + } } } @@ -3473,7 +3494,12 @@ PropertyXLink::getDocumentOutList(App::Document *doc) { std::map > ret; for(auto &v : _DocInfoMap) { for(auto link : v.second->links) { - if(!v.second->pcDoc) continue; + if(!v.second->pcDoc + || link->getScope() == LinkScope::Hidden + || link->testStatus(Property::PropTransient) + || link->testStatus(Property::Transient) + || link->testStatus(Property::PropNoPersist)) + continue; auto obj = dynamic_cast(link->getContainer()); if(!obj || !obj->getNameInDocument() || !obj->getDocument()) continue; @@ -3493,6 +3519,11 @@ PropertyXLink::getDocumentInList(App::Document *doc) { continue; auto &docs = ret[v.second->pcDoc]; for(auto link : v.second->links) { + if(link->getScope() == LinkScope::Hidden + || link->testStatus(Property::PropTransient) + || link->testStatus(Property::Transient) + || link->testStatus(Property::PropNoPersist)) + continue; auto obj = dynamic_cast(link->getContainer()); if(obj && obj->getNameInDocument() && obj->getDocument()) docs.insert(obj->getDocument()); @@ -4460,12 +4491,12 @@ void PropertyXLinkContainer::breakLink(App::DocumentObject *obj, bool clear) { } int PropertyXLinkContainer::checkRestore(std::string *msg) const { - if(_LinkRestored) - return 1; - for(auto &v : _XLinks) { - int res = v.second->checkRestore(msg); - if(res) - return res; + if(_LinkRestored) { + for(auto &v : _XLinks) { + int res = v.second->checkRestore(msg); + if(res) + return res; + } } return 0; } diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 589deecede..813e84a69f 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -1499,7 +1499,7 @@ void Document::slotFinishRestoreDocument(const App::Document& doc) } // reset modified flag - setModified(false); + setModified(doc.testStatus(App::Document::LinkStampChanged)); } void Document::slotShowHidden(const App::Document& doc) From 9885dadfff6bc8fe25ba7eb2cb45e4ddaeae1c33 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Fri, 6 Aug 2021 14:46:56 +0800 Subject: [PATCH 073/138] App: use enum in API Application::getDocumentByPath() --- src/App/Application.cpp | 8 ++++---- src/App/Application.h | 29 +++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 97c3bce6b5..72db851cba 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -629,7 +629,7 @@ Document* Application::openDocument(const char * FileName, bool createView) { return 0; } -Document *Application::getDocumentByPath(const char *path, int checkCanonical) const { +Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCanonical) const { if(!path || !path[0]) return nullptr; if(DocFileMap.empty()) { @@ -643,7 +643,7 @@ Document *Application::getDocumentByPath(const char *path, int checkCanonical) c if(it != DocFileMap.end()) return it->second; - if (!checkCanonical) + if (checkCanonical == MatchAbsolute) return nullptr; std::string filepath = FileInfo(path).filePath(); @@ -651,7 +651,7 @@ Document *Application::getDocumentByPath(const char *path, int checkCanonical) c for (auto &v : DocMap) { QFileInfo fi(QString::fromUtf8(v.second->FileName.getValue())); if (canonicalPath == fi.canonicalFilePath()) { - if (checkCanonical == 1) + if (checkCanonical == MatchCanonical) return v.second; bool samePath = (canonicalPath == QString::fromUtf8(filepath.c_str())); FC_WARN("Identical physical path '" << canonicalPath.toUtf8().constData() << "'\n" @@ -883,7 +883,7 @@ Document* Application::openDocumentPrivate(const char * FileName, } // Before creating a new document we check whether the document is already open - auto doc = getDocumentByPath(File.filePath().c_str(), 2); + auto doc = getDocumentByPath(File.filePath().c_str(), MatchCanonicalWarning); if(doc) { if(doc->testStatus(App::Document::PartialDoc) || doc->testStatus(App::Document::PartialRestore)) { diff --git a/src/App/Application.h b/src/App/Application.h index 5042df74db..738c0e19d7 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -120,16 +120,33 @@ public: App::Document* getActiveDocument(void) const; /// Retrieve a named document App::Document* getDocument(const char *Name) const; + + /// Path matching mode for getDocumentByPath() + enum PathMatchMode { + /// Match by resolving to absolute file path + MatchAbsolute = 0, + /** Match by absolute path first. If not found then match by resolving + * to canonical file path where any intermediate '.' '..' and symlinks + * are resolved. + */ + MatchCanonical = 1, + /** Same as MatchCanonical, but if a document is found by canonical + * path match, which means the document can be resolved using two + * different absolute path, a warning is printed and the found document + * is not returned. This is to allow the caller to intentionally load + * the same physical file as separate documents. + */ + MatchCanonicalWarning = 2, + }; /** Retrieve a document based on file path * * @param path: file path - * @param checkCanonical: if zero, only match absolute file path. If 1, - * then match by canonical file path, where any intermediate '.' and '..' - * and symlinks are resolved. If 2, then only print warning message if - * there is identical canonical file path found, but will not return the - * matched document. + * @param checkCanonical: file path matching mode, @sa PathMatchMode. + * @return Return the document found by matching with the given path */ - App::Document* getDocumentByPath(const char *path, int checkCanonical=0) const; + App::Document* getDocumentByPath(const char *path, + PathMatchMode checkCanonical = MatchAbsolute) const; + /// gets the (internal) name of the document const char * getDocumentName(const App::Document* ) const; /// get a list of all documents in the application From a2fb4a5d6d4a0f3365669b448617567b7e87223a Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Sat, 30 Oct 2021 12:30:45 +0800 Subject: [PATCH 074/138] Minor code change according to suggestions --- src/App/Application.cpp | 18 +++++++++--------- src/App/Application.h | 4 ++-- src/App/Document.h | 2 +- src/Gui/ViewProviderDocumentObject.cpp | 2 +- src/Mod/Part/Gui/ViewProviderExt.cpp | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 72db851cba..d51a6740fc 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -633,7 +633,7 @@ Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCa if(!path || !path[0]) return nullptr; if(DocFileMap.empty()) { - for(auto &v : DocMap) { + for(const auto &v : DocMap) { const auto &file = v.second->FileName.getStrValue(); if(file.size()) DocFileMap[FileInfo(file.c_str()).filePath()] = v.second; @@ -643,15 +643,15 @@ Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCa if(it != DocFileMap.end()) return it->second; - if (checkCanonical == MatchAbsolute) + if (checkCanonical == PathMatchMode::MatchAbsolute) return nullptr; std::string filepath = FileInfo(path).filePath(); QString canonicalPath = QFileInfo(QString::fromUtf8(path)).canonicalFilePath(); - for (auto &v : DocMap) { + for (const auto &v : DocMap) { QFileInfo fi(QString::fromUtf8(v.second->FileName.getValue())); if (canonicalPath == fi.canonicalFilePath()) { - if (checkCanonical == MatchCanonical) + if (checkCanonical == PathMatchMode::MatchCanonical) return v.second; bool samePath = (canonicalPath == QString::fromUtf8(filepath.c_str())); FC_WARN("Identical physical path '" << canonicalPath.toUtf8().constData() << "'\n" @@ -778,7 +778,7 @@ std::vector Application::openDocuments(const std::vector break; _pendingDocs = std::move(_pendingDocsReopen); _pendingDocsReopen.clear(); - for(auto &file : _pendingDocs) { + for(const auto &file : _pendingDocs) { auto doc = getDocumentByPath(file.c_str()); if(doc) closeDocument(doc->getName()); @@ -791,7 +791,7 @@ std::vector Application::openDocuments(const std::vector std::vector docs; docs.reserve(newDocs.size()); - for(auto &d : newDocs) { + for(const auto &d : newDocs) { auto doc = d.getDocument(); if(!doc) continue; @@ -843,7 +843,7 @@ std::vector Application::openDocuments(const std::vector seq.next(); } // Close the document for reloading - for(auto doc : docs) + for(const auto doc : docs) closeDocument(doc->getName()); }while(!_pendingDocs.empty()); @@ -883,7 +883,7 @@ Document* Application::openDocumentPrivate(const char * FileName, } // Before creating a new document we check whether the document is already open - auto doc = getDocumentByPath(File.filePath().c_str(), MatchCanonicalWarning); + auto doc = getDocumentByPath(File.filePath().c_str(), PathMatchMode::MatchCanonicalWarning); if(doc) { if(doc->testStatus(App::Document::PartialDoc) || doc->testStatus(App::Document::PartialRestore)) { @@ -897,7 +897,7 @@ Document* Application::openDocumentPrivate(const char * FileName, doc = nullptr; } else if(_allowPartial) { bool reopen = false; - for(auto &name : objNames) { + for(const auto &name : objNames) { auto obj = doc->getObject(name.c_str()); if(!obj || obj->testStatus(App::PartialObject)) { reopen = true; diff --git a/src/App/Application.h b/src/App/Application.h index 738c0e19d7..0d38fcfdab 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -122,7 +122,7 @@ public: App::Document* getDocument(const char *Name) const; /// Path matching mode for getDocumentByPath() - enum PathMatchMode { + enum class PathMatchMode { /// Match by resolving to absolute file path MatchAbsolute = 0, /** Match by absolute path first. If not found then match by resolving @@ -145,7 +145,7 @@ public: * @return Return the document found by matching with the given path */ App::Document* getDocumentByPath(const char *path, - PathMatchMode checkCanonical = MatchAbsolute) const; + PathMatchMode checkCanonical = PathMatchMode::MatchAbsolute) const; /// gets the (internal) name of the document const char * getDocumentName(const App::Document* ) const; diff --git a/src/App/Document.h b/src/App/Document.h index 16e8053fb8..6d0e7c5ec2 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -75,7 +75,7 @@ public: AllowPartialRecompute = 8, // allow recomputing editing object if SkipRecompute is set TempDoc = 9, // Mark as temporary document without prompt for save RestoreError = 10, - LinkStampChanged = 11, // Indicates during restore time if any linked document's time stamp has changed + LinkStampChanged = 11, // Indicates during restore time if any linked document's time stamp has changed }; /** @name Properties */ diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index ec1e08c199..e4cc2bbb2b 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -197,7 +197,7 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop) // modified then it must be be reversed. if (!testStatus(Gui::ViewStatus::TouchDocument)) { // Note: reverting document modified status like that is not - // appropreiate because we can't tell if there is any other + // appropriate because we can't tell if there is any other // property being changed due to the change of Visibility here. // Temporary setting the Visibility property as 'NoModify' is // the proper way. diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 64f92b99e9..9bd7037d1f 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -399,7 +399,7 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) updateVisual(); // updateVisual() may not be triggered by any change (e.g. // triggered by an external object through forceUpdate()). And - // since DiffuseColor is not changed here either, do not falsly set + // since DiffuseColor is not changed here either, do not falsely set // the document modified status Base::ObjectStatusLocker guard( App::Property::NoModify, &DiffuseColor); From c5fb91170b2ee9cf1d2d67243b7fdd5ab0e0bb61 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 30 Oct 2021 14:50:00 +0200 Subject: [PATCH 075/138] dist-git: make script working again for Py3 and Linux --- .../CreatePackagingTargets.cmake | 24 +++++++--- src/Tools/makedist.py | 44 +++++++++++++------ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/cMake/FreeCAD_Helpers/CreatePackagingTargets.cmake b/cMake/FreeCAD_Helpers/CreatePackagingTargets.cmake index 60cd19c758..de8b52c6d3 100644 --- a/cMake/FreeCAD_Helpers/CreatePackagingTargets.cmake +++ b/cMake/FreeCAD_Helpers/CreatePackagingTargets.cmake @@ -5,25 +5,37 @@ macro(CreatePackagingTargets) #add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) add_custom_target(dist-git COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py - --srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR} + --bindir=${CMAKE_BINARY_DIR} + --major=${PACKAGE_VERSION_MAJOR} + --minor=${PACKAGE_VERSION_MINOR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target(distdfsg-git COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py - --srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR} --dfsg + --bindir=${CMAKE_BINARY_DIR} + --major=${PACKAGE_VERSION_MAJOR} + --minor=${PACKAGE_VERSION_MINOR} + --dfsg WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) - if(CMAKE_COMPILER_IS_GNUCXX OR MINGW) + if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX OR MINGW) add_custom_target(distcheck-git COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py - --srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR} --check + --bindir=${CMAKE_BINARY_DIR} + --major=${PACKAGE_VERSION_MAJOR} + --minor=${PACKAGE_VERSION_MINOR} + --check WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target(distcheckdfsg-git COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py - --srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR} --dfsg --check + --bindir=${CMAKE_BINARY_DIR} + --major=${PACKAGE_VERSION_MAJOR} + --minor=${PACKAGE_VERSION_MINOR} + --dfsg + --check WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) - endif(CMAKE_COMPILER_IS_GNUCXX OR MINGW) + endif() endmacro(CreatePackagingTargets) diff --git a/src/Tools/makedist.py b/src/Tools/makedist.py index f4118fe68c..c054534fe8 100644 --- a/src/Tools/makedist.py +++ b/src/Tools/makedist.py @@ -5,23 +5,29 @@ # Python script to make source tarballs. # -import sys, os, getopt, tarfile, gzip, time, io, platform, shutil +import sys, os, getopt, tarfile, gzip, time, io, platform, shutil, subprocess def main(): bindir="." + major="0" + minor="0" dfsg=False check=False - wta="" + wta=None try: - opts, args = getopt.getopt(sys.argv[1:], "sb:", ["srcdir=","bindir=","dfsg", "check"]) + opts, args = getopt.getopt(sys.argv[1:], "sb:", ["srcdir=","bindir=","major=","minor=","dfsg", "check"]) except getopt.GetoptError: pass for o, a in opts: if o in ("-s", "--srcdir"): - print("%s is deprecated -- ignoring" % (o)) + print("{} is deprecated -- ignoring".format(o)) if o in ("-b", "--bindir"): bindir = a + if o in ("--major"): + major = a + if o in ("--minor"): + minor = a if o in ("--dfsg"): dfsg = True wta = "--worktree-attributes" @@ -41,14 +47,15 @@ def main(): info=os.popen("git rev-list HEAD").read() revision='%04d' % (info.count('\n')) - verfile = open("%s/src/Build/Version.h" % (bindir), 'r') - verstream = io.StringIO(verfile.read()) + verfile = open("{}/src/Build/Version.h".format(bindir), 'rb') + verstream = io.BytesIO(verfile.read()) verfile.close() - version_minor = verstream.getvalue().split('FCVersionMinor "')[1][:2] + version_major = major + version_minor = minor PACKAGE_NAME = 'freecad' - version = "0.%s.%s" % (version_minor, revision) + version = "{}.{}.{}".format(version_major, version_minor, revision) DIRNAME = "%(p)s-%(v)s" % {'p': PACKAGE_NAME, 'v': version} TARNAME = DIRNAME + '.tar' @@ -61,10 +68,13 @@ def main(): verinfo.size = len(verstream.getvalue()) verinfo.mtime = time.time() - print(("git archive %s --prefix=%s/ HEAD" % (wta, DIRNAME))) + if wta is None: + print(("git archive --prefix={}/ HEAD".format(DIRNAME))) + else: + print(("git archive {} --prefix={}/ HEAD".format(wta, DIRNAME))) + if platform.system() == 'Windows': - os.popen("git archive %s --prefix=%s/ --output=%s HEAD" - % (wta, DIRNAME, TARNAME)).read() + os.popen("git archive {} --prefix={}/ --output={} HEAD".format(wta, DIRNAME, TARNAME)).read() tar = tarfile.TarFile(mode="a", name=TARNAME) tar.addfile(verinfo, verstream) @@ -77,9 +87,15 @@ def main(): tardata.close() os.remove(TARNAME) else: - tardata = os.popen("git archive %s --prefix=%s/ HEAD" - % (wta, DIRNAME)).read() - tarstream = io.StringIO(tardata) + cmd_line = ["git", "archive"] + if not wta is None: + cmd_line.append(wta) + cmd_line.append("--prefix={}/".format(DIRNAME)) + cmd_line.append("HEAD") + + tardata = subprocess.Popen(cmd_line, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out,err = tardata.communicate() + tarstream = io.BytesIO(out) tar = tarfile.TarFile(mode="a", fileobj=tarstream) tar.addfile(verinfo, verstream) From b3a6f21866c124bca7d1371db922d5b26b98cf07 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 30 Oct 2021 17:29:11 +0200 Subject: [PATCH 076/138] Part: [skip ci] add PartGlobal.h with export/import macros --- src/Mod/Part/PartGlobal.h | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/Mod/Part/PartGlobal.h diff --git a/src/Mod/Part/PartGlobal.h b/src/Mod/Part/PartGlobal.h new file mode 100644 index 0000000000..a6d29ec10c --- /dev/null +++ b/src/Mod/Part/PartGlobal.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (c) 2021 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include + +#ifndef PART_GLOBAL_H +#define PART_GLOBAL_H + + +// Part +#ifndef PartExport +#ifdef Part_EXPORTS +# define PartExport FREECAD_DECL_EXPORT +#else +# define PartExport FREECAD_DECL_IMPORT +#endif +#endif + +// PartGui +#ifndef PartGuiExport +#ifdef PartGui_EXPORTS +# define PartGuiExport FREECAD_DECL_EXPORT +#else +# define PartGuiExport FREECAD_DECL_IMPORT +#endif +#endif + +#endif //PART_GLOBAL_H From 717219bcec1d6987cedc4180f8416a0fb0b3e29e Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 30 Oct 2021 17:57:11 +0200 Subject: [PATCH 077/138] Part: [skip ci] include PartGlobal.h in ViewProviderExt.h --- src/Mod/Part/Gui/ViewProviderExt.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Part/Gui/ViewProviderExt.h b/src/Mod/Part/Gui/ViewProviderExt.h index 2cb55c1873..4dcbd6ac5c 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.h +++ b/src/Mod/Part/Gui/ViewProviderExt.h @@ -34,6 +34,7 @@ #include #include #include +#include class TopoDS_Shape; class TopoDS_Edge; From c5b3ee7a72db9d8542618ba479e68cb1abc90f5b Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 30 Oct 2021 17:57:52 +0200 Subject: [PATCH 078/138] Part: in ViewProviderSpline use the ViewProviderSplineExtension to avoid code duplication --- src/Mod/Part/Gui/ViewProviderSpline.cpp | 240 +----------------------- src/Mod/Part/Gui/ViewProviderSpline.h | 47 ++--- 2 files changed, 19 insertions(+), 268 deletions(-) diff --git a/src/Mod/Part/Gui/ViewProviderSpline.cpp b/src/Mod/Part/Gui/ViewProviderSpline.cpp index e9d1ce23c4..fa6d67a912 100644 --- a/src/Mod/Part/Gui/ViewProviderSpline.cpp +++ b/src/Mod/Part/Gui/ViewProviderSpline.cpp @@ -63,10 +63,9 @@ namespace bp = boost::placeholders; PROPERTY_SOURCE(PartGui::ViewProviderSpline, PartGui::ViewProviderPartExt) ViewProviderSpline::ViewProviderSpline() - : pcControlPoints(0) { sPixmap = "Part_Spline_Parametric"; - ADD_PROPERTY(ControlPoints,(false)); + extension.initExtension(this); } ViewProviderSpline::~ViewProviderSpline() @@ -78,243 +77,6 @@ QIcon ViewProviderSpline::getIcon(void) const return Gui::BitmapFactory().pixmap(sPixmap); } -void ViewProviderSpline::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) -{ - ViewProviderPartExt::setupContextMenu(menu, receiver, member); - - // toggle command to display components - Gui::ActionFunction* func = new Gui::ActionFunction(menu); - QAction* act = menu->addAction(QObject::tr("Show control points")); - act->setCheckable(true); - act->setChecked(ControlPoints.getValue()); - func->toggle(act, boost::bind(&ViewProviderSpline::toggleControlPoints, this, bp::_1)); -} - -void ViewProviderSpline::toggleControlPoints(bool on) -{ - ControlPoints.setValue(on); -} - -void ViewProviderSpline::updateData(const App::Property* prop) -{ - ViewProviderPartExt::updateData(prop); - if (prop->getTypeId() == Part::PropertyPartShape::getClassTypeId() && strcmp(prop->getName(), "Shape") == 0) { - // update control points if there - if (pcControlPoints) { - Gui::coinRemoveAllChildren(pcControlPoints); - showControlPoints(this->ControlPoints.getValue(), prop); - } - } -} - -void ViewProviderSpline::onChanged(const App::Property* prop) -{ - if (prop == &ControlPoints) { - App::DocumentObject* obj = this->pcObject; - App::Property* shape = obj->getPropertyByName("Shape"); - showControlPoints(ControlPoints.getValue(), shape); - } - else { - ViewProviderPartExt::onChanged(prop); - } -} - -void ViewProviderSpline::showControlPoints(bool show, const App::Property* prop) -{ - if (!pcControlPoints && show) { - pcControlPoints = new SoSwitch(); - pcRoot->addChild(pcControlPoints); - } - - if (pcControlPoints) { - pcControlPoints->whichChild = (show ? SO_SWITCH_ALL : SO_SWITCH_NONE); - } - - if (!show || !pcControlPoints || pcControlPoints->getNumChildren() > 0) - return; - - // ask for the property we are interested in - if (prop && prop->getTypeId() == Part::PropertyPartShape::getClassTypeId()) { - const TopoDS_Shape& shape = static_cast(prop)->getValue(); - if (shape.IsNull()) - return; // empty shape - - for (TopExp_Explorer xp(shape, TopAbs_SHELL); xp.More(); xp.Next()) { - const TopoDS_Shell& shell = TopoDS::Shell(xp.Current()); - for (TopExp_Explorer xp2(shell, TopAbs_FACE); xp2.More(); xp2.Next()) { - const TopoDS_Face& face = TopoDS::Face(xp2.Current()); - showControlPointsOfFace(face); - } - } - for (TopExp_Explorer xp(shape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) { - const TopoDS_Face& face = TopoDS::Face(xp.Current()); - showControlPointsOfFace(face); - } - for (TopExp_Explorer xp(shape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) { - const TopoDS_Wire& wire = TopoDS::Wire(xp.Current()); - for (TopExp_Explorer xp2(wire, TopAbs_EDGE); xp2.More(); xp2.Next()) { - const TopoDS_Edge& edge = TopoDS::Edge(xp2.Current()); - showControlPointsOfEdge(edge); - } - } - for (TopExp_Explorer xp(shape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) { - const TopoDS_Edge& edge = TopoDS::Edge(xp.Current()); - showControlPointsOfEdge(edge); - } - } -} - -void ViewProviderSpline::showControlPointsOfEdge(const TopoDS_Edge& edge) -{ - std::list poles, knots; - Standard_Integer nCt=0; - - TopoDS_Edge edge_loc(edge); - TopLoc_Location aLoc; - edge_loc.Location(aLoc); - - BRepAdaptor_Curve curve(edge_loc); - switch (curve.GetType()) - { - case GeomAbs_BezierCurve: - { - Handle(Geom_BezierCurve) hBezier = curve.Bezier(); - nCt = hBezier->NbPoles(); - for (Standard_Integer i = 1; i <= nCt; i++) - poles.push_back(hBezier->Pole(i)); - if (hBezier->IsClosed()) { - nCt++; - poles.push_back(hBezier->Pole(1)); - } - } break; - case GeomAbs_BSplineCurve: - { - Handle(Geom_BSplineCurve) hBSpline = curve.BSpline(); - nCt = hBSpline->NbPoles(); - for (Standard_Integer i = 1; i <= nCt; i++) - poles.push_back(hBSpline->Pole(i)); - if (hBSpline->IsClosed()) { - nCt++; - poles.push_back(hBSpline->Pole(1)); - } - for (Standard_Integer i = hBSpline->FirstUKnotIndex()+1; i <= hBSpline->LastUKnotIndex()-1; i++) - knots.push_back(hBSpline->Value(hBSpline->Knot(i))); - } break; - default: - break; - } - - if (poles.empty()) - return; // nothing to do - - SoCoordinate3 * controlcoords = new SoCoordinate3; - controlcoords->point.setNum(nCt + knots.size()); - - int index=0; - SbVec3f* verts = controlcoords->point.startEditing(); - for (std::list::iterator p = poles.begin(); p != poles.end(); ++p) { - verts[index++].setValue((float)p->X(), (float)p->Y(), (float)p->Z()); - } - for (std::list::iterator k = knots.begin(); k != knots.end(); ++k) { - verts[index++].setValue((float)k->X(), (float)k->Y(), (float)k->Z()); - } - controlcoords->point.finishEditing(); - - - SoFCControlPoints* controlpoints = new SoFCControlPoints(); - controlpoints->numPolesU = nCt; - controlpoints->numPolesV = 1; - - SoSeparator* nodes = new SoSeparator(); - nodes->addChild(controlcoords); - nodes->addChild(controlpoints); - - pcControlPoints->addChild(nodes); -} - -void ViewProviderSpline::showControlPointsOfFace(const TopoDS_Face& face) -{ - std::list knots; - std::vector > poles; - Standard_Integer nCtU=0, nCtV=0; - - TopoDS_Face face_loc(face); - TopLoc_Location aLoc; - face_loc.Location(aLoc); - - BRepAdaptor_Surface surface(face_loc); - switch (surface.GetType()) - { - case GeomAbs_BezierSurface: - { - Handle(Geom_BezierSurface) hBezier = surface.Bezier(); - nCtU = hBezier->NbUPoles(); - nCtV = hBezier->NbVPoles(); - poles.resize(nCtU); - for (Standard_Integer u = 1; u <= nCtU; u++) { - poles[u-1].resize(nCtV); - for (Standard_Integer v = 1; v <= nCtV; v++) - poles[u-1][v-1] = hBezier->Pole(u, v); - } - } break; - case GeomAbs_BSplineSurface: - { - Handle(Geom_BSplineSurface) hBSpline = surface.BSpline(); - nCtU = hBSpline->NbUPoles(); - nCtV = hBSpline->NbVPoles(); - poles.resize(nCtU); - for (Standard_Integer u = 1; u <= nCtU; u++) { - poles[u-1].resize(nCtV); - for (Standard_Integer v = 1; v <= nCtV; v++) - poles[u-1][v-1] = hBSpline->Pole(u, v); - } - - //Standard_Integer nKnU = hBSpline->NbUKnots(); - //Standard_Integer nKnV = hBSpline->NbVKnots(); - for (Standard_Integer u = 1; u <= hBSpline->NbUKnots(); u++) { - for (Standard_Integer v = 1; v <= hBSpline->NbVKnots(); v++) - knots.push_back(hBSpline->Value(hBSpline->UKnot(u), hBSpline->VKnot(v))); - } - } break; - default: - break; - } - - if (poles.empty()) - return; // nothing to do - - SoCoordinate3 * coords = new SoCoordinate3; - coords->point.setNum(nCtU * nCtV + knots.size()); - - int index=0; - SbVec3f* verts = coords->point.startEditing(); - for (std::vector >::iterator u = poles.begin(); u != poles.end(); ++u) { - for (std::vector::iterator v = u->begin(); v != u->end(); ++v) { - verts[index++].setValue((float)v->X(), (float)v->Y(), (float)v->Z()); - } - } - for (std::list::iterator k = knots.begin(); k != knots.end(); ++k) { - verts[index++].setValue((float)k->X(), (float)k->Y(), (float)k->Z()); - } - coords->point.finishEditing(); - - - SoFCControlPoints* control = new SoFCControlPoints(); - control->numPolesU = nCtU; - control->numPolesV = nCtV; - - //if (knots.size() > 0) { - // control->numKnotsU = nKnU; - // control->numKnotsV = nKnV; - //} - - SoSeparator* nodes = new SoSeparator(); - nodes->addChild(coords); - nodes->addChild(control); - - pcControlPoints->addChild(nodes); -} - // ---------------------------------------------------------------------------- EXTENSION_PROPERTY_SOURCE(PartGui::ViewProviderSplineExtension, Gui::ViewProviderExtension) diff --git a/src/Mod/Part/Gui/ViewProviderSpline.h b/src/Mod/Part/Gui/ViewProviderSpline.h index 6861a2cdfe..2786e9fe01 100644 --- a/src/Mod/Part/Gui/ViewProviderSpline.h +++ b/src/Mod/Part/Gui/ViewProviderSpline.h @@ -23,46 +23,19 @@ #ifndef PARTGUI_VIEWPROVIDERPARTSPLINE_H #define PARTGUI_VIEWPROVIDERPARTSPLINE_H -#include "ViewProviderExt.h" +#include #include namespace PartGui { -class PartGuiExport ViewProviderSpline : public ViewProviderPartExt -{ - PROPERTY_HEADER(PartGui::ViewProviderSpline); - -public: - /// constructor - ViewProviderSpline(); - /// destructor - virtual ~ViewProviderSpline(); - - // Display properties - App::PropertyBool ControlPoints; - - QIcon getIcon(void) const; - void updateData(const App::Property* prop); - void setupContextMenu(QMenu* menu, QObject* receiver, const char* member); - -protected: - void onChanged(const App::Property* prop); - void toggleControlPoints(bool); - void showControlPoints(bool, const App::Property* prop); - void showControlPointsOfEdge(const TopoDS_Edge&); - void showControlPointsOfFace(const TopoDS_Face&); - - SoSwitch *pcControlPoints; -}; - class PartGuiExport ViewProviderSplineExtension : public Gui::ViewProviderExtension { EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(PartGui::ViewProviderSplineExtension); public: /// Constructor - ViewProviderSplineExtension(void); + ViewProviderSplineExtension(); virtual ~ViewProviderSplineExtension() = default; App::PropertyBool ControlPoints; @@ -80,6 +53,22 @@ protected: SoSwitch *pcControlPoints; }; +class PartGuiExport ViewProviderSpline : public ViewProviderPartExt +{ + PROPERTY_HEADER(PartGui::ViewProviderSpline); + +public: + /// constructor + ViewProviderSpline(); + /// destructor + virtual ~ViewProviderSpline(); + + QIcon getIcon() const; + +private: + ViewProviderSplineExtension extension; +}; + typedef Gui::ViewProviderExtensionPythonT ViewProviderSplineExtensionPython; } //namespace PartGui From 580a52f75cc765392a6cd30bcaf5c6b0e6f10db5 Mon Sep 17 00:00:00 2001 From: luz paz Date: Wed, 27 Oct 2021 14:28:25 -0400 Subject: [PATCH 079/138] Draft: Fix superfluous whitespace in translations + misc. typos --- src/Mod/Draft/WorkingPlane.py | 18 +++++++++--------- src/Mod/Draft/draftmake/make_sketch.py | 8 ++++---- src/Mod/Draft/draftobjects/array.py | 4 ++-- src/Mod/Draft/draftobjects/dimension.py | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index 3e130699dc..401cc4e055 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -652,14 +652,14 @@ class Plane: if not geom_is_shape: FreeCAD.Console.PrintError(translate( "draft", - "Object without Part.Shape geometry:'{}'\n".format( - obj.ObjectName))) + "Object without Part.Shape geometry:'{}".format( + obj.ObjectName)) + "\n") return False if geom.isNull(): FreeCAD.Console.PrintError(translate( "draft", - "Object with null Part.Shape geometry:'{}'\n".format( - obj.ObjectName))) + "Object with null Part.Shape geometry:'{}".format( + obj.ObjectName)) + "\n") return False if obj.HasSubObjects: shapes.extend(obj.SubObjects) @@ -672,7 +672,7 @@ class Plane: for n in range(len(shapes)): if not DraftGeomUtils.is_planar(shapes[n]): FreeCAD.Console.PrintError(translate( - "draft","'{}' object is not planar\n".format(names[n]))) + "draft", "'{}' object is not planar".format(names[n])) + "\n") return False if not normal: normal = DraftGeomUtils.get_normal(shapes[n]) @@ -683,8 +683,8 @@ class Plane: for n in range(len(shapes)): if not DraftGeomUtils.are_coplanar(shapes[shape_ref], shapes[n]): FreeCAD.Console.PrintError(translate( - "draft","{} and {} aren't coplanar\n".format( - names[shape_ref],names[n]))) + "draft", "{} and {} aren't coplanar".format( + names[shape_ref],names[n])) + "\n") return False else: # suppose all geometries are straight lines or points @@ -693,7 +693,7 @@ class Plane: poly = Part.makePolygon(points) if not DraftGeomUtils.is_planar(poly): FreeCAD.Console.PrintError(translate( - "draft","All Shapes must be coplanar\n")) + "draft", "All Shapes must be coplanar") + "\n") return False normal = DraftGeomUtils.get_normal(poly) else: @@ -701,7 +701,7 @@ class Plane: if not normal: FreeCAD.Console.PrintError(translate( - "draft","Selected Shapes must define a plane\n")) + "draft", "Selected Shapes must define a plane") + "\n") return False # set center of mass diff --git a/src/Mod/Draft/draftmake/make_sketch.py b/src/Mod/Draft/draftmake/make_sketch.py index 52d6fde7e1..f897918e07 100644 --- a/src/Mod/Draft/draftmake/make_sketch.py +++ b/src/Mod/Draft/draftmake/make_sketch.py @@ -100,13 +100,13 @@ def make_sketch(objects_list, autoconstraints=False, addTo=None, if isinstance(obj,Part.Shape): shape = obj elif not hasattr(obj,'Shape'): - App.Console.PrintError(translate("draft","No shape found\n")) + App.Console.PrintError(translate("draft", "No shape found") + '\n') return None else: shape = obj.Shape if not DraftGeomUtils.is_planar(shape, tol): - App.Console.PrintError(translate("draft","All Shapes must be planar\n")) + App.Console.PrintError(translate("draft", "All Shapes must be planar") + '\n') return None if DraftGeomUtils.get_normal(shape, tol): @@ -121,7 +121,7 @@ def make_sketch(objects_list, autoconstraints=False, addTo=None, if len(shape_norm_yes) >= 1: for shape in shapes_list[1:]: if not DraftGeomUtils.are_coplanar(shapes_list[0], shape, tol): - App.Console.PrintError(translate("draft","All Shapes must be coplanar\n")) + App.Console.PrintError(translate("draft", "All Shapes must be coplanar") + '\n') return None # define sketch normal normal = DraftGeomUtils.get_normal(shapes_list[0], tol) @@ -132,7 +132,7 @@ def make_sketch(objects_list, autoconstraints=False, addTo=None, if len(points) >= 2: poly = Part.makePolygon(points) if not DraftGeomUtils.is_planar(poly, tol): - App.Console.PrintError(translate("draft","All Shapes must be coplanar\n")) + App.Console.PrintError(translate("draft","All Shapes must be coplanar") + '\n') return None normal = DraftGeomUtils.get_normal(poly, tol) if not normal: diff --git a/src/Mod/Draft/draftobjects/array.py b/src/Mod/Draft/draftobjects/array.py index 946d474804..8292c60751 100644 --- a/src/Mod/Draft/draftobjects/array.py +++ b/src/Mod/Draft/draftobjects/array.py @@ -287,7 +287,7 @@ class Array(DraftLink): _tip = QT_TRANSLATE_NOOP("App::Property", "A parameter that determines " "how many symmetry planes " - " the circular array will have.") + "the circular array will have.") obj.addProperty("App::PropertyInteger", "Symmetry", "Circular array", @@ -380,7 +380,7 @@ class Array(DraftLink): obj.setPropertyStatus(pr, "Hidden") def execute(self, obj): - """Execture when the object is created or recomputed.""" + """Execute when the object is created or recomputed.""" if not obj.Base: return diff --git a/src/Mod/Draft/draftobjects/dimension.py b/src/Mod/Draft/draftobjects/dimension.py index a7143a655b..bc4bcbecdb 100644 --- a/src/Mod/Draft/draftobjects/dimension.py +++ b/src/Mod/Draft/draftobjects/dimension.py @@ -163,7 +163,7 @@ class DimensionBase(DraftAnnotation): "There are various possibilities:\n" "- An object, and one of its edges.\n" "- An object, and two of its vertices.\n" - "- An arc object, and its edge.\n") + "- An arc object, and its edge.") obj.addProperty("App::PropertyLinkSubList", "LinkedGeometry", "Dimension", From 4dcc03cc7274c31bafd349c220c19d311fbd55da Mon Sep 17 00:00:00 2001 From: luzpaz Date: Thu, 28 Oct 2021 10:17:31 -0400 Subject: [PATCH 080/138] Add requested revision by @chennes Co-authored-by: Chris Hennes --- src/Mod/Draft/draftmake/make_sketch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/draftmake/make_sketch.py b/src/Mod/Draft/draftmake/make_sketch.py index f897918e07..5a007ffbb1 100644 --- a/src/Mod/Draft/draftmake/make_sketch.py +++ b/src/Mod/Draft/draftmake/make_sketch.py @@ -132,7 +132,7 @@ def make_sketch(objects_list, autoconstraints=False, addTo=None, if len(points) >= 2: poly = Part.makePolygon(points) if not DraftGeomUtils.is_planar(poly, tol): - App.Console.PrintError(translate("draft","All Shapes must be coplanar") + '\n') + App.Console.PrintError(translate("draft", "All Shapes must be coplanar") + '\n') return None normal = DraftGeomUtils.get_normal(poly, tol) if not normal: From f742cd3c209369c9c7f4836f44c9399ce608aa48 Mon Sep 17 00:00:00 2001 From: luz paz Date: Thu, 28 Oct 2021 10:29:18 -0400 Subject: [PATCH 081/138] Refined code formatting for uniformity --- src/Mod/Draft/draftmake/make_sketch.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Mod/Draft/draftmake/make_sketch.py b/src/Mod/Draft/draftmake/make_sketch.py index 5a007ffbb1..b136c944bc 100644 --- a/src/Mod/Draft/draftmake/make_sketch.py +++ b/src/Mod/Draft/draftmake/make_sketch.py @@ -100,13 +100,15 @@ def make_sketch(objects_list, autoconstraints=False, addTo=None, if isinstance(obj,Part.Shape): shape = obj elif not hasattr(obj,'Shape'): - App.Console.PrintError(translate("draft", "No shape found") + '\n') + App.Console.PrintError(translate("draft", + "No shape found")+"\n") return None else: shape = obj.Shape if not DraftGeomUtils.is_planar(shape, tol): - App.Console.PrintError(translate("draft", "All Shapes must be planar") + '\n') + App.Console.PrintError(translate("draft", + "All Shapes must be planar")+"\n") return None if DraftGeomUtils.get_normal(shape, tol): @@ -121,7 +123,8 @@ def make_sketch(objects_list, autoconstraints=False, addTo=None, if len(shape_norm_yes) >= 1: for shape in shapes_list[1:]: if not DraftGeomUtils.are_coplanar(shapes_list[0], shape, tol): - App.Console.PrintError(translate("draft", "All Shapes must be coplanar") + '\n') + App.Console.PrintError(translate("draft", + "All Shapes must be coplanar")+"\n") return None # define sketch normal normal = DraftGeomUtils.get_normal(shapes_list[0], tol) @@ -132,7 +135,8 @@ def make_sketch(objects_list, autoconstraints=False, addTo=None, if len(points) >= 2: poly = Part.makePolygon(points) if not DraftGeomUtils.is_planar(poly, tol): - App.Console.PrintError(translate("draft", "All Shapes must be coplanar") + '\n') + App.Console.PrintError(translate("draft", + "All Shapes must be coplanar")+"\n") return None normal = DraftGeomUtils.get_normal(poly, tol) if not normal: From 30a3ccde46f474003b7a1717d0d27788cdc0b5d0 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Fri, 29 Oct 2021 09:24:27 -0400 Subject: [PATCH 082/138] Add requested revision by @chennes Co-authored-by: Chris Hennes --- src/Mod/Draft/WorkingPlane.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index 401cc4e055..fa209a0059 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -658,7 +658,7 @@ class Plane: if geom.isNull(): FreeCAD.Console.PrintError(translate( "draft", - "Object with null Part.Shape geometry:'{}".format( + "Object with null Part.Shape geometry:'{}'".format( obj.ObjectName)) + "\n") return False if obj.HasSubObjects: From b488071a55a3dcfa24f7f74626801b12e8733088 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Fri, 29 Oct 2021 09:24:49 -0400 Subject: [PATCH 083/138] Add requested revision by @chennes Co-authored-by: Chris Hennes --- src/Mod/Draft/WorkingPlane.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index fa209a0059..b155d74c13 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -652,7 +652,7 @@ class Plane: if not geom_is_shape: FreeCAD.Console.PrintError(translate( "draft", - "Object without Part.Shape geometry:'{}".format( + "Object without Part.Shape geometry:'{}'".format( obj.ObjectName)) + "\n") return False if geom.isNull(): From 9e8516002b9ced0bada5064b2dc775d022adea73 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 30 Oct 2021 22:28:57 +0200 Subject: [PATCH 084/138] Part: fixes #0004774: Datum plane or line is not available in sketch in another body via binder --- src/Mod/Part/App/Tools.cpp | 66 ++++++++++++++++++++++++++++ src/Mod/Part/App/Tools.h | 18 ++++++++ src/Mod/Part/Gui/ViewProviderExt.cpp | 10 ++++- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/Mod/Part/App/Tools.cpp b/src/Mod/Part/App/Tools.cpp index d95e49eaf4..7c6792a2ef 100644 --- a/src/Mod/Part/App/Tools.cpp +++ b/src/Mod/Part/App/Tools.cpp @@ -25,6 +25,11 @@ # include # include # include +# include +# include +# include +# include +# include # include # include # include @@ -590,3 +595,64 @@ void Part::Tools::applyTransformationOnNormals(const TopLoc_Location& loc, std:: } } } + +Handle (Poly_Triangulation) Part::Tools::triangulationOfFace(const TopoDS_Face& face) +{ + TopLoc_Location loc; + Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(face, loc); + if (!mesh.IsNull()) + return mesh; + + // If no triangulation exists then the shape is probably infinite + BRepAdaptor_Surface adapt(face); + double u1 = adapt.FirstUParameter(); + double u2 = adapt.LastUParameter(); + double v1 = adapt.FirstVParameter(); + double v2 = adapt.LastVParameter(); + + // recreate a face with a clear boundary + u1 = std::max(-50.0, u1); + u2 = std::min( 50.0, u2); + v1 = std::max(-50.0, v1); + v2 = std::min( 50.0, v2); + + Handle(Geom_Surface) surface = BRep_Tool::Surface(face); + BRepBuilderAPI_MakeFace mkBuilder(surface, u1, u2, v1, v2 +#if OCC_VERSION_HEX >= 0x060502 + , Precision::Confusion() +#endif + ); + + TopoDS_Shape shape = mkBuilder.Shape(); + shape.Location(loc); + + BRepMesh_IncrementalMesh(shape, 0.1); + return BRep_Tool::Triangulation(TopoDS::Face(shape), loc); +} + +Handle(Poly_Polygon3D) Part::Tools::polygonOfEdge(const TopoDS_Edge& edge, TopLoc_Location& loc) +{ + BRepAdaptor_Curve adapt(edge); + double u = adapt.FirstParameter(); + double v = adapt.LastParameter(); + Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D(edge, loc); + if (!aPoly.IsNull() && !Precision::IsInfinite(u) && !Precision::IsInfinite(v)) + return aPoly; + + // recreate an edge with a clear range + u = std::max(-50.0, u); + v = std::min( 50.0, v); + + double uv; + Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, uv, uv); + + BRepBuilderAPI_MakeEdge mkBuilder(curve, u, v); + TopoDS_Shape shape = mkBuilder.Shape(); + // why do we have to set the inverted location here? + TopLoc_Location inv = loc.Inverted(); + shape.Location(inv); + + BRepMesh_IncrementalMesh(shape, 0.1); + TopLoc_Location tmp; + return BRep_Tool::Polygon3D(TopoDS::Edge(shape), tmp); +} diff --git a/src/Mod/Part/App/Tools.h b/src/Mod/Part/App/Tools.h index 425032a12d..3bb543804e 100644 --- a/src/Mod/Part/App/Tools.h +++ b/src/Mod/Part/App/Tools.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include class gp_Lin; class gp_Pln; @@ -177,6 +179,22 @@ public: * \param normals */ static void applyTransformationOnNormals(const TopLoc_Location& loc, std::vector& normals); + /*! + * \brief triangulationOfInfinite + * Returns the triangulation of the face of the tessellated shape. In case the face has infinite lengths + * the triangulation of a limited parameter range is computed. + * \param edge + * \param loc + */ + static Handle (Poly_Triangulation) triangulationOfFace(const TopoDS_Face& face); + /*! + * \brief polygonOfEdge + * Returns the polygon of the edge of the tessellated shape. In case the edge has infinite length + * the polygon of a limited parameter range is computed. + * \param edge + * \param loc + */ + static Handle(Poly_Polygon3D) polygonOfEdge(const TopoDS_Edge& edge, TopLoc_Location& loc); }; } //namespace Part diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index daf355e909..b5025dee0c 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -985,6 +985,9 @@ void ViewProviderPartExt::updateVisual() TopExp::MapShapes(cShape, TopAbs_FACE, faceMap); for (int i=1; i <= faceMap.Extent(); i++) { Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(TopoDS::Face(faceMap(i)), aLoc); + if (mesh.IsNull()) { + mesh = Part::Tools::triangulationOfFace(TopoDS::Face(faceMap(i))); + } // Note: we must also count empty faces if (!mesh.IsNull()) { numTriangles += mesh->NbTriangles(); @@ -1024,7 +1027,7 @@ void ViewProviderPartExt::updateVisual() // a free edge. int hash = aEdge.HashCode(INT_MAX); if (faceEdges.find(hash) == faceEdges.end()) { - Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D(aEdge, aLoc); + Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); if (!aPoly.IsNull()) { int nbNodesInEdge = aPoly->NbNodes(); numNodes += nbNodesInEdge; @@ -1058,6 +1061,9 @@ void ViewProviderPartExt::updateVisual() const TopoDS_Face &actFace = TopoDS::Face(faceMap(i)); // get the mesh of the shape Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(actFace,aLoc); + if (mesh.IsNull()) { + mesh = Part::Tools::triangulationOfFace(actFace); + } if (mesh.IsNull()) { parts[ii] = 0; continue; @@ -1220,7 +1226,7 @@ void ViewProviderPartExt::updateVisual() // handling of the free edge that are not associated to a face int hash = aEdge.HashCode(INT_MAX); if (faceEdges.find(hash) == faceEdges.end()) { - Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D(aEdge, aLoc); + Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); if (!aPoly.IsNull()) { if (!aLoc.IsIdentity()) { identity = false; From a3a7b5a6a660b55931424598ecc0d1cb183cc402 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 31 Oct 2021 12:33:48 +0100 Subject: [PATCH 085/138] PD: [skip ci] if no active body exists then by default pre-select the first option to create a new body --- src/Mod/PartDesign/Gui/DlgActiveBody.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Mod/PartDesign/Gui/DlgActiveBody.cpp b/src/Mod/PartDesign/Gui/DlgActiveBody.cpp index aa916d6208..9aeb1b6c36 100644 --- a/src/Mod/PartDesign/Gui/DlgActiveBody.cpp +++ b/src/Mod/PartDesign/Gui/DlgActiveBody.cpp @@ -74,6 +74,14 @@ DlgActiveBody::DlgActiveBody(QWidget *parent, App::Document*& doc, // TODO: Any other logic (hover, select effects on view etc.) } + + if (!bodyOfActiveObject) { + // by default select the first item so that the user + // can continue by clicking Ok without further action + QListWidgetItem* first = ui->bodySelect->item(0); + if (first) + first->setSelected(true); + } } void DlgActiveBody::accept() From 9a7bf3abffb7043b706cde699496d61d15ca7a77 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Sun, 31 Oct 2021 23:29:31 +0100 Subject: [PATCH 086/138] Prefer QtWebEngine over QtWebKit --- cMake/FreeCAD_Helpers/SetupQt.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cMake/FreeCAD_Helpers/SetupQt.cmake b/cMake/FreeCAD_Helpers/SetupQt.cmake index 1e5519fe23..230d4198a0 100644 --- a/cMake/FreeCAD_Helpers/SetupQt.cmake +++ b/cMake/FreeCAD_Helpers/SetupQt.cmake @@ -23,9 +23,9 @@ if(BUILD_GUI) elseif(${FREECAD_USE_QTWEBMODULE} MATCHES "Qt WebEngine") find_package(Qt5WebEngineWidgets REQUIRED) else() # Automatic - find_package(Qt5WebKitWidgets QUIET) - if(NOT Qt5WebKitWidgets_FOUND) - find_package(Qt5WebEngineWidgets REQUIRED) + find_package(Qt5WebEngineWidgets QUIET) + if(NOT Qt5WebEngineWidgets_FOUND) + find_package(Qt5WebKitWidgets REQUIRED) endif() endif() endif() From ba2459cddc14f60d459909406789d0471f0030f6 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sun, 31 Oct 2021 23:38:11 -0500 Subject: [PATCH 087/138] [Spreadsheet] Fix missing header context menu --- src/Mod/Spreadsheet/Gui/SheetTableView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp index 634a30e815..e65365c3eb 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp @@ -163,8 +163,8 @@ SheetTableView::SheetTableView(QWidget *parent) auto cellProperties = new QAction(tr("Properties..."), this); addAction(cellProperties); - horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu); - verticalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu); + horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + verticalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); contextMenu = new QMenu(this); From 13ba2242dee53c5c5d8d390703bde78f4a1413be Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 1 Nov 2021 14:47:26 +0100 Subject: [PATCH 088/138] Gui: [skip ci] allow to set edit mode in cmdSetEdit() --- src/Gui/CommandT.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Gui/CommandT.h b/src/Gui/CommandT.h index 64154d8cca..a73095e3fd 100644 --- a/src/Gui/CommandT.h +++ b/src/Gui/CommandT.h @@ -279,11 +279,11 @@ inline void cmdAppObjectShow(const App::DocumentObject* obj) { * in-place editing an object, which may be brought in through linking to an * external group. */ -inline void cmdSetEdit(const App::DocumentObject* obj) { +inline void cmdSetEdit(const App::DocumentObject* obj, int mod = 0) { if (obj && obj->getNameInDocument()) { Gui::Command::doCommand(Gui::Command::Gui, - "Gui.ActiveDocument.setEdit(App.getDocument('%s').getObject('%s'))", - obj->getDocument()->getName(), obj->getNameInDocument()); + "Gui.ActiveDocument.setEdit(App.getDocument('%s').getObject('%s'), %d)", + obj->getDocument()->getName(), obj->getNameInDocument(), mod); } } From 833868bd1394862f6fab079fa74685f09675129a Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 1 Nov 2021 19:32:21 +0100 Subject: [PATCH 089/138] Path: [skip ci] partially fix issue 0004765: [MSVC][Build][Permissive-][std:c++latest] FreeCAD failed to build on MSVC --- src/Mod/Path/App/Area.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 11f11fc63f..d91652788f 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -1230,7 +1230,7 @@ int Area::project(TopoDS_Shape &shape_out, const TopoDS_Shape *work_plane) { FC_TIME_INIT2(t,t1); - Handle_HLRBRep_Algo brep_hlr = NULL; + Handle_HLRBRep_Algo brep_hlr; gp_Dir dir(0,0,1); try { brep_hlr = new HLRBRep_Algo(); From 9def811a3a492849d673fce88b9fd9d3c53b5ae1 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 2 Nov 2021 12:57:54 +0100 Subject: [PATCH 090/138] Gui: [skip ci] expose function to Python to enable/disable context-menu of 3d view --- src/Gui/View3DPy.cpp | 19 +++++++++++++++++++ src/Gui/View3DPy.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index 2ca90e415f..0576eae5da 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -115,6 +115,8 @@ void View3DInventorPy::init_type() add_varargs_method("stopAnimating",&View3DInventorPy::stopAnimating,"stopAnimating()"); add_varargs_method("setAnimationEnabled",&View3DInventorPy::setAnimationEnabled,"setAnimationEnabled()"); add_varargs_method("isAnimationEnabled",&View3DInventorPy::isAnimationEnabled,"isAnimationEnabled()"); + add_varargs_method("setPopupMenuEnabled",&View3DInventorPy::setPopupMenuEnabled,"setPopupMenuEnabled()"); + add_varargs_method("isPopupMenuEnabled",&View3DInventorPy::isPopupMenuEnabled,"isPopupMenuEnabled()"); add_varargs_method("dump",&View3DInventorPy::dump,"dump(filename, [onlyVisible=False])"); add_varargs_method("dumpNode",&View3DInventorPy::dumpNode,"dumpNode(node)"); add_varargs_method("setStereoType",&View3DInventorPy::setStereoType,"setStereoType()"); @@ -953,6 +955,23 @@ Py::Object View3DInventorPy::isAnimationEnabled(const Py::Tuple& args) return Py::Boolean(ok ? true : false); } +Py::Object View3DInventorPy::setPopupMenuEnabled(const Py::Tuple& args) +{ + int ok; + if (!PyArg_ParseTuple(args.ptr(), "i", &ok)) + throw Py::Exception(); + _view->getViewer()->setPopupMenuEnabled(ok!=0); + return Py::None(); +} + +Py::Object View3DInventorPy::isPopupMenuEnabled(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + SbBool ok = _view->getViewer()->isPopupMenuEnabled(); + return Py::Boolean(ok ? true : false); +} + Py::Object View3DInventorPy::saveImage(const Py::Tuple& args) { char *cFileName,*cColor="Current",*cComment="$MIBA"; diff --git a/src/Gui/View3DPy.h b/src/Gui/View3DPy.h index 71e2557bad..d84c073c34 100644 --- a/src/Gui/View3DPy.h +++ b/src/Gui/View3DPy.h @@ -87,6 +87,8 @@ public: Py::Object stopAnimating(const Py::Tuple&); Py::Object setAnimationEnabled(const Py::Tuple&); Py::Object isAnimationEnabled(const Py::Tuple&); + Py::Object setPopupMenuEnabled(const Py::Tuple&); + Py::Object isPopupMenuEnabled(const Py::Tuple&); Py::Object dump(const Py::Tuple&); Py::Object dumpNode(const Py::Tuple&); Py::Object setStereoType(const Py::Tuple&); From 1c93d3ee15eb7a161143a114aab8439b0ebd2bc4 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 2 Nov 2021 14:52:22 +0100 Subject: [PATCH 091/138] Gui: [skip ci] add method NavigationStyle::syncModifierKeys() to reduce code duplication --- src/Gui/BlenderNavigationStyle.cpp | 10 +--------- src/Gui/CADNavigationStyle.cpp | 10 +--------- src/Gui/GestureNavigationStyle.cpp | 5 ++--- src/Gui/InventorNavigationStyle.cpp | 10 +--------- src/Gui/MayaGestureNavigationStyle.cpp | 4 +--- src/Gui/NavigationStyle.cpp | 15 +++++++++++++++ src/Gui/NavigationStyle.h | 1 + src/Gui/OpenCascadeNavigationStyle.cpp | 10 +--------- src/Gui/OpenSCADNavigationStyle.cpp | 10 +--------- src/Gui/RevitNavigationStyle.cpp | 10 +--------- src/Gui/TouchpadNavigationStyle.cpp | 10 +--------- 11 files changed, 26 insertions(+), 69 deletions(-) diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index b5b0cfbc42..7e10aa9e77 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -107,15 +107,7 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // give the nodes in the foreground root the chance to handle events (e.g color bar) if (!viewer->isEditing()) { diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index cd8067abb3..c973e56a3d 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -111,15 +111,7 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // give the nodes in the foreground root the chance to handle events (e.g color bar) if (!viewer->isEditing()) { diff --git a/src/Gui/GestureNavigationStyle.cpp b/src/Gui/GestureNavigationStyle.cpp index 98a1e3e2a7..7d0732ae54 100644 --- a/src/Gui/GestureNavigationStyle.cpp +++ b/src/Gui/GestureNavigationStyle.cpp @@ -918,9 +918,8 @@ SbBool GestureNavigationStyle::processSoEvent(const SoEvent* const ev) //whatever else, we don't track } } - this->ctrldown = ev->wasCtrlDown(); - this->shiftdown = ev->wasShiftDown(); - this->altdown = ev->wasAltDown(); + + syncModifierKeys(ev); smev.modifiers = (this->button1down ? NS::Event::BUTTON1DOWN : 0) | diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index 74062c51b8..2aaad878ff 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -115,15 +115,7 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // give the nodes in the foreground root the chance to handle events (e.g color bar) if (!viewer->isEditing()) { diff --git a/src/Gui/MayaGestureNavigationStyle.cpp b/src/Gui/MayaGestureNavigationStyle.cpp index dbb5815a6c..2a34f062e5 100644 --- a/src/Gui/MayaGestureNavigationStyle.cpp +++ b/src/Gui/MayaGestureNavigationStyle.cpp @@ -190,9 +190,7 @@ SbBool MayaGestureNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - this->ctrldown = ev->wasCtrlDown(); - this->shiftdown = ev->wasShiftDown(); - this->altdown = ev->wasAltDown(); + syncModifierKeys(ev); //before this block, mouse button states in NavigationStyle::buttonXdown reflected those before current event arrived. //track mouse button states if (evIsButton) { diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index 43a4a73045..c923f142ea 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -1372,6 +1372,21 @@ void NavigationStyle::clearLog(void) this->log.historysize = 0; } +void NavigationStyle::syncModifierKeys(const SoEvent * const ev) +{ + // Mismatches in state of the modifier keys happens if the user + // presses or releases them outside the viewer window. + if (this->ctrldown != ev->wasCtrlDown()) { + this->ctrldown = ev->wasCtrlDown(); + } + if (this->shiftdown != ev->wasShiftDown()) { + this->shiftdown = ev->wasShiftDown(); + } + if (this->altdown != ev->wasAltDown()) { + this->altdown = ev->wasAltDown(); + } +} + // The viewer is a state machine, and all changes to the current state // are made through this call. void NavigationStyle::setViewingMode(const ViewerMode newmode) diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 659d8f9e03..959500a102 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -213,6 +213,7 @@ protected: void clearLog(void); void addToLog(const SbVec2s pos, const SbTime time); + void syncModifierKeys(const SoEvent * const ev); protected: struct { // tracking mouse movement in a log diff --git a/src/Gui/OpenCascadeNavigationStyle.cpp b/src/Gui/OpenCascadeNavigationStyle.cpp index b554327b66..6f6fa816d4 100644 --- a/src/Gui/OpenCascadeNavigationStyle.cpp +++ b/src/Gui/OpenCascadeNavigationStyle.cpp @@ -107,15 +107,7 @@ SbBool OpenCascadeNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // give the nodes in the foreground root the chance to handle events (e.g color bar) if (!viewer->isEditing()) { diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp index 0148e46eb3..6106d4eb14 100644 --- a/src/Gui/OpenSCADNavigationStyle.cpp +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -107,15 +107,7 @@ SbBool OpenSCADNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // give the nodes in the foreground root the chance to handle events (e.g color bar) if (!viewer->isEditing()) { diff --git a/src/Gui/RevitNavigationStyle.cpp b/src/Gui/RevitNavigationStyle.cpp index 977d0c01a2..289af98f9b 100644 --- a/src/Gui/RevitNavigationStyle.cpp +++ b/src/Gui/RevitNavigationStyle.cpp @@ -107,15 +107,7 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // give the nodes in the foreground root the chance to handle events (e.g color bar) if (!viewer->isEditing()) { diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index 7b279eefc4..d039c54de9 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -107,15 +107,7 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // give the nodes in the foreground root the chance to handle events (e.g color bar) if (!viewer->isEditing()) { From 0b802eb71742d37ac72e33068e5c5eedc3215556 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 2 Nov 2021 15:32:20 +0100 Subject: [PATCH 092/138] Gui: [skip ci] add method NavigationStyle::handleKeyboardEvent() to achieve consistent handling and reduce code duplication --- src/Gui/BlenderNavigationStyle.cpp | 37 ++----------------- src/Gui/CADNavigationStyle.cpp | 33 ++--------------- src/Gui/InventorNavigationStyle.cpp | 33 ++--------------- src/Gui/NavigationStyle.cpp | 49 ++++++++++++++++++++++++++ src/Gui/NavigationStyle.h | 1 + src/Gui/OpenCascadeNavigationStyle.cpp | 33 ++--------------- src/Gui/OpenSCADNavigationStyle.cpp | 33 ++--------------- src/Gui/RevitNavigationStyle.cpp | 33 ++--------------- src/Gui/TouchpadNavigationStyle.cpp | 41 ++------------------- 9 files changed, 64 insertions(+), 229 deletions(-) diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index 7e10aa9e77..b67e8ee6e5 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -118,41 +118,8 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { - const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::R: - processed = true; - viewer->resetToHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - default: - break; - } + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index c973e56a3d..8b2c690bec 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -122,37 +122,8 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { - const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - default: - break; - } + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index 2aaad878ff..27fdb07399 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -126,37 +126,8 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { - const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - default: - break; - } + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index c923f142ea..dc66d74beb 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -1387,6 +1387,55 @@ void NavigationStyle::syncModifierKeys(const SoEvent * const ev) } } +SbBool NavigationStyle::handleKeyboardEvent(const SoKeyboardEvent * const event, const SbVec2f & posn) +{ + SbBool processed = false; + const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; + switch (event->getKey()) { + case SoKeyboardEvent::LEFT_CONTROL: + case SoKeyboardEvent::RIGHT_CONTROL: + this->ctrldown = press; + break; + case SoKeyboardEvent::LEFT_SHIFT: + case SoKeyboardEvent::RIGHT_SHIFT: + this->shiftdown = press; + break; + case SoKeyboardEvent::LEFT_ALT: + case SoKeyboardEvent::RIGHT_ALT: + this->altdown = press; + break; + case SoKeyboardEvent::H: + processed = true; + viewer->saveHomePosition(); + break; + case SoKeyboardEvent::R: + processed = true; + viewer->resetToHomePosition(); + break; + case SoKeyboardEvent::S: + case SoKeyboardEvent::HOME: + case SoKeyboardEvent::LEFT_ARROW: + case SoKeyboardEvent::UP_ARROW: + case SoKeyboardEvent::RIGHT_ARROW: + case SoKeyboardEvent::DOWN_ARROW: + if (!this->isViewing()) + this->setViewing(true); + break; + case SoKeyboardEvent::PAGE_UP: + processed = true; + doZoom(viewer->getSoRenderManager()->getCamera(), getDelta(), posn); + break; + case SoKeyboardEvent::PAGE_DOWN: + processed = true; + doZoom(viewer->getSoRenderManager()->getCamera(), -getDelta(), posn); + break; + default: + break; + } + + return processed; +} + // The viewer is a state machine, and all changes to the current state // are made through this call. void NavigationStyle::setViewingMode(const ViewerMode newmode) diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 959500a102..5f394c550b 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -214,6 +214,7 @@ protected: void addToLog(const SbVec2s pos, const SbTime time); void syncModifierKeys(const SoEvent * const ev); + SbBool handleKeyboardEvent(const SoKeyboardEvent * const event, const SbVec2f & posn); protected: struct { // tracking mouse movement in a log diff --git a/src/Gui/OpenCascadeNavigationStyle.cpp b/src/Gui/OpenCascadeNavigationStyle.cpp index 6f6fa816d4..376247f83d 100644 --- a/src/Gui/OpenCascadeNavigationStyle.cpp +++ b/src/Gui/OpenCascadeNavigationStyle.cpp @@ -118,37 +118,8 @@ SbBool OpenCascadeNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { - const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - default: - break; - } + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp index 6106d4eb14..4df1eefbe7 100644 --- a/src/Gui/OpenSCADNavigationStyle.cpp +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -118,37 +118,8 @@ SbBool OpenSCADNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { - const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - default: - break; - } + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/RevitNavigationStyle.cpp b/src/Gui/RevitNavigationStyle.cpp index 289af98f9b..df547cd44c 100644 --- a/src/Gui/RevitNavigationStyle.cpp +++ b/src/Gui/RevitNavigationStyle.cpp @@ -118,37 +118,8 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { - const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - default: - break; - } + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index d039c54de9..4ee5b53e07 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -118,45 +118,8 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { - const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - case SoKeyboardEvent::PAGE_UP: - doZoom(viewer->getSoRenderManager()->getCamera(), getDelta(), posn); - processed = true; - break; - case SoKeyboardEvent::PAGE_DOWN: - doZoom(viewer->getSoRenderManager()->getCamera(), -getDelta(), posn); - processed = true; - break; - default: - break; - } + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); } // Mouse Button / Spaceball Button handling From bec3c797a87c561eb183923797033e935d635474 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 2 Nov 2021 15:57:54 +0100 Subject: [PATCH 093/138] Gui: [skip ci] add method NavigationStyle::getNormalizedPosition() to reduce code duplication --- src/Gui/BlenderNavigationStyle.cpp | 8 +++----- src/Gui/CADNavigationStyle.cpp | 8 +++----- src/Gui/InventorNavigationStyle.cpp | 8 +++----- src/Gui/NavigationStyle.cpp | 10 ++++++++++ src/Gui/NavigationStyle.h | 1 + src/Gui/OpenCascadeNavigationStyle.cpp | 8 +++----- src/Gui/OpenSCADNavigationStyle.cpp | 8 +++----- src/Gui/RevitNavigationStyle.cpp | 8 +++----- src/Gui/TouchpadNavigationStyle.cpp | 8 +++----- 9 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index b67e8ee6e5..b7b6fc1759 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -88,13 +88,11 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); - const SbVec2f prevnormalized = this->lastmouseposition; - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; + const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index 8b2c690bec..17a590933e 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -92,13 +92,11 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); - const SbVec2f prevnormalized = this->lastmouseposition; - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; + const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index 27fdb07399..1a46fe8439 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -96,13 +96,11 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); - const SbVec2f prevnormalized = this->lastmouseposition; - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; + const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index dc66d74beb..0a38a210f9 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -1372,6 +1372,16 @@ void NavigationStyle::clearLog(void) this->log.historysize = 0; } +SbVec2f NavigationStyle::getNormalizedPosition(const SoEvent * const ev, + const SbViewportRegion & vpRgn) const +{ + const SbVec2s size(vpRgn.getViewportSizePixels()); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), + (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + return posn; +} + void NavigationStyle::syncModifierKeys(const SoEvent * const ev) { // Mismatches in state of the modifier keys happens if the user diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 5f394c550b..af68928008 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -213,6 +213,7 @@ protected: void clearLog(void); void addToLog(const SbVec2s pos, const SbTime time); + SbVec2f getNormalizedPosition(const SoEvent * const ev, const SbViewportRegion & vpRgn) const; void syncModifierKeys(const SoEvent * const ev); SbBool handleKeyboardEvent(const SoKeyboardEvent * const event, const SbVec2f & posn); diff --git a/src/Gui/OpenCascadeNavigationStyle.cpp b/src/Gui/OpenCascadeNavigationStyle.cpp index 376247f83d..62abcebb9f 100644 --- a/src/Gui/OpenCascadeNavigationStyle.cpp +++ b/src/Gui/OpenCascadeNavigationStyle.cpp @@ -88,13 +88,11 @@ SbBool OpenCascadeNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); - const SbVec2f prevnormalized = this->lastmouseposition; - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; + const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp index 4df1eefbe7..7b0c31682a 100644 --- a/src/Gui/OpenSCADNavigationStyle.cpp +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -88,13 +88,11 @@ SbBool OpenSCADNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); - const SbVec2f prevnormalized = this->lastmouseposition; - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; + const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/RevitNavigationStyle.cpp b/src/Gui/RevitNavigationStyle.cpp index df547cd44c..8510dec8f2 100644 --- a/src/Gui/RevitNavigationStyle.cpp +++ b/src/Gui/RevitNavigationStyle.cpp @@ -88,13 +88,11 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); - const SbVec2f prevnormalized = this->lastmouseposition; - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; + const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index 4ee5b53e07..50d4d69672 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -88,13 +88,11 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); - const SbVec2f prevnormalized = this->lastmouseposition; - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; + const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an From 60467908c8d84cff0d04e36379bdccdcb6f2b8f2 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 2 Nov 2021 16:06:21 +0100 Subject: [PATCH 094/138] Gui: [skip ci] code clean-up --- src/Gui/BlenderNavigationStyle.cpp | 16 +------- src/Gui/CADNavigationStyle.cpp | 51 +------------------------- src/Gui/OpenCascadeNavigationStyle.cpp | 4 +- src/Gui/RevitNavigationStyle.cpp | 17 +-------- src/Gui/TouchpadNavigationStyle.cpp | 3 -- 5 files changed, 4 insertions(+), 87 deletions(-) diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index b7b6fc1759..c933d30dfb 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -136,10 +136,6 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) this->seekToPoint(pos); // implicitly calls interactiveCountInc() processed = true; } - //else if (press && (this->currentmode == NavigationStyle::IDLE)) { - // this->setViewing(true); - // processed = true; - //} else if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; @@ -285,11 +281,6 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) this->lockButton1 = false; processed = true; } - - //if (curmode == NavigationStyle::DRAGGING) { - // if (doSpin()) - // newmode = NavigationStyle::SPINNING; - //} break; case BUTTON1DOWN: case CTRLDOWN|BUTTON1DOWN: @@ -311,9 +302,6 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) } newmode = NavigationStyle::DRAGGING; break; - //case BUTTON1DOWN|BUTTON2DOWN|BUTTON3DOWN: - // newmode = NavigationStyle::ZOOMING; - // break; case CTRLDOWN|SHIFTDOWN|BUTTON2DOWN: case CTRLDOWN|BUTTON3DOWN: newmode = NavigationStyle::ZOOMING; @@ -335,10 +323,8 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) // If not handled in this class, pass on upwards in the inheritance // hierarchy. - if (/*(curmode == NavigationStyle::SELECTION || viewer->isEditing()) && */!processed) + if (!processed) processed = inherited::processSoEvent(ev); - else - return true; return processed; } diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index 17a590933e..a390e0ff3d 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -135,39 +135,11 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) case SoMouseButtonEvent::BUTTON1: this->lockrecenter = true; this->button1down = press; -#if 0 // disable to avoid interferences where this key combination is used, too - if (press && ev->wasShiftDown() && - (this->currentmode != NavigationStyle::SELECTION)) { - this->centerTime = ev->getTime(); - float ratio = vp.getViewportAspectRatio(); - SbViewVolume vv = viewer->getCamera()->getViewVolume(ratio); - this->panningplane = vv.getPlane(viewer->getCamera()->focalDistance.getValue()); - this->lockrecenter = false; - } - else if (!press && ev->wasShiftDown() && - (this->currentmode != NavigationStyle::SELECTION)) { - SbTime tmp = (ev->getTime() - this->centerTime); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // is it just a left click? - if (tmp.getValue() < dci && !this->lockrecenter) { - if (!this->moveToPoint(pos)) { - panToCenter(panningplane, posn); - this->interactiveCountDec(); - } - processed = true; - } - } - else -#endif if (press && (this->currentmode == NavigationStyle::SEEK_WAIT_MODE)) { newmode = NavigationStyle::SEEK_MODE; this->seekToPoint(pos); // implicitly calls interactiveCountInc() processed = true; } - //else if (press && (this->currentmode == NavigationStyle::IDLE)) { - // this->setViewing(true); - // processed = true; - //} else if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; @@ -327,11 +299,6 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) this->lockButton1 = false; processed = true; } - - //if (curmode == NavigationStyle::DRAGGING) { - // if (doSpin()) - // newmode = NavigationStyle::SPINNING; - //} break; case BUTTON1DOWN: // make sure not to change the selection when stopping spinning @@ -364,16 +331,6 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) case CTRLDOWN|SHIFTDOWN|BUTTON2DOWN: newmode = NavigationStyle::ZOOMING; break; - //case CTRLDOWN: - //case CTRLDOWN|BUTTON1DOWN: - //case CTRLDOWN|SHIFTDOWN: - //case CTRLDOWN|SHIFTDOWN|BUTTON1DOWN: - // newmode = NavigationStyle::SELECTION; - // break; - //case BUTTON1DOWN|BUTTON3DOWN: - //case CTRLDOWN|BUTTON3DOWN: - // newmode = NavigationStyle::ZOOMING; - // break; // There are many cases we don't handle that just falls through to // the default case, like SHIFTDOWN, CTRLDOWN, CTRLDOWN|SHIFTDOWN, @@ -385,10 +342,6 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) default: // The default will make a spin stop and otherwise not do // anything. - //if ((curmode != NavigationStyle::SEEK_WAIT_MODE) && - // (curmode != NavigationStyle::SEEK_MODE)) { - // newmode = NavigationStyle::IDLE; - //} break; } @@ -404,10 +357,8 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) // If not handled in this class, pass on upwards in the inheritance // hierarchy. - if (/*(curmode == NavigationStyle::SELECTION || viewer->isEditing()) && */!processed) + if (!processed) processed = inherited::processSoEvent(ev); - else - return true; return processed; } diff --git a/src/Gui/OpenCascadeNavigationStyle.cpp b/src/Gui/OpenCascadeNavigationStyle.cpp index 62abcebb9f..6b42b53003 100644 --- a/src/Gui/OpenCascadeNavigationStyle.cpp +++ b/src/Gui/OpenCascadeNavigationStyle.cpp @@ -303,10 +303,8 @@ SbBool OpenCascadeNavigationStyle::processSoEvent(const SoEvent * const ev) // If not handled in this class, pass on upwards in the inheritance // hierarchy. - if (/*(curmode == NavigationStyle::SELECTION || viewer->isEditing()) && */!processed) + if (!processed) processed = inherited::processSoEvent(ev); - else - return true; return processed; } diff --git a/src/Gui/RevitNavigationStyle.cpp b/src/Gui/RevitNavigationStyle.cpp index 8510dec8f2..0c22aeeffd 100644 --- a/src/Gui/RevitNavigationStyle.cpp +++ b/src/Gui/RevitNavigationStyle.cpp @@ -136,10 +136,6 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) this->seekToPoint(pos); // implicitly calls interactiveCountInc() processed = true; } - //else if (press && (this->currentmode == NavigationStyle::IDLE)) { - // this->setViewing(true); - // processed = true; - //} else if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; @@ -283,11 +279,6 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) this->lockButton1 = false; processed = true; } - - //if (curmode == NavigationStyle::DRAGGING) { - // if (doSpin()) - // newmode = NavigationStyle::SPINNING; - //} break; case BUTTON1DOWN: case CTRLDOWN|BUTTON1DOWN: @@ -309,9 +300,6 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) } newmode = NavigationStyle::DRAGGING; break; - //case BUTTON1DOWN|BUTTON2DOWN|BUTTON3DOWN: - // newmode = NavigationStyle::ZOOMING; - // break; case CTRLDOWN|SHIFTDOWN|BUTTON2DOWN: case CTRLDOWN|BUTTON3DOWN: newmode = NavigationStyle::ZOOMING; @@ -333,10 +321,7 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) // If not handled in this class, pass on upwards in the inheritance // hierarchy. - if (/*(curmode == NavigationStyle::SELECTION || viewer->isEditing()) && */!processed) + if (!processed) processed = inherited::processSoEvent(ev); - else - return true; - return processed; } diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index 50d4d69672..b6fd4b3df4 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -299,8 +299,5 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) // hierarchy. if (!processed) processed = inherited::processSoEvent(ev); - else - return true; - return processed; } From 0d511a6dd227b6d463c533e460657d7bd3380e7b Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Tue, 2 Nov 2021 10:10:28 -0500 Subject: [PATCH 095/138] Spreadsheet: Fix bug in content and alias lineedits The two line edit widgets above the spreadsheet, one for exiting the cell contents and one for editing the alias, were set to use the same editing widget as the individual spreadsheet cells. Once that widget was refactored to handle tab/enter behavior it was no longer the correct widget for those elements. This commit changes them to Gui::ExpressionLineEdit widgets instead, so that the Enter key works correctly for them again. --- src/Mod/Spreadsheet/Gui/Sheet.ui | 8 ++++---- src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Mod/Spreadsheet/Gui/Sheet.ui b/src/Mod/Spreadsheet/Gui/Sheet.ui index 5704291d1e..68aa09998e 100644 --- a/src/Mod/Spreadsheet/Gui/Sheet.ui +++ b/src/Mod/Spreadsheet/Gui/Sheet.ui @@ -27,7 +27,7 @@ - + false @@ -44,7 +44,7 @@ - + false @@ -68,9 +68,9 @@ Spreadsheet.my_alias_name instead of Spreadsheet.B1
SheetTableView.h
- SpreadsheetGui::LineEdit + Gui::ExpressionLineEdit QLineEdit -
SpreadsheetView.h
+
Gui/ExpressionCompleter.h
diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index 3d2056cb3f..df46121a19 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -99,10 +99,8 @@ SheetView::SheetView(Gui::Document *pcDocument, App::DocumentObject *docObj, QWi this, SLOT(rowResized(int, int, int))); connect(delegate, &SpreadsheetDelegate::finishedWithKey, this, &SheetView::editingFinishedWithKey); - connect(ui->cellContent, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmContentChanged(ui->cellContent->text()); }); - connect(ui->cellContent, &LineEdit::returnPressed, this, [this]() {confirmContentChanged(ui->cellContent->text()); }); - connect(ui->cellAlias, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmAliasChanged(ui->cellAlias->text()); }); - connect(ui->cellAlias, &LineEdit::returnPressed, this, [this]() {confirmAliasChanged(ui->cellAlias->text()); }); + connect(ui->cellContent, &ExpressionLineEdit::returnPressed, this, [this]() {confirmContentChanged(ui->cellContent->text()); }); + connect(ui->cellAlias, &ExpressionLineEdit::returnPressed, this, [this]() {confirmAliasChanged(ui->cellAlias->text()); }); connect(ui->cellAlias, &LineEdit::textEdited, this, &SheetView::aliasChanged); columnWidthChangedConnection = sheet->columnWidthChanged.connect(bind(&SheetView::resizeColumn, this, bp::_1, bp::_2)); From 7542673b00997a32692a3b80cbcc4430e5eb7899 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 2 Nov 2021 17:26:35 +0100 Subject: [PATCH 096/138] FEM: command module, fix regression in the regard of translation added with 5b52840ca5fdbb --- src/Mod/Fem/femcommands/commands.py | 432 ++++++++++++++++++++++------ 1 file changed, 341 insertions(+), 91 deletions(-) diff --git a/src/Mod/Fem/femcommands/commands.py b/src/Mod/Fem/femcommands/commands.py index 782fffc61f..051be66cb3 100644 --- a/src/Mod/Fem/femcommands/commands.py +++ b/src/Mod/Fem/femcommands/commands.py @@ -31,17 +31,21 @@ __url__ = "https://www.freecadweb.org" import FreeCAD import FreeCADGui +from FreeCAD import Qt from .manager import CommandManager from femtools.femutils import is_of_type -# Python command definitions +# Python command definitions: # for C++ command definitions see src/Mod/Fem/Command.cpp # TODO, may be even more generic class creation # with type() and identifier instead of class for # the commands which add new document objects. # see https://www.python-course.eu/python3_classes_and_type.php +# Translation: +# some information in the regard of translation can be found in forum post +# https://forum.freecadweb.org/viewtopic.php?f=18&t=62449&p=543845#p543593 class _Analysis(CommandManager): @@ -49,9 +53,12 @@ class _Analysis(CommandManager): def __init__(self): super(_Analysis, self).__init__() - self.menutext = "Analysis container" + self.menutext = Qt.QT_TRANSLATE_NOOP("FEM_Analysis", "Analysis container") self.accel = "S, A" - self.tooltip = "Creates an analysis container with standard solver CalculiX" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_Analysis", + "Creates an analysis container with standard solver CalculiX" + ) self.is_active = "with_document" def Activated(self): @@ -74,8 +81,11 @@ class _ClippingPlaneAdd(CommandManager): def __init__(self): super(_ClippingPlaneAdd, self).__init__() - self.menutext = "Clipping plane on face" - self.tooltip = "Add a clipping plane on a selected face" + self.menutext = Qt.QT_TRANSLATE_NOOP("FEM_ClippingPlaneAdd", "Clipping plane on face") + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ClippingPlaneAdd", + "Add a clipping plane on a selected face" + ) self.is_active = "with_document" def Activated(self): @@ -123,8 +133,14 @@ class _ClippingPlaneRemoveAll(CommandManager): def __init__(self): super(_ClippingPlaneRemoveAll, self).__init__() - self.menutext = "Remove all clipping planes" - self.tooltip = "Remove all clipping planes" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ClippingPlaneRemoveAll", + "Remove all clipping planes" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ClippingPlaneRemoveAll", + "Remove all clipping planes" + ) self.is_active = "with_document" def Activated(self): @@ -143,8 +159,14 @@ class _ConstantVacuumPermittivity(CommandManager): def __init__(self): super(_ConstantVacuumPermittivity, self).__init__() self.pixmap = "fem-solver-analysis-thermomechanical.svg" - self.menutext = "Constant vacuum permittivity" - self.tooltip = "Creates a FEM constant vacuum permittivity to overwrite standard value" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstantVacuumPermittivity", + "Constant vacuum permittivity" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstantVacuumPermittivity", + "Creates a FEM constant vacuum permittivity to overwrite standard value" + ) self.is_active = "with_document" self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" @@ -156,8 +178,14 @@ class _ConstraintBodyHeatSource(CommandManager): def __init__(self): super(_ConstraintBodyHeatSource, self).__init__() self.pixmap = "FEM_ConstraintHeatflux" # the heatflux icon is used - self.menutext = "Constraint body heat source" - self.tooltip = "Creates a FEM constraint body heat source" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintBodyHeatSource", + "Constraint body heat source" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintBodyHeatSource", + "Creates a FEM constraint body heat source" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" @@ -167,8 +195,14 @@ class _ConstraintCentrif(CommandManager): def __init__(self): super(_ConstraintCentrif, self).__init__() - self.menutext = "Constraint centrif" - self.tooltip = "Creates a FEM constraint centrif" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintCentrif", + "Constraint centrif" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintCentrif", + "Creates a FEM constraint centrif" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -178,8 +212,14 @@ class _ConstraintElectrostaticPotential(CommandManager): def __init__(self): super(_ConstraintElectrostaticPotential, self).__init__() - self.menutext = "Constraint electrostatic potential" - self.tooltip = "Creates a FEM constraint electrostatic potential" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintElectrostaticPotential", + "Constraint electrostatic potential" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintElectrostaticPotential", + "Creates a FEM constraint electrostatic potential" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -189,8 +229,14 @@ class _ConstraintFlowVelocity(CommandManager): def __init__(self): super(_ConstraintFlowVelocity, self).__init__() - self.menutext = "Constraint flow velocity" - self.tooltip = "Creates a FEM constraint flow velocity" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintFlowVelocity", + "Constraint flow velocity" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintFlowVelocity", + "Creates a FEM constraint flow velocity" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -200,8 +246,14 @@ class _ConstraintInitialFlowVelocity(CommandManager): def __init__(self): super(_ConstraintInitialFlowVelocity, self).__init__() - self.menutext = "Constraint initial flow velocity" - self.tooltip = "Creates a FEM constraint initial flow velocity" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintInitialFlowVelocity", + "Constraint initial flow velocity" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintInitialFlowVelocity", + "Creates a FEM constraint initial flow velocity" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -211,8 +263,14 @@ class _ConstraintSectionPrint(CommandManager): def __init__(self): super(_ConstraintSectionPrint, self).__init__() - self.menutext = "Constraint sectionprint" - self.tooltip = "Creates a FEM constraint sectionprint" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintSectionPrint", + "Constraint sectionprint" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintSectionPrint", + "Creates a FEM constraint sectionprint" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -222,8 +280,14 @@ class _ConstraintSelfWeight(CommandManager): def __init__(self): super(_ConstraintSelfWeight, self).__init__() - self.menutext = "Constraint self weight" - self.tooltip = "Creates a FEM constraint self weight" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintSelfWeight", + "Constraint self weight" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintSelfWeight", + "Creates a FEM constraint self weight" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" @@ -233,8 +297,14 @@ class _ConstraintTie(CommandManager): def __init__(self): super(_ConstraintTie, self).__init__() - self.menutext = "Constraint tie" - self.tooltip = "Creates a FEM constraint tie" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintTie", + "Constraint tie" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ConstraintTie", + "Creates a FEM constraint tie" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -244,8 +314,14 @@ class _ElementFluid1D(CommandManager): def __init__(self): super(_ElementFluid1D, self).__init__() - self.menutext = "Fluid section for 1D flow" - self.tooltip = "Creates a FEM fluid section for 1D flow" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementFluid1D", + "Fluid section for 1D flow" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementFluid1D", + "Creates a FEM fluid section for 1D flow" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -255,8 +331,14 @@ class _ElementGeometry1D(CommandManager): def __init__(self): super(_ElementGeometry1D, self).__init__() - self.menutext = "Beam cross section" - self.tooltip = "Creates a FEM beam cross section" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementGeometry1D", + "Beam cross section" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementGeometry1D", + "Creates a FEM beam cross section" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -266,8 +348,14 @@ class _ElementGeometry2D(CommandManager): def __init__(self): super(_ElementGeometry2D, self).__init__() - self.menutext = "Shell plate thickness" - self.tooltip = "Creates a FEM shell plate thickness" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementGeometry2D", + "Shell plate thickness" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementGeometry2D", + "Creates a FEM shell plate thickness" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -277,8 +365,14 @@ class _ElementRotation1D(CommandManager): def __init__(self): super(_ElementRotation1D, self).__init__() - self.menutext = "Beam rotation" - self.tooltip = "Creates a FEM beam rotation" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementRotation1D", + "Beam rotation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ElementRotation1D", + "Creates a FEM beam rotation" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" @@ -288,8 +382,14 @@ class _EquationElectrostatic(CommandManager): def __init__(self): super(_EquationElectrostatic, self).__init__() - self.menutext = "Electrostatic equation" - self.tooltip = "Creates a FEM equation for electrostatic" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationElectrostatic", + "Electrostatic equation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationElectrostatic", + "Creates a FEM equation for electrostatic" + ) self.is_active = "with_solver_elmer" self.do_activated = "add_obj_on_gui_selobj_noset_edit" @@ -299,8 +399,14 @@ class _EquationElasticity(CommandManager): def __init__(self): super(_EquationElasticity, self).__init__() - self.menutext = "Elasticity equation" - self.tooltip = "Creates a FEM equation for elasticity" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationElasticity", + "Elasticity equation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationElasticity", + "Creates a FEM equation for elasticity" + ) self.is_active = "with_solver_elmer" self.do_activated = "add_obj_on_gui_selobj_noset_edit" @@ -310,8 +416,14 @@ class _EquationFlow(CommandManager): def __init__(self): super(_EquationFlow, self).__init__() - self.menutext = "Flow equation" - self.tooltip = "Creates a FEM equation for flow" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationFlow", + "Flow equation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationFlow", + "Creates a FEM equation for flow" + ) self.is_active = "with_solver_elmer" self.do_activated = "add_obj_on_gui_selobj_noset_edit" @@ -321,8 +433,14 @@ class _EquationFlux(CommandManager): def __init__(self): super(_EquationFlux, self).__init__() - self.menutext = "Flux equation" - self.tooltip = "Creates a FEM equation for flux" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationFlux", + "Flux equation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationFlux", + "Creates a FEM equation for flux" + ) self.is_active = "with_solver_elmer" self.do_activated = "add_obj_on_gui_selobj_noset_edit" @@ -332,8 +450,14 @@ class _EquationElectricforce(CommandManager): def __init__(self): super(_EquationElectricforce, self).__init__() - self.menutext = "Electricforce equation" - self.tooltip = "Creates a FEM equation for electric forces" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationElectricforce", + "Electricforce equation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationElectricforce", + "Creates a FEM equation for electric forces" + ) self.is_active = "with_solver_elmer" self.do_activated = "add_obj_on_gui_selobj_noset_edit" @@ -343,8 +467,14 @@ class _EquationHeat(CommandManager): def __init__(self): super(_EquationHeat, self).__init__() - self.menutext = "Heat equation" - self.tooltip = "Creates a FEM equation for heat" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationHeat", + "Heat equation" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_EquationHeat", + "Creates a FEM equation for heat" + ) self.is_active = "with_solver_elmer" self.do_activated = "add_obj_on_gui_selobj_noset_edit" @@ -355,8 +485,14 @@ class _Examples(CommandManager): def __init__(self): super(_Examples, self).__init__() self.pixmap = "FemWorkbench" - self.menutext = "Open FEM examples" - self.tooltip = "Open FEM examples" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_Examples", + "Open FEM examples" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_Examples", + "Open FEM examples" + ) self.is_active = "always" def Activated(self): @@ -370,8 +506,14 @@ class _MaterialEditor(CommandManager): def __init__(self): super(_MaterialEditor, self).__init__() self.pixmap = "Arch_Material_Group" - self.menutext = "Material editor" - self.tooltip = "Opens the FreeCAD material editor" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialEditor", + "Material editor" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialEditor", + "Opens the FreeCAD material editor" + ) self.is_active = "always" def Activated(self): @@ -384,8 +526,14 @@ class _MaterialFluid(CommandManager): def __init__(self): super(_MaterialFluid, self).__init__() - self.menutext = "Material for fluid" - self.tooltip = "Creates a FEM material for fluid" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialFluid", + "Material for fluid" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialFluid", + "Creates a FEM material for fluid" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -395,8 +543,14 @@ class _MaterialMechanicalNonlinear(CommandManager): def __init__(self): super(_MaterialMechanicalNonlinear, self).__init__() - self.menutext = "Nonlinear mechanical material" - self.tooltip = "Creates a nonlinear mechanical material" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialMechanicalNonlinear", + "Nonlinear mechanical material" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialMechanicalNonlinear", + "Creates a nonlinear mechanical material" + ) self.is_active = "with_material_solid" def Activated(self): @@ -459,8 +613,14 @@ class _MaterialReinforced(CommandManager): def __init__(self): super(_MaterialReinforced, self).__init__() - self.menutext = "Reinforced material (concrete)" - self.tooltip = "Creates a material for reinforced matrix material such as concrete" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialReinforced", + "Reinforced material (concrete)" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialReinforced", + "Creates a material for reinforced matrix material such as concrete" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -470,9 +630,15 @@ class _MaterialSolid(CommandManager): def __init__(self): super(_MaterialSolid, self).__init__() - self.menutext = "Material for solid" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialSolid", + "Material for solid" + ) self.accel = "M, S" - self.tooltip = "Creates a FEM material for solid" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MaterialSolid", + "Creates a FEM material for solid" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_set_edit" @@ -482,8 +648,14 @@ class _FEMMesh2Mesh(CommandManager): def __init__(self): super(_FEMMesh2Mesh, self).__init__() - self.menutext = "FEM mesh to mesh" - self.tooltip = "Convert the surface of a FEM mesh to a mesh" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_FEMMesh2Mesh", + "FEM mesh to mesh" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_FEMMesh2Mesh", + "Convert the surface of a FEM mesh to a mesh" + ) self.is_active = "with_femmesh_andor_res" def Activated(self): @@ -523,8 +695,14 @@ class _MeshBoundaryLayer(CommandManager): def __init__(self): super(_MeshBoundaryLayer, self).__init__() - self.menutext = "FEM mesh boundary layer" - self.tooltip = "Creates a FEM mesh boundary layer" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshBoundaryLayer", + "FEM mesh boundary layer" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshBoundaryLayer", + "Creates a FEM mesh boundary layer" + ) self.is_active = "with_gmsh_femmesh" self.do_activated = "add_obj_on_gui_selobj_set_edit" @@ -534,8 +712,14 @@ class _MeshClear(CommandManager): def __init__(self): super(_MeshClear, self).__init__() - self.menutext = "Clear FEM mesh" - self.tooltip = "Clear the Mesh of a FEM mesh object" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshClear", + "Clear FEM mesh" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshClear", + "Clear the Mesh of a FEM mesh object" + ) self.is_active = "with_femmesh" def Activated(self): @@ -553,8 +737,14 @@ class _MeshDisplayInfo(CommandManager): def __init__(self): super(_MeshDisplayInfo, self).__init__() - self.menutext = "Display FEM mesh info" - self.tooltip = "Display FEM mesh info" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshDisplayInfo", + "Display FEM mesh info" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshDisplayInfo", + "Display FEM mesh info" + ) self.is_active = "with_femmesh" def Activated(self): @@ -576,8 +766,14 @@ class _MeshGmshFromShape(CommandManager): def __init__(self): super(_MeshGmshFromShape, self).__init__() - self.menutext = "FEM mesh from shape by Gmsh" - self.tooltip = "Create a FEM mesh from a shape by Gmsh mesher" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshGmshFromShape", + "FEM mesh from shape by Gmsh" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshGmshFromShape", + "Create a FEM mesh from a shape by Gmsh mesher" + ) self.is_active = "with_part_feature" def Activated(self): @@ -615,8 +811,14 @@ class _MeshGroup(CommandManager): def __init__(self): super(_MeshGroup, self).__init__() - self.menutext = "FEM mesh group" - self.tooltip = "Creates a FEM mesh group" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshGroup", + "FEM mesh group" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshGroup", + "Creates a FEM mesh group" + ) self.is_active = "with_gmsh_femmesh" self.do_activated = "add_obj_on_gui_selobj_set_edit" @@ -626,8 +828,14 @@ class _MeshNetgenFromShape(CommandManager): def __init__(self): super(_MeshNetgenFromShape, self).__init__() - self.menutext = "FEM mesh from shape by Netgen" - self.tooltip = "Create a FEM mesh from a solid or face shape by Netgen internal mesher" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshNetgenFromShape", + "FEM mesh from shape by Netgen" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshNetgenFromShape", + "Create a FEM mesh from a solid or face shape by Netgen internal mesher" + ) self.is_active = "with_part_feature" def Activated(self): @@ -665,8 +873,14 @@ class _MeshRegion(CommandManager): def __init__(self): super(_MeshRegion, self).__init__() - self.menutext = "FEM mesh region" - self.tooltip = "Creates a FEM mesh region" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshRegion", + "FEM mesh region" + ) + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_MeshRegion", + "Creates a FEM mesh region" + ) self.is_active = "with_gmsh_femmesh" self.do_activated = "add_obj_on_gui_selobj_set_edit" @@ -676,9 +890,15 @@ class _ResultShow(CommandManager): def __init__(self): super(_ResultShow, self).__init__() - self.menutext = "Show result" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ResultShow", + "Show result" + ) self.accel = "R, S" - self.tooltip = "Shows and visualizes selected result data" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ResultShow", + "Shows and visualizes selected result data" + ) self.is_active = "with_selresult" def Activated(self): @@ -690,9 +910,15 @@ class _ResultsPurge(CommandManager): def __init__(self): super(_ResultsPurge, self).__init__() - self.menutext = "Purge results" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_ResultsPurge", + "Purge results" + ) self.accel = "R, P" - self.tooltip = "Purges all results from active analysis" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_ResultsPurge", + "Purges all results from active analysis" + ) self.is_active = "with_results" def Activated(self): @@ -706,9 +932,15 @@ class _SolverCxxtools(CommandManager): def __init__(self): super(_SolverCxxtools, self).__init__() self.pixmap = "FEM_SolverStandard" - self.menutext = "Solver CalculiX Standard" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverCxxtools", + "Solver CalculiX Standard" + ) self.accel = "S, X" - self.tooltip = "Creates a standard FEM solver CalculiX with ccx tools" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverCxxtools", + "Creates a standard FEM solver CalculiX with ccx tools" + ) self.is_active = "with_analysis" def Activated(self): @@ -741,9 +973,15 @@ class _SolverCalculix(CommandManager): def __init__(self): super(_SolverCalculix, self).__init__() self.pixmap = "FEM_SolverStandard" - self.menutext = "Solver CalculiX (new framework)" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverCalculix", + "Solver CalculiX (new framework)" + ) self.accel = "S, C" - self.tooltip = "Creates a FEM solver CalculiX new framework (less result error handling)" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverCalculix", + "Creates a FEM solver CalculiX new framework (less result error handling)" + ) self.is_active = "with_analysis" self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" @@ -754,9 +992,15 @@ class _SolverControl(CommandManager): def __init__(self): super(_SolverControl, self).__init__() - self.menutext = "Solver job control" + self.menutext = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverControl", + "Solver job control" + ) self.accel = "S, T" - self.tooltip = "Changes solver attributes and runs the calculations for the selected solver" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverControl", + "Changes solver attributes and runs the calculations for the selected solver" + ) self.is_active = "with_solver" def Activated(self): @@ -768,9 +1012,12 @@ class _SolverElmer(CommandManager): def __init__(self): super(_SolverElmer, self).__init__() - self.menutext = "Solver Elmer" + self.menutext = Qt.QT_TRANSLATE_NOOP("FEM_SolverElmer", "Solver Elmer") self.accel = "S, E" - self.tooltip = "Creates a FEM solver Elmer" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverElmer", + "Creates a FEM solver Elmer" + ) self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" @@ -781,9 +1028,9 @@ class _SolverMystran(CommandManager): def __init__(self): super(_SolverMystran, self).__init__() self.pixmap = "FEM_SolverStandard" - self.menutext = "Solver Mystran" + self.menutext = Qt.QT_TRANSLATE_NOOP("FEM_SolverMystran", "Solver Mystran") self.accel = "S, M" - self.tooltip = "Creates a FEM solver Mystran" + self.tooltip = Qt.QT_TRANSLATE_NOOP("FEM_SolverMystran", "Creates a FEM solver Mystran") self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" @@ -793,9 +1040,12 @@ class _SolverRun(CommandManager): def __init__(self): super(_SolverRun, self).__init__() - self.menutext = "Run solver calculations" + self.menutext = Qt.QT_TRANSLATE_NOOP("FEM_SolverRun", "Run solver calculations") self.accel = "S, R" - self.tooltip = "Runs the calculations for the selected solver" + self.tooltip = Qt.QT_TRANSLATE_NOOP( + "FEM_SolverRun", + "Runs the calculations for the selected solver" + ) self.is_active = "with_solver" def Activated(self): @@ -810,9 +1060,9 @@ class _SolverZ88(CommandManager): def __init__(self): super(_SolverZ88, self).__init__() - self.menutext = "Solver Z88" + self.menutext = Qt.QT_TRANSLATE_NOOP("FEM_SolverZ88", "Solver Z88") self.accel = "S, Z" - self.tooltip = "Creates a FEM solver Z88" + self.tooltip = Qt.QT_TRANSLATE_NOOP("FEM_SolverZ88", "Creates a FEM solver Z88") self.is_active = "with_analysis" self.do_activated = "add_obj_on_gui_noset_edit" From 5b3bf97f2b9e3d8b12765cb06831266e10d7b072 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 2 Nov 2021 19:06:12 +0100 Subject: [PATCH 097/138] Gui: [skip ci] replace getNormalizedPosition() with normalizePixelPos() --- src/Gui/BlenderNavigationStyle.cpp | 4 ++-- src/Gui/CADNavigationStyle.cpp | 4 ++-- src/Gui/InventorNavigationStyle.cpp | 4 ++-- src/Gui/NavigationStyle.cpp | 10 ---------- src/Gui/NavigationStyle.h | 1 - src/Gui/OpenCascadeNavigationStyle.cpp | 4 ++-- src/Gui/OpenSCADNavigationStyle.cpp | 4 ++-- src/Gui/RevitNavigationStyle.cpp | 4 ++-- src/Gui/TouchpadNavigationStyle.cpp | 4 ++-- 9 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index c933d30dfb..81fc6f5202 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -88,11 +88,11 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; - const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index a390e0ff3d..53d893f0ff 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -92,11 +92,11 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; - const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index 1a46fe8439..4c57393cb4 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -96,11 +96,11 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; - const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index 0a38a210f9..dc66d74beb 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -1372,16 +1372,6 @@ void NavigationStyle::clearLog(void) this->log.historysize = 0; } -SbVec2f NavigationStyle::getNormalizedPosition(const SoEvent * const ev, - const SbViewportRegion & vpRgn) const -{ - const SbVec2s size(vpRgn.getViewportSizePixels()); - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); - return posn; -} - void NavigationStyle::syncModifierKeys(const SoEvent * const ev) { // Mismatches in state of the modifier keys happens if the user diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index af68928008..5f394c550b 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -213,7 +213,6 @@ protected: void clearLog(void); void addToLog(const SbVec2s pos, const SbTime time); - SbVec2f getNormalizedPosition(const SoEvent * const ev, const SbViewportRegion & vpRgn) const; void syncModifierKeys(const SoEvent * const ev); SbBool handleKeyboardEvent(const SoKeyboardEvent * const event, const SbVec2f & posn); diff --git a/src/Gui/OpenCascadeNavigationStyle.cpp b/src/Gui/OpenCascadeNavigationStyle.cpp index 6b42b53003..58501d9d7b 100644 --- a/src/Gui/OpenCascadeNavigationStyle.cpp +++ b/src/Gui/OpenCascadeNavigationStyle.cpp @@ -88,11 +88,11 @@ SbBool OpenCascadeNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; - const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp index 7b0c31682a..1681ce5281 100644 --- a/src/Gui/OpenSCADNavigationStyle.cpp +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -88,11 +88,11 @@ SbBool OpenSCADNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; - const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/RevitNavigationStyle.cpp b/src/Gui/RevitNavigationStyle.cpp index 0c22aeeffd..3408929108 100644 --- a/src/Gui/RevitNavigationStyle.cpp +++ b/src/Gui/RevitNavigationStyle.cpp @@ -88,11 +88,11 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; - const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index b6fd4b3df4..606412fdd8 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -88,11 +88,11 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) const SoType type(ev->getTypeId()); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2f posn = getNormalizedPosition(ev, vp); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); const SbVec2f prevnormalized = this->lastmouseposition; this->lastmouseposition = posn; - const SbVec2s pos(ev->getPosition()); // Set to true if any event processing happened. Note that it is not // necessary to restrict ourselves to only do one "action" for an From 123b5b5da1df51af9b5322692da5a043fcc2ffe0 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Tue, 2 Nov 2021 23:12:31 -0500 Subject: [PATCH 098/138] Spreadsheet: Remove double-set of value on enter --- src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index df46121a19..4103c2fd91 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -312,7 +312,6 @@ void SheetView::editingFinishedWithKey(int key, Qt::KeyboardModifiers modifiers) QModelIndex i = ui->cells->currentIndex(); if (i.isValid()) { - ui->cells->model()->setData(i, QVariant(ui->cellContent->text()), Qt::EditRole); ui->cells->finishEditWithMove(key, modifiers); } } From 56d900c669da2728fab609ed5b4316245c3b7651 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 3 Nov 2021 09:23:43 -0500 Subject: [PATCH 099/138] Spreadsheet: Display new value, or pending, when dirty If recomputes are turned off, the old behavior was that a cell would display its old property value in the SheetView. The new behavior is that we check to see if the value is actually something that gets computed: if so, show "#PENDING". If not, display the new value, but format it specially to indicate that it's been changed and that a recompute is (eventually) needed. --- src/Mod/Spreadsheet/Gui/SheetModel.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Mod/Spreadsheet/Gui/SheetModel.cpp b/src/Mod/Spreadsheet/Gui/SheetModel.cpp index 341466214f..ed12e8ad44 100644 --- a/src/Mod/Spreadsheet/Gui/SheetModel.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetModel.cpp @@ -277,18 +277,34 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const return QVariant::fromValue(f); } - if (!prop) { + auto dirtyCells = sheet->getCells()->getDirty(); + auto dirty = (dirtyCells.find(CellAddress(row,col)) != dirtyCells.end()); + + if (!prop || dirty) { switch (role) { case Qt::ForegroundRole: { - return QColor(0, 0, 255.0); + return QColor(0, 0, 255.0); // TODO: Remove this hardcoded color, replace with preference } case Qt::TextAlignmentRole: { qtAlignment = Qt::AlignHCenter | Qt::AlignVCenter; return QVariant::fromValue(qtAlignment); } case Qt::DisplayRole: - if(cell->getExpression()) - return QVariant(QLatin1String("#PENDING")); + if(cell->getExpression()) { + std::string str; + if (cell->getStringContent(str)) + if (str.size() > 0 && str[0] == '=') + // If this is a real computed value, indicate that a recompute is + // needed before we can display it + return QVariant(QLatin1String("#PENDING")); + else + // If it's just a simple value, display the new value, but still + // format it as a pending value to indicate to the user that + // a recompute is needed + return QVariant(QString::fromUtf8(str.c_str())); + else + return QVariant(); + } else return QVariant(); default: From 9a9267f922f64d8babe5509066564158fcbfcf66 Mon Sep 17 00:00:00 2001 From: luz paz Date: Wed, 3 Nov 2021 11:13:10 -0400 Subject: [PATCH 100/138] Mesh: Convert comments from DE->EN Bringing uniformity to documenting FreeCAD --- src/Mod/Mesh/App/Core/Approximation.h | 2 +- src/Mod/Mesh/App/Core/Elements.cpp | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Mod/Mesh/App/Core/Approximation.h b/src/Mod/Mesh/App/Core/Approximation.h index 99d73be059..524fca7d25 100644 --- a/src/Mod/Mesh/App/Core/Approximation.h +++ b/src/Mod/Mesh/App/Core/Approximation.h @@ -514,7 +514,7 @@ public: dKoeff[ ct ] = pKoef[ ct ]; } /** - * Destruktor. Deletes the ImpicitSurface instance + * Destructor. Deletes the ImpicitSurface instance * of the WildMagic library */ ~FunctionContainer(){ delete pImplSurf; } diff --git a/src/Mod/Mesh/App/Core/Elements.cpp b/src/Mod/Mesh/App/Core/Elements.cpp index bd434698ce..53bc252add 100644 --- a/src/Mod/Mesh/App/Core/Elements.cpp +++ b/src/Mod/Mesh/App/Core/Elements.cpp @@ -180,22 +180,22 @@ MeshFacetArray& MeshFacetArray::operator = (const MeshFacetArray &rclFAry) bool MeshGeomEdge::ContainedByOrIntersectBoundingBox ( const Base::BoundBox3f &rclBB ) const { - // Test, ob alle Eckpunkte der Edge sich auf einer der 6 Seiten der BB befinden + // Test whether all corner points of the Edge are on one of the 6 sides of the BB if ((GetBoundBox() && rclBB) == false) return false; - // Test, ob Edge-BB komplett in BB liegt + // Test whether Edge-BB is completely in BB if (rclBB.IsInBox(GetBoundBox())) return true; - // Test, ob einer der Eckpunkte in BB liegt + // Test whether one of the corner points is in BB for (int i=0;i<2;i++) { if (rclBB.IsInBox(_aclPoints[i])) return true; } - // "echter" Test auf Schnitt + // "real" test for cut if (IntersectBoundingBox(rclBB)) return true; @@ -487,7 +487,7 @@ bool MeshGeomFacet::IsPointOf (const Base::Vector3f &rclPoint, float fDistance) clProjPt.ProjectToPlane(_aclPoints[0], clNorm); - // Kante P0 --> P1 + // Edge P0 --> P1 clEdge = clP1 - clP0; fLP = clProjPt.DistanceToLine(clP0, clEdge); if (fLP > 0.0f) @@ -500,9 +500,9 @@ bool MeshGeomFacet::IsPointOf (const Base::Vector3f &rclPoint, float fDistance) } else return false; - } + } - // Kante P0 --> P2 + // Edge P0 --> P2 clEdge = clP2 - clP0; fLP = clProjPt.DistanceToLine(clP0, clEdge); if (fLP > 0.0f) @@ -515,9 +515,9 @@ bool MeshGeomFacet::IsPointOf (const Base::Vector3f &rclPoint, float fDistance) } else return false; - } + } - // Kante P1 --> P2 + // Edge P1 --> P2 clEdge = clP2 - clP1; fLP = clProjPt.DistanceToLine(clP1, clEdge); if (fLP > 0.0f) @@ -537,7 +537,7 @@ bool MeshGeomFacet::IsPointOf (const Base::Vector3f &rclPoint, float fDistance) bool MeshGeomFacet::IsPointOfFace (const Base::Vector3f& rclP, float fDistance) const { - // effektivere Implementierung als in MeshGeomFacet::IsPointOf + // more effective implementation than in MeshGeomFacet::IsPointOf // Base::Vector3f a(_aclPoints[0].x, _aclPoints[0].y, _aclPoints[0].z); Base::Vector3f b(_aclPoints[1].x, _aclPoints[1].y, _aclPoints[1].z); @@ -907,7 +907,7 @@ bool MeshGeomFacet::Foraminate (const Base::Vector3f &P, const Base::Vector3f &d bool MeshGeomFacet::IntersectPlaneWithLine (const Base::Vector3f &rclPt, const Base::Vector3f &rclDir, Base::Vector3f &rclRes) const { - // berechne den Schnittpunkt Gerade <-> Ebene + // calculate the intersection of the straight line <-> plane if ( fabs(rclDir * GetNormal()) < 1e-3f ) return false; // line and plane are parallel @@ -979,7 +979,7 @@ void MeshGeomFacet::SubSample (float fStep, std::vector &rclPoin Base::Vector3f clVecAC(C - A); Base::Vector3f clVecBC(C - B); - // laengste Achse entspricht AB + // longest axis corresponds to AB float fLenAB = clVecAB.Length(); float fLenAC = clVecAC.Length(); float fLenBC = clVecBC.Length(); From e8016bdb82bbecebdb8c2c51458b389f30a2fe76 Mon Sep 17 00:00:00 2001 From: luz paz Date: Wed, 3 Nov 2021 11:17:05 -0400 Subject: [PATCH 101/138] Mesh: separating translation section in Mesh.qrc --- src/Mod/Mesh/Gui/Resources/Mesh.qrc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mod/Mesh/Gui/Resources/Mesh.qrc b/src/Mod/Mesh/Gui/Resources/Mesh.qrc index 3c358b6c78..5dbfb6d409 100644 --- a/src/Mod/Mesh/Gui/Resources/Mesh.qrc +++ b/src/Mod/Mesh/Gui/Resources/Mesh.qrc @@ -42,6 +42,8 @@ icons/RegularSolids/Mesh_Ellipsoid.svg icons/RegularSolids/Mesh_Sphere.svg icons/RegularSolids/Mesh_Torus.svg + + translations/Mesh_af.qm translations/Mesh_de.qm translations/Mesh_fi.qm From b8ad042986d6b19951413c0a8f7aa992075bc991 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 3 Nov 2021 14:31:11 -0500 Subject: [PATCH 102/138] Spreadsheet: Fix copy of empty but used cell --- src/Mod/Spreadsheet/App/PropertySheet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 286b72890f..265f8d7697 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -351,7 +351,7 @@ void PropertySheet::copyCells(Base::Writer& writer, const std::vector& ra writer.incInd(); do { auto cell = getValue(*range); - if (cell) { + if (cell && cell->isUsed()) { cell->save(writer); } else { From 909b035d0b0d5e91e726e8a774c696ea7bdc9f5f Mon Sep 17 00:00:00 2001 From: Uwe Date: Thu, 4 Nov 2021 00:52:08 +0100 Subject: [PATCH 103/138] Squashed commit of the following: commit b5aa19950f09d9a391b189a578073b9920943294 Author: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Tue Nov 2 14:37:19 2021 +0100 Std: Fix OpenSCAD navigation string Holding down SHIFT is not required when zooming with the MMB. --- src/Gui/OpenSCADNavigationStyle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp index 1681ce5281..75b34dac11 100644 --- a/src/Gui/OpenSCADNavigationStyle.cpp +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -69,7 +69,7 @@ const char* OpenSCADNavigationStyle::mouseButtons(ViewerMode mode) case NavigationStyle::DRAGGING: return QT_TR_NOOP("Press left mouse button and move mouse"); case NavigationStyle::ZOOMING: - return QT_TR_NOOP("Press SHIFT and middle or right mouse button"); + return QT_TR_NOOP("Press middle mouse button or SHIFT and right mouse button"); default: return "No description"; } From bcab6b5a312439358c35d53ecb85110f90fa5156 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 3 Nov 2021 19:16:31 -0500 Subject: [PATCH 104/138] Spreadsheet: Prevent marking a cell dirty when the content didn't change --- src/Mod/Spreadsheet/Gui/SheetModel.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Mod/Spreadsheet/Gui/SheetModel.cpp b/src/Mod/Spreadsheet/Gui/SheetModel.cpp index ed12e8ad44..c289dcc121 100644 --- a/src/Mod/Spreadsheet/Gui/SheetModel.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetModel.cpp @@ -547,6 +547,16 @@ bool SheetModel::setData(const QModelIndex & index, const QVariant & value, int try { QString str = value.toString(); + + // Check to see if this is already the value in the cell, and skip the update if so + auto cell = sheet->getCell(address); + if (cell) { + std::string oldContent; + cell->getStringContent(oldContent); + if (str == QString::fromStdString(oldContent)) + return true; + } + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Edit cell")); // Because of possible complication of recursively escaped // characters, let's take a shortcut and bypass the command From 4346fddc9c2997e8442f0e05451d814105c98e53 Mon Sep 17 00:00:00 2001 From: Uwe Date: Thu, 4 Nov 2021 01:30:19 +0100 Subject: [PATCH 105/138] [skip ci] update links in Windows installer until not all issues are sorted out with the new domain this should not be backported to the 0.19 branch --- src/WindowsInstaller/include/declarations.nsh | 4 ++-- src/WindowsInstaller/include/gui.nsh | 2 +- src/WindowsInstaller/lang/dutch.nsh | 2 +- src/WindowsInstaller/lang/english.nsh | 2 +- src/WindowsInstaller/lang/french.nsh | 2 +- src/WindowsInstaller/lang/galician.nsh | 2 +- src/WindowsInstaller/lang/german.nsh | 2 +- src/WindowsInstaller/lang/hungarian.nsh | 2 +- src/WindowsInstaller/lang/indonesian.nsh | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/WindowsInstaller/include/declarations.nsh b/src/WindowsInstaller/include/declarations.nsh index 03e26d9b6a..c63da6e971 100644 --- a/src/WindowsInstaller/include/declarations.nsh +++ b/src/WindowsInstaller/include/declarations.nsh @@ -26,9 +26,9 @@ Configuration and variables of FreeCAD installer !define APP_DIR_USERDATA ${APP_NAME} #!define APP_DIR_USERDATA "${APP_NAME}${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}" !define APP_INFO "${APP_NAME} - Your Own 3D Parametric Modeler" -!define APP_WEBPAGE "https://freecadweb.org/" +!define APP_WEBPAGE "https://freecad.org/" !define APP_WEBPAGE_INFO "${APP_NAME} Website" -!define APP_WIKI "https://www.freecadweb.org/wiki/Main_Page" +!define APP_WIKI "https://www.freecad.org/wiki/Main_Page" !define APP_WIKI_INFO "${APP_NAME} Wiki" !define APP_COPYRIGHT "${APP_NAME} is Copyright © 2001-${COPYRIGHT_YEAR} by the ${APP_NAME} Team" diff --git a/src/WindowsInstaller/include/gui.nsh b/src/WindowsInstaller/include/gui.nsh index ac0d6b5c80..16a285284f 100644 --- a/src/WindowsInstaller/include/gui.nsh +++ b/src/WindowsInstaller/include/gui.nsh @@ -66,7 +66,7 @@ BrandingText " " !define MUI_FINISHPAGE_SHOWREADME_FUNCTION StartFreeCAD !define MUI_FINISHPAGE_SHOWREADME_TEXT $(FinishPageRun) !define MUI_FINISHPAGE_LINK $(TEXT_FINISH_WEBSITE) -!define MUI_FINISHPAGE_LINK_LOCATION "https://freecadweb.org/" +!define MUI_FINISHPAGE_LINK_LOCATION "https://freecad.org/" #!define MUI_PAGE_CUSTOMFUNCTION_SHOW CheckDesktopShortcut !insertmacro MUI_PAGE_FINISH diff --git a/src/WindowsInstaller/lang/dutch.nsh b/src/WindowsInstaller/lang/dutch.nsh index e3cc15322f..07ddce249d 100644 --- a/src/WindowsInstaller/lang/dutch.nsh +++ b/src/WindowsInstaller/lang/dutch.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Dit installatie programma zal FreeCAD op uw syst #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org for the latest news, support and tips" #${LangFileString} FileTypeTitle "FreeCAD-Document" diff --git a/src/WindowsInstaller/lang/english.nsh b/src/WindowsInstaller/lang/english.nsh index 138c5aada0..fcf401586c 100644 --- a/src/WindowsInstaller/lang/english.nsh +++ b/src/WindowsInstaller/lang/english.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "This wizard will guide you through the installat #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org/ for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org/ for the latest news, support and tips" #${LangFileString} FileTypeTitle "FreeCAD-Document" diff --git a/src/WindowsInstaller/lang/french.nsh b/src/WindowsInstaller/lang/french.nsh index 5b4132444f..c75e5b9ce5 100644 --- a/src/WindowsInstaller/lang/french.nsh +++ b/src/WindowsInstaller/lang/french.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Cet assistant va vous guider tout au long de l'i #${LangFileString} TEXT_CONFIGURE_PYTHON "Compilation des scripts Python..." ${LangFileString} TEXT_FINISH_DESKTOP "Créer un raccourci sur le bureau" -${LangFileString} TEXT_FINISH_WEBSITE "Consulter les dernières nouvelles, trucs et astuces sur le site freecadweb.org" +${LangFileString} TEXT_FINISH_WEBSITE "Consulter les dernières nouvelles, trucs et astuces sur le site freecad.org" #${LangFileString} FileTypeTitle "Document FreeCAD" diff --git a/src/WindowsInstaller/lang/galician.nsh b/src/WindowsInstaller/lang/galician.nsh index 48d684f2d9..f66e93e863 100644 --- a/src/WindowsInstaller/lang/galician.nsh +++ b/src/WindowsInstaller/lang/galician.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Este asistente vai-no guiar na instalación do F #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org for the latest news, support and tips" #${LangFileString} FileTypeTitle "Documento FreeCAD" diff --git a/src/WindowsInstaller/lang/german.nsh b/src/WindowsInstaller/lang/german.nsh index 44e831d58e..dbb04b10df 100644 --- a/src/WindowsInstaller/lang/german.nsh +++ b/src/WindowsInstaller/lang/german.nsh @@ -15,7 +15,7 @@ ${LangFileString} TEXT_WELCOME "Dieser Assistent wird Sie durch die Installation #${LangFileString} TEXT_CONFIGURE_PYTHON "Kompiliere Python Skripte..." ${LangFileString} TEXT_FINISH_DESKTOP "Ein Symbol auf der Arbeitsoberfläche erzeugen" -${LangFileString} TEXT_FINISH_WEBSITE "Besuchen Sie freecadweb.org für aktuelle Neuigkeiten" +${LangFileString} TEXT_FINISH_WEBSITE "Besuchen Sie freecad.org für aktuelle Neuigkeiten" #${LangFileString} FileTypeTitle "FreeCAD-Dokument" diff --git a/src/WindowsInstaller/lang/hungarian.nsh b/src/WindowsInstaller/lang/hungarian.nsh index 5230dc66a4..55e6ac43d5 100644 --- a/src/WindowsInstaller/lang/hungarian.nsh +++ b/src/WindowsInstaller/lang/hungarian.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "A varázsló segítségével tudja telepíteni a #${LangFileString} TEXT_CONFIGURE_PYTHON "Python parancsfájlok fordítása..." ${LangFileString} TEXT_FINISH_DESKTOP "Indítóikon létrehozása Asztalon" -${LangFileString} TEXT_FINISH_WEBSITE "Látogasson el a freecadweb.org oldalra az aktuális hírekért, támogatásért és tippekért" +${LangFileString} TEXT_FINISH_WEBSITE "Látogasson el a freecad.org oldalra az aktuális hírekért, támogatásért és tippekért" #${LangFileString} FileTypeTitle "FreeCAD-dokumentum" diff --git a/src/WindowsInstaller/lang/indonesian.nsh b/src/WindowsInstaller/lang/indonesian.nsh index 1b92dd13b3..09282cc38a 100644 --- a/src/WindowsInstaller/lang/indonesian.nsh +++ b/src/WindowsInstaller/lang/indonesian.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Program ini akan memandu anda dalam melakukan in #${LangFileString} TEXT_CONFIGURE_PYTHON "Proses kompilasi skrip Python ..." ${LangFileString} TEXT_FINISH_DESKTOP "Membuat pintasan ikon di destop" -${LangFileString} TEXT_FINISH_WEBSITE "Kunjungi freecadweb.org untuk berita terbaru serta dukungan" +${LangFileString} TEXT_FINISH_WEBSITE "Kunjungi freecad.org untuk berita terbaru serta dukungan" #${LangFileString} FileTypeTitle "Dokumen-FreeCAD" From f881330e8e2438aa9f1448d1db5ae01f73609f1f Mon Sep 17 00:00:00 2001 From: Uwe Date: Thu, 4 Nov 2021 01:34:16 +0100 Subject: [PATCH 106/138] [skip ci] update links in Windows installer - missing part until not all issues are sorted out with the new domain this should not be backported to the 0.19 branch --- src/WindowsInstaller/lang/arabic.nsh | 2 +- src/WindowsInstaller/lang/basque.nsh | 2 +- src/WindowsInstaller/lang/catalan.nsh | 2 +- src/WindowsInstaller/lang/czech.nsh | 2 +- src/WindowsInstaller/lang/danish.nsh | 2 +- src/WindowsInstaller/lang/italian.nsh | 2 +- src/WindowsInstaller/lang/japanese.nsh | 2 +- src/WindowsInstaller/lang/norwegian.nsh | 2 +- src/WindowsInstaller/lang/polish.nsh | 2 +- src/WindowsInstaller/lang/portuguese.nsh | 2 +- src/WindowsInstaller/lang/portugueseBR.nsh | 2 +- src/WindowsInstaller/lang/romanian.nsh | 2 +- src/WindowsInstaller/lang/russian.nsh | 2 +- src/WindowsInstaller/lang/slovak.nsh | 2 +- src/WindowsInstaller/lang/spanish.nsh | 2 +- src/WindowsInstaller/lang/swedish.nsh | 2 +- src/WindowsInstaller/lang/turkish.nsh | 2 +- src/WindowsInstaller/lang/ukrainian.nsh | 2 +- src/WindowsInstaller/setup/configure.nsh | 4 ++-- 19 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/WindowsInstaller/lang/arabic.nsh b/src/WindowsInstaller/lang/arabic.nsh index a97346e770..f7771f51af 100644 --- a/src/WindowsInstaller/lang/arabic.nsh +++ b/src/WindowsInstaller/lang/arabic.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "هذا المساعد سوف يرشدك خلال #${LangFileString} TEXT_CONFIGURE_PYTHON "بناء سكربتات بايثون..." ${LangFileString} TEXT_FINISH_DESKTOP "إنشاء اختصار سطح المكتب" -${LangFileString} TEXT_FINISH_WEBSITE "زيارة freecadweb.org لمشاهدة آخر الاخبار, الدعم والأفكار" +${LangFileString} TEXT_FINISH_WEBSITE "زيارة freecad.org لمشاهدة آخر الاخبار, الدعم والأفكار" #${LangFileString} FileTypeTitle "مستند - ليك" diff --git a/src/WindowsInstaller/lang/basque.nsh b/src/WindowsInstaller/lang/basque.nsh index 981d11bf20..7f4bfe2c86 100644 --- a/src/WindowsInstaller/lang/basque.nsh +++ b/src/WindowsInstaller/lang/basque.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Morroi honek $(^NameDA) aplikazioaren instalazio #${LangFileString} TEXT_CONFIGURE_PYTHON "Python script-ak konpilatzen..." ${LangFileString} TEXT_FINISH_DESKTOP "Sortu mahaigaineko lasterbidea" -${LangFileString} TEXT_FINISH_WEBSITE "Bisitatu freecadweb.org azken berriak, aholkuak eta laguntza lortzeko" +${LangFileString} TEXT_FINISH_WEBSITE "Bisitatu freecad.org azken berriak, aholkuak eta laguntza lortzeko" #${LangFileString} FileTypeTitle "FreeCAD-dokumentua" diff --git a/src/WindowsInstaller/lang/catalan.nsh b/src/WindowsInstaller/lang/catalan.nsh index 12a77adc31..2c9c5d32dc 100644 --- a/src/WindowsInstaller/lang/catalan.nsh +++ b/src/WindowsInstaller/lang/catalan.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Aquest assistent us guiarà en la instal·lació #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org for the latest news, support and tips" #${LangFileString} FileTypeTitle "Document FreeCAD" diff --git a/src/WindowsInstaller/lang/czech.nsh b/src/WindowsInstaller/lang/czech.nsh index 807cf7c82e..399945e327 100644 --- a/src/WindowsInstaller/lang/czech.nsh +++ b/src/WindowsInstaller/lang/czech.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Tento pomocník vás provede instalací FreeCADu #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org for the latest news, support and tips" #${LangFileString} FileTypeTitle "FreeCAD-dokumentů" diff --git a/src/WindowsInstaller/lang/danish.nsh b/src/WindowsInstaller/lang/danish.nsh index 9c43044e6f..9ef681a349 100644 --- a/src/WindowsInstaller/lang/danish.nsh +++ b/src/WindowsInstaller/lang/danish.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Denne guide vil installere FreeCAD på din compu #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org for the latest news, support and tips" #${LangFileString} FileTypeTitle "FreeCAD-Dokument" diff --git a/src/WindowsInstaller/lang/italian.nsh b/src/WindowsInstaller/lang/italian.nsh index 589ac1e945..4eff2860c0 100644 --- a/src/WindowsInstaller/lang/italian.nsh +++ b/src/WindowsInstaller/lang/italian.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Verrete guidati nell'installazione di $(^NameDA) #${LangFileString} TEXT_CONFIGURE_PYTHON "Compilazione degli script Python in corso..." ${LangFileString} TEXT_FINISH_DESKTOP "Crea icona sul desktop" -${LangFileString} TEXT_FINISH_WEBSITE "Visitate freecadweb.org per ultime novità, aiuto e suggerimenti" +${LangFileString} TEXT_FINISH_WEBSITE "Visitate freecad.org per ultime novità, aiuto e suggerimenti" #${LangFileString} FileTypeTitle "Documento di FreeCAD" diff --git a/src/WindowsInstaller/lang/japanese.nsh b/src/WindowsInstaller/lang/japanese.nsh index b973095539..7320c9cfdc 100644 --- a/src/WindowsInstaller/lang/japanese.nsh +++ b/src/WindowsInstaller/lang/japanese.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "このウィザードが、あなたのFreeCAD #${LangFileString} TEXT_CONFIGURE_PYTHON "Pythonスクリプトをコンパイルしています..." ${LangFileString} TEXT_FINISH_DESKTOP "デスクトップにショートカットを作成する" -${LangFileString} TEXT_FINISH_WEBSITE "freecadweb.orgを開いて最新ニュースやサポート、ヒントなどを入手する" +${LangFileString} TEXT_FINISH_WEBSITE "freecad.orgを開いて最新ニュースやサポート、ヒントなどを入手する" #${LangFileString} FileTypeTitle "FreeCAD文書" diff --git a/src/WindowsInstaller/lang/norwegian.nsh b/src/WindowsInstaller/lang/norwegian.nsh index 6069b9278f..2e3293b4f1 100644 --- a/src/WindowsInstaller/lang/norwegian.nsh +++ b/src/WindowsInstaller/lang/norwegian.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Denne veiviseren installerer FreeCAD på datamas #${LangFileString} TEXT_CONFIGURE_PYTHON "Kompilerer Python script..." ${LangFileString} TEXT_FINISH_DESKTOP "Lager snarveg på skrivebordet" -${LangFileString} TEXT_FINISH_WEBSITE "Besøk freecadweb.org for de seneste nyhetene, hjelp og støtte" +${LangFileString} TEXT_FINISH_WEBSITE "Besøk freecad.org for de seneste nyhetene, hjelp og støtte" #${LangFileString} FileTypeTitle "FreeCAD-dokument" diff --git a/src/WindowsInstaller/lang/polish.nsh b/src/WindowsInstaller/lang/polish.nsh index 861959f5a4..8e9ad43d90 100644 --- a/src/WindowsInstaller/lang/polish.nsh +++ b/src/WindowsInstaller/lang/polish.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Kreator przeprowadzi Ciebie przez proces instala #${LangFileString} TEXT_CONFIGURE_PYTHON "Kompilowanie skryptów Python..." ${LangFileString} TEXT_FINISH_DESKTOP "Utwórz skrót na pulpicie" -${LangFileString} TEXT_FINISH_WEBSITE "Odwiedź freecadweb.org by poznać wiadomości i wskazówki lub skorzystać ze wsparcia" +${LangFileString} TEXT_FINISH_WEBSITE "Odwiedź freecad.org by poznać wiadomości i wskazówki lub skorzystać ze wsparcia" #${LangFileString} FileTypeTitle "Dokument FreeCAD" diff --git a/src/WindowsInstaller/lang/portuguese.nsh b/src/WindowsInstaller/lang/portuguese.nsh index df7aa3fbb5..24757e9b59 100644 --- a/src/WindowsInstaller/lang/portuguese.nsh +++ b/src/WindowsInstaller/lang/portuguese.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Este assistente de instalação irá guiá-lo at #${LangFileString} TEXT_CONFIGURE_PYTHON "Compilando os scripts de Python..." ${LangFileString} TEXT_FINISH_DESKTOP "Criar um atalho no ambiente de trabalho" -${LangFileString} TEXT_FINISH_WEBSITE "Visite freecadweb.org para as últimas notícias, suporte e dicas" +${LangFileString} TEXT_FINISH_WEBSITE "Visite freecad.org para as últimas notícias, suporte e dicas" #${LangFileString} FileTypeTitle "Documento FreeCAD" diff --git a/src/WindowsInstaller/lang/portugueseBR.nsh b/src/WindowsInstaller/lang/portugueseBR.nsh index b2925508e4..2658c70d98 100644 --- a/src/WindowsInstaller/lang/portugueseBR.nsh +++ b/src/WindowsInstaller/lang/portugueseBR.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Este assistente guiará você durante a instala #${LangFileString} TEXT_CONFIGURE_PYTHON "Compilando scripts Python..." ${LangFileString} TEXT_FINISH_DESKTOP "Criar atalho na área de trabalho" -${LangFileString} TEXT_FINISH_WEBSITE "Visite freecadweb.org para ver as últimas novidades do FreeCAD!" +${LangFileString} TEXT_FINISH_WEBSITE "Visite freecad.org para ver as últimas novidades do FreeCAD!" #${LangFileString} FileTypeTitle "Documento-FreeCAD" diff --git a/src/WindowsInstaller/lang/romanian.nsh b/src/WindowsInstaller/lang/romanian.nsh index c95950fe2c..2dadf8a9c5 100644 --- a/src/WindowsInstaller/lang/romanian.nsh +++ b/src/WindowsInstaller/lang/romanian.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Acest asistent vă va ghida în procesul de inst #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org for the latest news, support and tips" #${LangFileString} FileTypeTitle "Document FreeCAD" diff --git a/src/WindowsInstaller/lang/russian.nsh b/src/WindowsInstaller/lang/russian.nsh index 92cc4169fd..879ebce14f 100644 --- a/src/WindowsInstaller/lang/russian.nsh +++ b/src/WindowsInstaller/lang/russian.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Этот мастер проведет вас ч #${LangFileString} TEXT_CONFIGURE_PYTHON "Компиляция скриптов Python..." ${LangFileString} TEXT_FINISH_DESKTOP "Создать ярлык на рабочем столе" -${LangFileString} TEXT_FINISH_WEBSITE "Перейти на freecadweb.org за новостями, поддержкой и советами" +${LangFileString} TEXT_FINISH_WEBSITE "Перейти на freecad.org за новостями, поддержкой и советами" #${LangFileString} FileTypeTitle "FreeCAD-Document" diff --git a/src/WindowsInstaller/lang/slovak.nsh b/src/WindowsInstaller/lang/slovak.nsh index 14e0aef61e..910538a125 100644 --- a/src/WindowsInstaller/lang/slovak.nsh +++ b/src/WindowsInstaller/lang/slovak.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Tento sprievodca Vám pomáha inštalovať FreeC #${LangFileString} TEXT_CONFIGURE_PYTHON "Kompilácia Python skriptov..." ${LangFileString} TEXT_FINISH_DESKTOP "Vytvoriť skratku pre pracovnú plochu" -${LangFileString} TEXT_FINISH_WEBSITE "Navštívte freecadweb.org pre posledné novinky, podporu a tipy" +${LangFileString} TEXT_FINISH_WEBSITE "Navštívte freecad.org pre posledné novinky, podporu a tipy" #${LangFileString} FileTypeTitle "FreeCAD dokument" diff --git a/src/WindowsInstaller/lang/spanish.nsh b/src/WindowsInstaller/lang/spanish.nsh index eb6c5a2079..3540bf480b 100644 --- a/src/WindowsInstaller/lang/spanish.nsh +++ b/src/WindowsInstaller/lang/spanish.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Este programa instalará FreeCAD en su ordenador #${LangFileString} TEXT_CONFIGURE_PYTHON "Compilando guiones Python..." ${LangFileString} TEXT_FINISH_DESKTOP "Crear acceso directo en el escritorio" -${LangFileString} TEXT_FINISH_WEBSITE "Visite freecadweb.org para últimas noticias, ayuda y consejos" +${LangFileString} TEXT_FINISH_WEBSITE "Visite freecad.org para últimas noticias, ayuda y consejos" #${LangFileString} FileTypeTitle "Documento FreeCAD" diff --git a/src/WindowsInstaller/lang/swedish.nsh b/src/WindowsInstaller/lang/swedish.nsh index 22bb333d20..0176d07b3f 100644 --- a/src/WindowsInstaller/lang/swedish.nsh +++ b/src/WindowsInstaller/lang/swedish.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Denna guide tar dig igenom installationen av $(^ #${LangFileString} TEXT_CONFIGURE_PYTHON "Kompilerar Pythonskript..." ${LangFileString} TEXT_FINISH_DESKTOP "Skapa skrivbordsgenväg" -${LangFileString} TEXT_FINISH_WEBSITE "Besök freecadweb.org för de senaste nyheterna, support och tips" +${LangFileString} TEXT_FINISH_WEBSITE "Besök freecad.org för de senaste nyheterna, support och tips" #${LangFileString} FileTypeTitle "FreeCAD-dokument" diff --git a/src/WindowsInstaller/lang/turkish.nsh b/src/WindowsInstaller/lang/turkish.nsh index dd1511abcf..12f4eedab2 100644 --- a/src/WindowsInstaller/lang/turkish.nsh +++ b/src/WindowsInstaller/lang/turkish.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "Bu sihirbaz size FreeCAD programını kuracak.$\ #${LangFileString} TEXT_CONFIGURE_PYTHON "Compiling Python scripts..." ${LangFileString} TEXT_FINISH_DESKTOP "Create desktop shortcut" -${LangFileString} TEXT_FINISH_WEBSITE "Visit freecadweb.org for the latest news, support and tips" +${LangFileString} TEXT_FINISH_WEBSITE "Visit freecad.org for the latest news, support and tips" #${LangFileString} FileTypeTitle "FreeCAD-Document" diff --git a/src/WindowsInstaller/lang/ukrainian.nsh b/src/WindowsInstaller/lang/ukrainian.nsh index d1e4f975f2..fac5191ab4 100644 --- a/src/WindowsInstaller/lang/ukrainian.nsh +++ b/src/WindowsInstaller/lang/ukrainian.nsh @@ -14,7 +14,7 @@ ${LangFileString} TEXT_WELCOME "За допомогою цього майстр #${LangFileString} TEXT_CONFIGURE_PYTHON "Обробка скриптів Python..." ${LangFileString} TEXT_FINISH_DESKTOP "Створити значок на стільниці" -${LangFileString} TEXT_FINISH_WEBSITE "Відвідати freecadweb.org, щоб ознайомитися з новинами, довідковими матеріалами та підказками" +${LangFileString} TEXT_FINISH_WEBSITE "Відвідати freecad.org, щоб ознайомитися з новинами, довідковими матеріалами та підказками" #${LangFileString} FileTypeTitle "Документ FreeCAD" diff --git a/src/WindowsInstaller/setup/configure.nsh b/src/WindowsInstaller/setup/configure.nsh index dae07512f0..d42ef4d6bb 100644 --- a/src/WindowsInstaller/setup/configure.nsh +++ b/src/WindowsInstaller/setup/configure.nsh @@ -45,9 +45,9 @@ Section -InstallData WriteRegStr SHCTX ${APP_UNINST_KEY} "DisplayVersion" "${APP_VERSION}" WriteRegStr SHCTX ${APP_UNINST_KEY} "DisplayIcon" "$INSTDIR\${APP_RUN}" WriteRegStr SHCTX ${APP_UNINST_KEY} "URLUpdateInfo" "${APP_WEBPAGE}" - WriteRegStr SHCTX ${APP_UNINST_KEY} "URLInfoAbout" "https://www.freecadweb.org/" + WriteRegStr SHCTX ${APP_UNINST_KEY} "URLInfoAbout" "https://www.freecad.org/" WriteRegStr SHCTX ${APP_UNINST_KEY} "Publisher" "${APP_NAME} Team" - WriteRegStr SHCTX ${APP_UNINST_KEY} "HelpLink" "https://forum.freecadweb.org/" + WriteRegStr SHCTX ${APP_UNINST_KEY} "HelpLink" "https://forum.freecad.org/" WriteRegDWORD SHCTX ${APP_UNINST_KEY} "NoModify" 0x00000001 WriteRegDWORD SHCTX ${APP_UNINST_KEY} "NoRepair" 0x00000001 WriteRegStr SHCTX ${APP_UNINST_KEY} "StartMenu" "$SMPROGRAMS\$StartmenuFolder" From 13dcfa64f6654cc11760a6c276ff04d8769ac2e1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Thu, 4 Nov 2021 02:04:21 +0100 Subject: [PATCH 107/138] [skpi ci] Win installer: update MSVC version --- src/WindowsInstaller/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WindowsInstaller/README.md b/src/WindowsInstaller/README.md index 542f264de7..777ce80e43 100644 --- a/src/WindowsInstaller/README.md +++ b/src/WindowsInstaller/README.md @@ -20,7 +20,7 @@ To build the installer you can do the following: (You can alternatively get nsProcess from https://nsis.sourceforge.io/NsProcess_plugin) 7. Copy all FreeCAD files to the folder "~\FreeCAD" e.g. "C:\FreeCAD\Installer\FreeCAD" -8. If you use a version of FreeCAD that was compiled using another MSVC version than MSVC 2017, +8. If you use a version of FreeCAD that was compiled using another MSVC version than MSVC 2019, copy its distributable DLLs to the folder FILES_DEPS (see step 3). 9. Right-click on the file FreeCAD-installer.nsi and choose "Compile NSIS script" to compile the installer. From 09da245af974c25b035ba3635bbadc99e18f27d6 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 4 Nov 2021 10:50:09 +0100 Subject: [PATCH 108/138] App: harmonize API of App::Application * make getHomePath() static and return a std::string * make getExecutableName() static and return a std::string --- src/App/Application.cpp | 8 ++++---- src/App/Application.h | 4 ++-- src/App/ApplicationPy.cpp | 2 +- src/App/Document.cpp | 2 +- src/Gui/Application.cpp | 8 ++++---- src/Gui/ApplicationPy.cpp | 6 +++--- src/Gui/Assistant.cpp | 8 ++++---- src/Gui/BitmapFactory.cpp | 6 +++--- src/Gui/Command.cpp | 2 +- src/Gui/DlgActionsImp.cpp | 2 +- src/Gui/DlgMacroExecuteImp.cpp | 6 +++--- src/Gui/DocumentRecovery.cpp | 2 +- src/Gui/DownloadItem.cpp | 2 +- src/Gui/GuiApplication.cpp | 2 +- src/Gui/MainWindow.cpp | 4 ++-- src/Gui/NetworkRetriever.cpp | 6 +++--- src/Gui/SoFCOffscreenRenderer.cpp | 4 ++-- src/Gui/Splashscreen.cpp | 2 +- src/Gui/WidgetFactory.cpp | 2 +- src/Mod/Import/App/AppImportPy.cpp | 2 +- src/Mod/Import/Gui/AppImportGuiPy.cpp | 2 +- src/Mod/Path/Gui/AppPathGuiPy.cpp | 6 +++--- src/Mod/Raytracing/App/AppRaytracingPy.cpp | 2 +- 23 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index d51a6740fc..faac993af3 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1040,14 +1040,14 @@ Application::TransactionSignaller::~TransactionSignaller() { } } -const char* Application::getHomePath(void) const +std::string Application::getHomePath() { - return _mConfig["AppHomePath"].c_str(); + return mConfig["AppHomePath"]; } -const char* Application::getExecutableName(void) const +std::string Application::getExecutableName() { - return _mConfig["ExeName"].c_str(); + return mConfig["ExeName"]; } std::string Application::getTempPath() diff --git a/src/App/Application.h b/src/App/Application.h index 0d38fcfdab..fbd20abc7c 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -395,8 +395,8 @@ public: /** @name Application directories */ //@{ - const char* getHomePath(void) const; - const char* getExecutableName(void) const; + static std::string getHomePath(); + static std::string getExecutableName(); /*! Returns the temporary directory. By default, this is set to the system's temporary directory but can be customized by the user. diff --git a/src/App/ApplicationPy.cpp b/src/App/ApplicationPy.cpp index 0eaa6ac788..de213ff32e 100644 --- a/src/App/ApplicationPy.cpp +++ b/src/App/ApplicationPy.cpp @@ -691,7 +691,7 @@ PyObject* Application::sGetHomePath(PyObject * /*self*/, PyObject *args) if (!PyArg_ParseTuple(args, "")) // convert args: Python->C return NULL; // NULL triggers exception - Py::String homedir(GetApplication().getHomePath(),"utf-8"); + Py::String homedir(Application::getHomePath(),"utf-8"); return Py::new_reference_to(homedir); } diff --git a/src/App/Document.cpp b/src/App/Document.cpp index f6ec4f46ac..0c6fceb43f 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -1685,7 +1685,7 @@ std::string Document::getTransientDirectoryName(const std::string& uuid, const s std::stringstream s; QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(filename.c_str(), filename.size()); - s << App::Application::getTempPath() << GetApplication().getExecutableName() + s << App::Application::getTempPath() << App::Application::getExecutableName() << "_Doc_" << uuid << "_" << hash.result().toHex().left(6).constData() << "_" << QCoreApplication::applicationPid(); diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index e0ce35be61..0dc5ed879a 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1955,13 +1955,13 @@ void Application::runApplication(void) mainApp.setApplicationName(QString::fromUtf8(it->second.c_str())); } else { - mainApp.setApplicationName(QString::fromUtf8(App::GetApplication().getExecutableName())); + mainApp.setApplicationName(QString::fromStdString(App::Application::getExecutableName())); } #ifndef Q_OS_MACX mainApp.setWindowIcon(Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str())); #endif QString plugin; - plugin = QString::fromUtf8(App::GetApplication().getHomePath()); + plugin = QString::fromStdString(App::Application::getHomePath()); plugin += QLatin1String("/plugins"); QCoreApplication::addLibraryPath(plugin); @@ -2127,7 +2127,7 @@ void Application::runApplication(void) // init the Inventor subsystem initOpenInventor(); - QString home = QString::fromUtf8(App::GetApplication().getHomePath()); + QString home = QString::fromStdString(App::Application::getHomePath()); it = cfg.find("WindowTitle"); if (it != cfg.end()) { @@ -2267,7 +2267,7 @@ void Application::runApplication(void) try { std::stringstream s; - s << App::Application::getTempPath() << App::GetApplication().getExecutableName() + s << App::Application::getTempPath() << App::Application::getExecutableName() << "_" << QCoreApplication::applicationPid() << ".lock"; // open a lock file with the PID Base::FileInfo fi(s.str()); diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index cf23554b0d..82a883cb1f 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -1029,7 +1029,7 @@ PyObject* Application::sAddResPath(PyObject * /*self*/, PyObject *args) PyMem_Free(filePath); if (QDir::isRelativePath(path)) { // Home path ends with '/' - QString home = QString::fromUtf8(App::GetApplication().getHomePath()); + QString home = QString::fromStdString(App::Application::getHomePath()); path = home + path; } @@ -1048,7 +1048,7 @@ PyObject* Application::sAddLangPath(PyObject * /*self*/, PyObject *args) PyMem_Free(filePath); if (QDir::isRelativePath(path)) { // Home path ends with '/' - QString home = QString::fromUtf8(App::GetApplication().getHomePath()); + QString home = QString::fromStdString(App::Application::getHomePath()); path = home + path; } @@ -1066,7 +1066,7 @@ PyObject* Application::sAddIconPath(PyObject * /*self*/, PyObject *args) PyMem_Free(filePath); if (QDir::isRelativePath(path)) { // Home path ends with '/' - QString home = QString::fromUtf8(App::GetApplication().getHomePath()); + QString home = QString::fromStdString(App::Application::getHomePath()); path = home + path; } diff --git a/src/Gui/Assistant.cpp b/src/Gui/Assistant.cpp index f903c7a28f..e905fe8dfd 100644 --- a/src/Gui/Assistant.cpp +++ b/src/Gui/Assistant.cpp @@ -85,8 +85,8 @@ bool Assistant::startAssistant() if (proc->state() != QProcess::Running) { #ifdef Q_OS_WIN QString app; - app = QDir::toNativeSeparators(QString::fromUtf8 - (App::GetApplication().getHomePath()) + QLatin1String("bin/")); + app = QDir::toNativeSeparators(QString::fromStdString + (App::Application::getHomePath()) + QLatin1String("bin/")); #elif defined(Q_OS_MAC) QString app = QCoreApplication::applicationDirPath() + QDir::separator(); #else @@ -95,8 +95,8 @@ bool Assistant::startAssistant() app += QLatin1String("assistant"); // get the name of the executable and the doc path - QString exe = QString::fromUtf8(App::GetApplication().getExecutableName()); - QString doc = QString::fromUtf8(App::Application::getHelpDir().c_str()); + QString exe = QString::fromStdString(App::Application::getExecutableName()); + QString doc = QString::fromStdString(App::Application::getHelpDir()); QString qhc = doc + exe.toLower() + QLatin1String(".qhc"); diff --git a/src/Gui/BitmapFactory.cpp b/src/Gui/BitmapFactory.cpp index bfa5317cf1..2e9ffd7156 100644 --- a/src/Gui/BitmapFactory.cpp +++ b/src/Gui/BitmapFactory.cpp @@ -97,15 +97,15 @@ BitmapFactoryInst& BitmapFactoryInst::instance(void) std::map::const_iterator it; it = App::GetApplication().Config().find("ProgramIcons"); if (it != App::GetApplication().Config().end()) { - QString home = QString::fromUtf8(App::GetApplication().getHomePath()); + QString home = QString::fromStdString(App::Application::getHomePath()); QString path = QString::fromUtf8(it->second.c_str()); if (QDir(path).isRelative()) { path = QFileInfo(QDir(home), path).absoluteFilePath(); } _pcSingleton->addPath(path); } - _pcSingleton->addPath(QString::fromLatin1("%1/icons").arg(QString::fromUtf8(App::GetApplication().getHomePath()))); - _pcSingleton->addPath(QString::fromLatin1("%1/icons").arg(QString::fromUtf8(App::GetApplication().Config()["UserAppData"].c_str()))); + _pcSingleton->addPath(QString::fromLatin1("%1/icons").arg(QString::fromStdString(App::Application::getHomePath()))); + _pcSingleton->addPath(QString::fromLatin1("%1/icons").arg(QString::fromStdString(App::Application::getUserAppDataDir()))); _pcSingleton->addPath(QLatin1String(":/icons/")); _pcSingleton->addPath(QLatin1String(":/Icons/")); } diff --git a/src/Gui/Command.cpp b/src/Gui/Command.cpp index cd95d3ae31..3f44c28d87 100644 --- a/src/Gui/Command.cpp +++ b/src/Gui/Command.cpp @@ -1117,7 +1117,7 @@ void MacroCommand::activated(int iMsg) d = QDir(QString::fromUtf8(cMacroPath.c_str())); } else { - QString dirstr = QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro"); + QString dirstr = QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro"); d = QDir(dirstr); } diff --git a/src/Gui/DlgActionsImp.cpp b/src/Gui/DlgActionsImp.cpp index 35aa3b9f0b..f2c85ebb99 100644 --- a/src/Gui/DlgActionsImp.cpp +++ b/src/Gui/DlgActionsImp.cpp @@ -71,7 +71,7 @@ DlgCustomActionsImp::DlgCustomActionsImp( QWidget* parent ) for (unsigned int i=0; iactionMacros->insertItem(0,d[i],QVariant(false)); - QString systemMacroDirStr = QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro"); + QString systemMacroDirStr = QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro"); d = QDir(systemMacroDirStr, QLatin1String("*.FCMacro *.py")); if (d.exists()) { for (unsigned int i=0; isetText(0, dir[i]); } - QString dirstr = QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro"); + QString dirstr = QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro"); dir = QDir(dirstr, QLatin1String("*.FCMacro *.py")); ui->systemMacroListBox->clear(); @@ -268,7 +268,7 @@ void DlgMacroExecuteImp::accept() dir =QDir(this->macroPath); } else { - QString dirstr = QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro"); + QString dirstr = QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro"); dir = QDir(dirstr); } @@ -319,7 +319,7 @@ void DlgMacroExecuteImp::on_editButton_clicked() else { //index == 1 system-wide item = ui->systemMacroListBox->currentItem(); - dir.setPath(QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro")); + dir.setPath(QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro")); } if (!item) diff --git a/src/Gui/DocumentRecovery.cpp b/src/Gui/DocumentRecovery.cpp index 4f2d60006e..924c4e32fb 100644 --- a/src/Gui/DocumentRecovery.cpp +++ b/src/Gui/DocumentRecovery.cpp @@ -666,7 +666,7 @@ void DocumentRecoveryHandler::checkForPreviousCrashes(const std::function locks = tmp.entryInfoList(); for (QList::iterator it = locks.begin(); it != locks.end(); ++it) { QString bn = it->baseName(); diff --git a/src/Gui/DownloadItem.cpp b/src/Gui/DownloadItem.cpp index 43e59a26ba..6e87fbc5e8 100644 --- a/src/Gui/DownloadItem.cpp +++ b/src/Gui/DownloadItem.cpp @@ -273,7 +273,7 @@ void DownloadItem::init() QString DownloadItem::getDownloadDirectory() const { - QString exe = QString::fromLatin1(App::GetApplication().getExecutableName()); + QString exe = QString::fromStdString(App::Application::getExecutableName()); QString path = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString dirPath = QDir(path).filePath(exe); Base::Reference hPath = App::GetApplication().GetUserParameter().GetGroup("BaseApp") diff --git a/src/Gui/GuiApplication.cpp b/src/Gui/GuiApplication.cpp index b254658000..1a9e7e335f 100644 --- a/src/Gui/GuiApplication.cpp +++ b/src/Gui/GuiApplication.cpp @@ -177,7 +177,7 @@ public: , running(false) { timer->setSingleShot(true); - std::string exeName = App::GetApplication().getExecutableName(); + std::string exeName = App::Application::getExecutableName(); serverName = QString::fromStdString(exeName); } diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index 35a335e9c3..b0dfe66630 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -1490,7 +1490,7 @@ QPixmap MainWindow::aboutImage() const if (!about_path.empty() && about_image.isNull()) { QString path = QString::fromUtf8(about_path.c_str()); if (QDir(path).isRelative()) { - QString home = QString::fromUtf8(App::GetApplication().getHomePath()); + QString home = QString::fromStdString(App::Application::getHomePath()); path = QFileInfo(QDir(home), path).absoluteFilePath(); } about_image.load(path); @@ -1517,7 +1517,7 @@ QPixmap MainWindow::splashImage() const if (splash_image.isNull()) { QString path = QString::fromUtf8(splash_path.c_str()); if (QDir(path).isRelative()) { - QString home = QString::fromUtf8(App::GetApplication().getHomePath()); + QString home = QString::fromStdString(App::Application::getHomePath()); path = QFileInfo(QDir(home), path).absoluteFilePath(); } diff --git a/src/Gui/NetworkRetriever.cpp b/src/Gui/NetworkRetriever.cpp index 8ba993e207..38e8528f62 100644 --- a/src/Gui/NetworkRetriever.cpp +++ b/src/Gui/NetworkRetriever.cpp @@ -418,7 +418,7 @@ Action * StdCmdDownloadOnlineHelp::createAction(void) { Action *pcAction; - QString exe = QString::fromLatin1(App::GetApplication().getExecutableName()); + QString exe = QString::fromStdString(App::Application::getExecutableName()); pcAction = new Action(this,getMainWindow()); pcAction->setText(QCoreApplication::translate( this->className(), getMenuText())); @@ -437,7 +437,7 @@ Action * StdCmdDownloadOnlineHelp::createAction(void) void StdCmdDownloadOnlineHelp::languageChange() { if (_pcAction) { - QString exe = QString::fromLatin1(App::GetApplication().getExecutableName()); + QString exe = QString::fromStdString(App::Application::getExecutableName()); _pcAction->setText(QCoreApplication::translate( this->className(), getMenuText())); _pcAction->setToolTip(QCoreApplication::translate( @@ -483,7 +483,7 @@ void StdCmdDownloadOnlineHelp::activated(int iMsg) bool canStart = false; // set output directory - QString path = QString::fromUtf8(App::GetApplication().getHomePath()); + QString path = QString::fromStdString(App::Application::getHomePath()); path += QString::fromLatin1("/doc/"); ParameterGrp::handle hURLGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/OnlineHelp"); path = QString::fromUtf8(hURLGrp->GetASCII( "DownloadLocation", path.toLatin1() ).c_str()); diff --git a/src/Gui/SoFCOffscreenRenderer.cpp b/src/Gui/SoFCOffscreenRenderer.cpp index a54220fac7..01c393fd46 100644 --- a/src/Gui/SoFCOffscreenRenderer.cpp +++ b/src/Gui/SoFCOffscreenRenderer.cpp @@ -166,7 +166,7 @@ void SoFCOffscreenRenderer::writeToImageFile(const char* filename, const char* c img.setText(QLatin1String("Description"), QString::fromUtf8(comment)); img.setText(QLatin1String("Creation Time"), QDateTime::currentDateTime().toString()); img.setText(QLatin1String("Software"), - QString::fromUtf8(App::GetApplication().getExecutableName())); + QString::fromStdString(App::Application::getExecutableName())); } QFile f(QString::fromUtf8(filename)); @@ -296,7 +296,7 @@ std::string SoFCOffscreenRenderer::createMIBA(const SbMatrix& mat) const com << " \n" ; com << " Unknown\n" ; com << " " << QDateTime::currentDateTime().toString().toLatin1().constData() << "\n" ; - com << " " << App::GetApplication().getExecutableName() << " " << major << "." << minor << "\n" ; + com << " " << App::Application::getExecutableName() << " " << major << "." << minor << "\n" ; com << " Unknown\n"; com << " 1.0\n"; com << " \n" ; diff --git a/src/Gui/Splashscreen.cpp b/src/Gui/Splashscreen.cpp index 4b8e5b53fd..c899f92ae9 100644 --- a/src/Gui/Splashscreen.cpp +++ b/src/Gui/Splashscreen.cpp @@ -680,7 +680,7 @@ void AboutDialog::on_copyButton_clicked() QTextStream str(&data); std::map& config = App::Application::Config(); std::map::iterator it; - QString exe = QString::fromLatin1(App::GetApplication().getExecutableName()); + QString exe = QString::fromStdString(App::Application::getExecutableName()); QString major = QString::fromLatin1(config["BuildVersionMajor"].c_str()); QString minor = QString::fromLatin1(config["BuildVersionMinor"].c_str()); diff --git a/src/Gui/WidgetFactory.cpp b/src/Gui/WidgetFactory.cpp index b68456e7a4..241c35f655 100644 --- a/src/Gui/WidgetFactory.cpp +++ b/src/Gui/WidgetFactory.cpp @@ -431,7 +431,7 @@ void PyResource::load(const char* name) // checks whether it's a relative path if (fi.isRelative()) { QString cwd = QDir::currentPath (); - QString home= QDir(QString::fromUtf8(App::GetApplication().getHomePath())).path(); + QString home= QDir(QString::fromStdString(App::Application::getHomePath())).path(); // search in cwd and home path for the file // diff --git a/src/Mod/Import/App/AppImportPy.cpp b/src/Mod/Import/App/AppImportPy.cpp index f76e59bf15..88e7f2be6e 100644 --- a/src/Mod/Import/App/AppImportPy.cpp +++ b/src/Mod/Import/App/AppImportPy.cpp @@ -368,7 +368,7 @@ private: //makeHeader.SetName(new TCollection_HAsciiString((Standard_CString)Utf8Name.c_str())); makeHeader.SetAuthorValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Author", "Author").c_str())); makeHeader.SetOrganizationValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Company").c_str())); - makeHeader.SetOriginatingSystem(new TCollection_HAsciiString(App::GetApplication().getExecutableName())); + makeHeader.SetOriginatingSystem(new TCollection_HAsciiString(App::Application::getExecutableName().c_str())); makeHeader.SetDescriptionValue(1, new TCollection_HAsciiString("FreeCAD Model")); IFSelect_ReturnStatus ret = writer.Write(name8bit.c_str()); if (ret == IFSelect_RetError || ret == IFSelect_RetFail || ret == IFSelect_RetStop) { diff --git a/src/Mod/Import/Gui/AppImportGuiPy.cpp b/src/Mod/Import/Gui/AppImportGuiPy.cpp index 774c40e2f9..8f855a3a32 100644 --- a/src/Mod/Import/Gui/AppImportGuiPy.cpp +++ b/src/Mod/Import/Gui/AppImportGuiPy.cpp @@ -668,7 +668,7 @@ private: //makeHeader.SetName(new TCollection_HAsciiString((Standard_CString)Utf8Name.c_str())); makeHeader.SetAuthorValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Author", "Author").c_str())); makeHeader.SetOrganizationValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Company").c_str())); - makeHeader.SetOriginatingSystem(new TCollection_HAsciiString(App::GetApplication().getExecutableName())); + makeHeader.SetOriginatingSystem(new TCollection_HAsciiString(App::Application::getExecutableName().c_str())); makeHeader.SetDescriptionValue(1, new TCollection_HAsciiString("FreeCAD Model")); IFSelect_ReturnStatus ret = writer.Write(name8bit.c_str()); if (ret == IFSelect_RetError || ret == IFSelect_RetFail || ret == IFSelect_RetStop) { diff --git a/src/Mod/Path/Gui/AppPathGuiPy.cpp b/src/Mod/Path/Gui/AppPathGuiPy.cpp index a442244b41..062da3a069 100644 --- a/src/Mod/Path/Gui/AppPathGuiPy.cpp +++ b/src/Mod/Path/Gui/AppPathGuiPy.cpp @@ -82,7 +82,7 @@ private: wc.restoreCursor(); try { - std::string path = App::GetApplication().getHomePath(); + std::string path = App::Application::getHomePath(); path += "Mod/Path/PathScripts/post/"; QDir dir1(QString::fromUtf8(path.c_str()), QString::fromLatin1("*_pre.py")); std::string cMacroPath = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro") @@ -149,7 +149,7 @@ private: wc.restoreCursor(); try { - std::string path = App::GetApplication().getHomePath(); + std::string path = App::Application::getHomePath(); path += "Mod/Path/PathScripts/post/"; QDir dir1(QString::fromUtf8(path.c_str()), QString::fromLatin1("*_pre.py")); std::string cMacroPath = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro") @@ -225,7 +225,7 @@ private: if (objlist.size() == 0) throw Py::RuntimeError("No object to export"); - std::string path = App::GetApplication().getHomePath(); + std::string path = App::Application::getHomePath(); path += "Mod/Path/PathScripts/post/"; QDir dir1(QString::fromUtf8(path.c_str()), QString::fromLatin1("*_post.py")); std::string cMacroPath = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro") diff --git a/src/Mod/Raytracing/App/AppRaytracingPy.cpp b/src/Mod/Raytracing/App/AppRaytracingPy.cpp index 5b124e91fc..dc6a528415 100644 --- a/src/Mod/Raytracing/App/AppRaytracingPy.cpp +++ b/src/Mod/Raytracing/App/AppRaytracingPy.cpp @@ -222,7 +222,7 @@ private: if (! PyArg_ParseTuple(args.ptr(), "ss",&FileName,&DestDir)) throw Py::Exception(); - std::string resName = App::GetApplication().getHomePath(); + std::string resName = App::Application::getHomePath(); resName += "Mod"; resName += PATHSEP ; resName += "Raytracing"; From eac21354fa4b60de2eb074bfb331705681a86fd3 Mon Sep 17 00:00:00 2001 From: 0penBrain <48731257+0penBrain@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:44:40 +0100 Subject: [PATCH 109/138] [Sketcher] Refactor Validate Sketch task UI to decrease confusion --- .../Sketcher/Gui/TaskSketcherValidation.ui | 113 +++++++++++------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui b/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui index 4cf647b8cf..d451104fd0 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui +++ b/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui @@ -6,30 +6,43 @@ 0 0 - 311 - 453 + 266 + 684 Sketcher validation - - + + + + Reversed external geometry + + + + + + Find + + + + + + + Swap endpoints in constraints + + + + + + + + Missing coincidences - - - - Tolerance: - - - - - - @@ -47,6 +60,13 @@ + + + + Tolerance: + + + @@ -54,17 +74,13 @@ - - - - Highlight open vertexes - - + + - + Invalid constraints @@ -94,7 +110,35 @@
- + + + + Open and non-manifold vertexes + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Highlight troublesome vertexes + + + + + + + Degenerated geometry @@ -117,30 +161,7 @@ - - - - Reversed external geometry - - - - - - Find - - - - - - - Swap endpoints in constraints - - - - - - - + Constraint orientation locking From b22f2c656279e90306ae6dbeecd083a67ff3f7af Mon Sep 17 00:00:00 2001 From: 0penBrain <48731257+0penBrain@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:55:12 +0100 Subject: [PATCH 110/138] [Sketcher] Add tooltips in Validate Sketch task UI --- .../Sketcher/Gui/TaskSketcherValidation.ui | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui b/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui index d451104fd0..bde339ba91 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui +++ b/src/Mod/Sketcher/Gui/TaskSketcherValidation.ui @@ -22,6 +22,9 @@ + + Finds reversed external geometries + Find @@ -29,6 +32,9 @@ + + Fixes found reversed external geometries by swapping their endpoints + Swap endpoints in constraints @@ -39,12 +45,18 @@ + + Fixes found missing coincidences by adding extra coincident constrains + Missing coincidences + + If checked, construction geometries are ignored in the search + Ignore construction geometry @@ -55,6 +67,10 @@ + + Finds and displays missing coincidences found in the sketch +This is done by analyzing the sketch geometries and constraints + Find @@ -75,7 +91,11 @@ - + + + Defines the X/Y tolerance inside which missing coincidences are searched. + + @@ -88,6 +108,9 @@ + + Finds invalid/malformed constrains in the sketch + Find @@ -95,6 +118,9 @@ + + Tries to fix found invalid constraints + Fix @@ -102,6 +128,9 @@ + + Deletes constrains refering to external geometry + Delete constraints to external geom. @@ -130,6 +159,10 @@ + + Highlights open and non-manifold vertexes that could lead to error if sketch is used to generate solids +This is purely based on topological shape of the sketch and not on its geometry/constrain set. + Highlight troublesome vertexes @@ -146,6 +179,9 @@ + + Finds degenerated geometries in the sketch + Find @@ -153,6 +189,9 @@ + + Tries to fix found degenerated geometries + Fix @@ -169,6 +208,9 @@ + + Enables/updates constraint orientation locking + Enable/Update @@ -176,6 +218,9 @@ + + Disables constraint orientation locking + Disable From 8366028e6f5103ed0b3ecc79831ff480e2f81e8c Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 4 Nov 2021 23:21:14 +0100 Subject: [PATCH 111/138] PD: [skip ci] open a transaction when creating a new body with DlgActiveBody --- src/Mod/PartDesign/Gui/DlgActiveBody.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/Gui/DlgActiveBody.cpp b/src/Mod/PartDesign/Gui/DlgActiveBody.cpp index 9aeb1b6c36..fb4841d94f 100644 --- a/src/Mod/PartDesign/Gui/DlgActiveBody.cpp +++ b/src/Mod/PartDesign/Gui/DlgActiveBody.cpp @@ -28,6 +28,7 @@ # include #endif +#include #include #include "DlgActiveBody.h" @@ -92,10 +93,15 @@ void DlgActiveBody::accept() App::DocumentObject* selectedBody = selectedItems[0]->data(Qt::UserRole).value(); - if (selectedBody) + if (selectedBody) { activeBody = makeBodyActive(selectedBody, _doc); - else + } + else { + // A transaction must be created as otherwise the undo/redo is broken + App::GetApplication().setActiveTransaction(QT_TRANSLATE_NOOP("Command", "Add a Body"), true); activeBody = makeBody(_doc); + App::GetApplication().closeActiveTransaction(); + } QDialog::accept(); } From fdcdaebc9b7222c6b498f9ab62bf4decdf3bcc18 Mon Sep 17 00:00:00 2001 From: luz paz Date: Thu, 4 Nov 2021 06:27:24 -0400 Subject: [PATCH 112/138] Fix typos in source comments [skip ci] Found via `codespell -q 3 -L aci,ake,aline,alle,alledges,alocation,als,ang,anid,apoints,ba,beginn,behaviour,bloaded,bottome,byteorder,calculater,cancelled,cancelling,cas,cascade,centimetre,childs,colour,colours,commen,connexion,currenty,dof,doubleclick,dum,eiter,elemente,ende,feld,finde,findf,freez,hist,iff,indicies,initialisation,initialise,initialised,initialises,initialisiert,inout,ist,kilometre,lod,mantatory,methode,metres,millimetre,modell,nd,noe,normale,normaly,nto,numer,oder,ontop,orgin,orginx,orginy,ot,pard,parms,pres,programm,que,recurrance,rougly,seperator,serie,sinc,strack,substraction,te,thist,thru,tread,uint,unter,vertexes,wallthickness,whitespaces -S ./.git,*.po,*.ts,./ChangeLog.txt,./src/3rdParty,./src/Mod/Assembly/App/opendcm,./src/CXX,./src/zipios++,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL,./src/WindowsInstaller,./src/Doc/FreeCAD.uml,./build/doc/SourceDocu` --- src/App/Application.h | 2 +- src/Mod/Fem/femtaskpanels/task_material_common.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App/Application.h b/src/App/Application.h index fbd20abc7c..2e7237ea9e 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -597,7 +597,7 @@ private: std::deque _pendingDocsReopen; std::map > _pendingDocMap; - // To prevent infinite recursion of reloading a partial document due a truely + // To prevent infinite recursion of reloading a partial document due a truly // missing object std::map > _docReloadAttempts; diff --git a/src/Mod/Fem/femtaskpanels/task_material_common.py b/src/Mod/Fem/femtaskpanels/task_material_common.py index 1ce0320928..4ceb7fa941 100644 --- a/src/Mod/Fem/femtaskpanels/task_material_common.py +++ b/src/Mod/Fem/femtaskpanels/task_material_common.py @@ -549,7 +549,7 @@ class _TaskPanel: # for example PoissonRatio value = Units.Quantity(inputfield_text).Value old_value = Units.Quantity(self.material[matProperty]).Value - # value = float(inputfield_text) # this fails on locale with komma + # value = float(inputfield_text) # this fails on locale with comma # https://forum.freecadweb.org/viewtopic.php?f=18&t=56912&p=523313#p523313 if value: if not (1 - variation < float(old_value) / value < 1 + variation): From a2d6c317fd24860f320ffa706ba3334b7bfe4636 Mon Sep 17 00:00:00 2001 From: 0penBrain <48731257+0penBrain@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:06:25 +0100 Subject: [PATCH 113/138] [Examples] Add thumbnail to PDN example --- data/examples/PartDesignExample.FCStd | Bin 36250 -> 40455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/examples/PartDesignExample.FCStd b/data/examples/PartDesignExample.FCStd index 8e5278bc540dcd22c1981df81e67997cef4abac2..58a01160851a7570e92b2467beccd936eb6aaf92 100644 GIT binary patch delta 14708 zcmZ|01y~$Q(>A=gy9IZGLvTWHfl_uN%?b#?F6+cmRk>5!oi-j(Z*A??DUs+AqLKBCBNu23C}JT&g}w>{bt3q*Ze zdAht$pM1uTU6I6t=sO-8&2t9oeEaln+J7J#@6sVBm+TG>j(rwgMjxVfnhX(Kx@dq}-)HA%*(I%TM(o?$MM= zw*e`GMD***isXqqVqh{4ylzXv^1U_7M?R4|$tDCM`>if2u(1`}yQWR%#_jiK8Ride zVs;PuH}+J)EHXDfbvp6|F2k;`KySo<(7SY9?j1@dtK#6N@xKj!4Ro|@L^qo(AAt8C zGnb@QU3Di_YM<0DOefW;;|sS=lJEDM`sKdo_h&>|T@LwAOqs;n9cpeM}x z`p2G|RTqCA)#)Fo08m_QMz^ag^9@aH3@=#4XO&Ps3eGU8TJlwY80FWyc+<%68h-3_ z$mc@f!6_>lY0;OaMG)!*^P zr?KaTbKa{IWzS#tLbS}n1C~wC?|6__2Odxt{a`ETxRrmB$ViWS-xxIFf&@=gv9 z`_C8Co!vqfY5~3i=!SZ;9le>;*&oEtrKz9Zx}CK0a*YSxt`3_?W@@@#@aA1Ww$dr) zG9N#8v1_ENM6!wh;MFri6PQ63_t#6B$yuLZHK4}$(nj2R{;Oi)Mg_r|*z-(d?|4C&lSQyd^9RTLcaAgIz&Sd$)$SgE%w zMiAbk>86_BmAb-lTF*YS#I*VmYn<3CC4O!1Zr;AAI->Ku$gz5#53%yp(yYa!XL$LE z_UiDyj~rkN;h>f{OGqSgGR#y7OKx%Oo*3NcCd&NrW!K|w8l}HL#fnEjQ+1cV%n-Xz z-9{Ry;sACjv7acpm`^3wv9J8hzWXz4C5D$QwOG&)-+AaxcUj7wX~0fcZhl*1qB3r^ zIlR+M6#typeOFM$asPy}Ga=PwFOwj4Md*JG2CoKMJ&Dnu zy$GJg?X1gpJ32+^(X`NaYU$RK-~4o}>zcS0a0hDDMrtdeyE|P7p78*#w?LC_T=Oia z`=GM~)x*XIoJYdbkVQvCvG~Yim3}ihy^unu)66{q)3spCiL@5wGlr)3#BynJuo{9* zfHLuG`!rc;qC7it+cp_CJnD3$C7%df=myq|3=Bm@S)?y(-R0;RNUPjX_v-f-O|%htD*Mj_N64WZC+>6Px^>s7 zcAL!>0o+dn(_{-yjB~?PHm{2FElg1afMdD>y<_)LM18+>7vc&ZzWDMc>4Y7%O``93 z8JPZtG06yGzfRX?Fc%qu-~>EQGz= zhOn1DN2@#NuU`Z)BAnGWIm#fJ^OQP6V?dLrCsJ^kezQwYB9KF^^M_TevQV9On`@Ez zJaqllmi)`--F4Go8XMe#o*us_O-Ua%P6MJ=%RD5j6OJZ>^h zW`^#FXo6B6B0e~CiLwcK`MqLOv;>bBO(kA_;^@$3dr z3vgZ0MK`2#Ti^t#hV&w1)m%0D?J=~e-cf@QLv)K%pZk2jh*zr09o=&27NtjBzU{9n8jd)`*61}`X-!mNb?9|8FBTFcT<~Xr8cficvRpxkI%vNQN zZggvCsO?tsI%Qa$OB zfKgHUHUeU!w}w-4^6erQ5#>i@ifPT|g875R=Ce0Hd6E}ok5k^WT>2tScpH4Fr(v83 z;SSz8l)6Y`qk_lJ4~{e4NkgcN(b)Z#U)dwgvf;65NxKA>s6Fn9;Q?=gHI#_cUBh`o zhaL;*L7<^Y=W0n4)lZUN;18&?PN`M%7)_*a*Swh4_>!zzR8Lni@h7d{G;3!1 zGB>rX%G*xHQo9o9o_ZR(@5yLr3`(3sk|m=Q+AX=WuRcj_!eQsR%>WO6*9vqvurd3= zmS);&QsU>7)xxe@)V+!|1JI{RSCm}R%6f zhB@XrT93#VC6EUFYDpH$L%Wk9bw{!g!%#h1 z3&u(C;e4)Qlg9Vpmcg8NLEr$$gu$cNd_bF)$lSR2sqh5AQyhaXbiLr-e zXiQ&NYNUHAY+IBi&WTlFl8#rJEVb;^SmkE)3O(-)=oOG9>M2F`LGy$br-TnMWea30 zMa?uEb%bvNL+baDAkQ9?Vu{1R8+@#EAPG-4>L&f3sazP%N7u3hE4>*;qZ_(<^#B`D z^iJ`TmWe<(p@wdsDg{6Df&Hy!Mz)j~1Mh9-;rY$#$Xb?hX6^TG-esmW?E4T`GP2F7 zufb!0btfRyY)*F7zf2An9nU{V{iV367(yKp*NayRUu8_>;J8&2aSA>s&@iXk!0q;A z8O2N?>2=*3#Hl)eOx`b2$HEXD5C^-cT(j1DJN-p^HvQ@NO1t6F&g@BfNL(!@579a*e19?vL;rvrLxK zB^8+n-RzK}*m46YVW|w9rnAts<&sosJ=i$#i@cV&9GVO%M@C6mAFxRiY|wYz{l0vt z5jQKm2!va53MjlqK@;AgWWA$ArTszzCY~Qh$amff`e1+t8mB2G-)>>XZ3A;7dw?VR zCW?T|&zNw|j{#C}U)~Z%VwsfLB2>sFOnib#*GXabpLWHz&@Du3DBHhu+J!rC2|rvU zaus(#?%Z8@hKcK^i#oCL(LWebsd?}197E(-g$dETm_ku(gEC71rRVGbZ+VP`cnQbV znxl=FB2ikijyXRy_YqaEqRnwHQGkb(VCx1D6fDQf@rFpndRS(@PgswRvt^H+6%ew^ z@g(f{iGkzSOQgS+L2485F-oGB?+tAm<0a=?i3`h+GlERxl2+pCdAOs2Wqfo(IFI`1 zeD?~?iO&_+uLLUJ8-9$SL_yr6Nw`<%*+{Ki6G_l~R#v`07)^Ez1ofd}cG?`t1Wo4x zzIEA(&!qNzFvM5R@%cmkD@)-4s7*)^JzG{H*@Hh1n;g;zGB_GDCJ|ZK&oH;@w0|BW z`L2?WbL;xr`rD@ZarnII@sZ87cU3+VUE19KQGZ>7?FHCwur>rAA?xIE8xDqK2v`v< z9rbP0Gd%dVBKqu<6Io_H-{?8_Oq-MeNhyWCgN9pF{CZ-SNbjHVXdkK4oNlIAYMHqe zw-o*R*VUjohx&X?r;wv9lm@6rs7XYx4$({e2_`m^RM|L+y*z`bT%vc*5KH{U-s>u1 zYZptukEZAMfK1k=Q7ydiQ^ydxtVPC&2&7h8uE zG1N(`YpPA~i({!KLHzs~Cub34p6aDiz9b`6PkU}LktdtznHcqDlG%1tELCEv=Oc#!B9V6jr#0Ai#r-aJ zTH-FYusS;lLYq@>MLAhJ4IfItI3Ayp&G^USTSbg|aL_{LMwcB7(gJM*pdUt-OHNaP z7P&@q!-@XT$OvC>Ov;3(&}*X0CD+S}yweqa@%ZIi__#FErFCddQb=AN$GreO<0C83 zLx5JadXZbdrv%ZuRnFh>z3fr^;|>#qIwr3*+c#$@9Vyvze~Kj9sBs^-@v^T@fknEL z(P(uIN)4Ssgr%6TS42yUz$EB+KxJF2J$DN8qJa|O%q0e1nC>lm*qN<3k+1>wGIUJ6 z6U4;3R2oF zJPB(1ahN_Jr9E2Pdd}0DZzuT&aX|9SDtdQ)wPzWD`T*e&L=HK`0g2%6j>i;;&lHH{ z^0NGmx0!^s{E#f`XhzaA1Wu&=nR)g6V=_cHlf<21mUT}M1Jw~-22T+U%8}!LudE*K zoeao&bmP?h47tbDS+~m>ZnGqkZ#}?vnD_ zG`jd?dM){h`M1on)#9-1^eU;FML%E)B9hYF$O_Fy3cH{Th-rZmjs%7soVV*j$+Pjh)tb;gj90 zgO<7r8L4D_mVuYGVkY|#E0jmL^mk6A<_^5roS9Y|hPwRkL8i#s>Lo7tl!QVta=8Re z#e`|F#(vspp=|G1bOb$S>g)(AQe;;%eafi)1;#0`97A^is71FZNHK%Ac?i!nUC-d= zfc*6u^O|5AyZYM)91c3dT*UUL$HZ6)kOk@ufuMjWuhs|(1%?i7-G|T3YsEuJQiM%B z;VweiDFiZ_IUOy-%*b*Es+!!RCVQq&>%>o4Gn zo{1dHmDU)6o^gyw^6i;?Ld~Smfor=8@YG2heQn=zI)uTr9bivI(vyIf7^tD&4Dqa?gF!WGt`TvOa#`fw$8Y0L z(=&!)Z{Ye+efZRiCm7%#lK=iV8R)sSAkyuuyip;s|} zpG}|+a-*Q${ED|OK_n@-E@sxskO?xm9YXlrfwALfjt=*=hux!2$4q!WGWdA&YjsB9 zFG~&}Mm`#&Wgni4HqG1B;>c8lAjT#ISL9QUsOV|@ML%v~-&w8;=>Vx<;6qP1YJW~r zqfts6v?<{#ZacJ!F)q9lak1m`yGd@Zlqoo`S;3jb{rjbIUNOzT7RVEL%boGYET~khjMRLJ%ysJ{1SWp70+xW+Lom zzhyazKbF*aq3;-xuc;R{y` zz(-eFXS&a8XXV7-#M|&Dw>QP4_1wtYQ*&H`yK(uDBo7Ven?y65Yi*}pGWSz3d!T{b zt2`}ikt!)tr{x)GdF8cM7Uq@rH3dx7d5|#c?5Wl37n^5KEbovuC@*FjvEH_~^YZgv zn_DYEKisE3!~Jg-6{xPnsYHW%NvKwvQqM#D5ky|oLjMtL8U7`tuvkF-X@O$z`3p0| zG4~fHpBD=L&#hA7mA_hI#N{FXw5&)XA^s5#l`i1^2%B0RI0(O!x5S^!71Y4r97uq4 zX-wa-a)Y_zk?&ft(2%Bi8A^9VTHn+qByM8ne?utyF{RG>AJ^3dEoyp9*_7_ofU4jB-hFV<)J5;)Fq z?P@;yT&7u%DeC+FiYDE(Ny%!3L5y8oQKiXney;*pc0AMA zWiNYn-O6r0?`U@IqkT0W&y<-PyR7M1xV%Se=?bgx(VTf;MZSf<+$|>B2>; zYCraK0%5|a-douSl8^6Zj`2p*xl1rJU`C@i+R$2ppKs!w=+$-`F6}9vgx*o^zTR%j z-wI}Rhxw2i8!#YjctxAWERzfYXexTl<%0{c`StFmLseG&y{l8^I??6KEW|#QX9!$% zT(7hY!fq(CQ^^>bNFcpyo(s^`RXP3oAaH&l*;H$e^v*GDiY@W-^TU}`mf(G2K;xj) zJ!(1Xmxkk(dlB9h&*dl)SvE$nlhK$+55o-l(%Z^2JZYS%Om%xC7boGYz%RHCCki~ZhQ1?d^USv%RJaYoP+>`M8RUQ*r8g$d9 z*MpAV69YxUt6Le=;%p{%fwbb3kbDQ0HVJ$Rm+}Mn$4VM_Rn>AuJo;7EyfoIwS_+-aYNYL$F^>;Y&=~0vgzI4Grc;dB1>6 zifhPtXJ~@R87sSJ-s_4}rb3dcW@Z)g#eChgNoxWyD4g zn^$bWW76x5GUA?Gqm$`J=?! zl}EO@33r!C7v?RJE5c*C#i`a2`DUr!B|=p7<@+#0nSi;egs!=x(r^zw2B(gF8o$;z z=`QWKMZ0NNs`LV^D7=j~Q0=u$Bnmjk!3PEscMx{)2&`PZv<-sV)COKrb8rhi7ab^4 zMz(%Crm4JRq432`@2jOEukQ%+>;Ie5#rx*rIXf5twsnVAWV+mJ?34(9ku z40>4c^_z>e4g;;T6l3R>CkDz=4scM^a%nRZK-jkzEei-ec$;6f2hP2&fi<{-*nG`- zktZ(W5)LVLWm#g;c@Q|Ke-=Qbq&R`v@`xnc!rq0mT!Q{wXJIpXNaiJC${t4H&EfN@ zmqyS;8=4C^w5#peeWa+sXQZ&d7=R8$8bWM9rzcaTpofR6&tiWH=u0u|r*RD!iI4h5 zVn@jvf8)ht@QCmga+=icDe2oC#KRBH6qR2t2k7#d<$5Rk1j=CS>QEgq;o)XwuVHyu zv!9&dwIZEo+Zp&$$57_7Ts_gLG5dUm{VF)$_iRc5QS`DEv9{wDw z>s*v0t36&B-qT)&WW8D&Dopdx*W~;b&AA$1IGWAW810tq;F+fN=&sc~D_z#2-1Qa0 zN^~axgl%I~8%1*#4BvNRPlR~4a9w3l($4BzK0TxudATA)xn@;i7)2+I7wTXAq$qx- z@_FCvV7eGn;$6fTC{rx;c3rn-E2ivHr`?m31<`H?c!%X@dAmKrnb~9uv5s!6;Ul{o z>IKgP`jUn+NfN?{v_1Qs{CdDh(1YDnm)F<<63|5>RWvZva3XXxrD}d%Jp#Dr*4Pj(;8!WAwML$@&E>XHD-S2od{y#3 zb7#8}zmAs;82Fx=2;t3t7urp_r_Ex}a- z$k3LfU!Bf7M{FfRY=W9NoL$FX;We<xwP;9$Ww!N(GH5KS;k8R<^|KB|rKNk`10zI*A6u6E$R+wIN@ zvue#X=qgFJ%FhcRA;gOAU256eetHUcF(6O;J-3o_w&q^C75+-Rh>L9Y3nquPTx@TY|@440hluv zpwy2dXs`M#wPi;9e(n>0MH*+o_o63V zycpqfvca&0pKZi|K0rhOk$lL-kuPubBd)g-?O^8ZsG{YDKIqAyKrJE3=b8n7BgyAz zTF&-LQPR|8hq$UBfrWHyctfdcgWQmiGK}j${jxvb^}74A5YiInx75hp1#r3vC2xxR z;O``ajh0L76-rwMo7}6EVt*|eUomc?va7q4_Ai*)#dKf9+DX`SHTgYfgl4*= z@Ll9oWktIUPod0i3l=Kbdy}tZY8lq2o0Sf` z146C&yem7q^XMnYJQ@HMHMPqnQIDIi-uPr9*hkum$-yoGhf zHf6de*eMr4OJg%$?mn+95J3Ab^u9 zTKG->WnQGAc*yH|hBYhMb~syEL~BIEgR-#3K#l}?4tobpoD{3P{hBDYjc9;^M-1#O zZ`@~PS$P-_dGUvYlJat;SMokzbXyw{JYtA>vzkj4iZM7kQ6!14^I7{G_n0O7k2Ax! zKgDAz$j(C4vTp!tSRKxwvCiG1W>sr0dJQf#>s0b8eGk-nGpH;mkI|?v9jPYyk((tY zL-kqKCSQWIpvV41Y+ml(Q#X3KICl>8fVA7PN39v9A>| zJ$=ITrNd2h1vQS(tK%m2WEKXimM$q4?v_`3S@>qsnav64;pBlcjx4ywa1uQbF+5xO zZs6}kN8h^TN;hIeSL~~Y3Qjl(&UY^ewKL<}CVaV;lBo6)aK3$dH2Ybocpa$G+fkZn zU{J$Bu$bvX|Fdvns81Ijb~8*U27WWlT5dL1j~Vx*6aOpuSv+4~7W8|O2k|YOL;tGR zR6mXw7QF!yWf3Df!m&?YBvaBc1Gby=WV3l#-?jQ7mxd2$yWp5Tlnm(j|<+i&>(%_`d$d-4WpPfDzGZ9?8<7G=oPS~VZT#7REi7_e3 z)p$5@INA7TRJ#@AY;bNph2K)F3hOlcig2Ow3A7 z4WOc|eyV>{9}{3aV_QqPw!+k_;4&h;azMe?k<=A`7!zlL-*C{f^k(E@-DE2*-}+nK zgj&-sC*Q>c?VX5#no{-Y@<0~T@uwxT2%-!>x+2}y;AhtazMkhtmZ#5RFkbzl_Cf4# zmUa$Ho}vDC>qi>8VYvyQfj}!S=c||ZYFsQ_ZH?>=tS*7Fav$vQ!o~L zzwiGP=;-|$wOl&!dWA&{*W5-vZIx3*qNQ&>|I_n@Wb z1_A{H7-ht(2lmm)jVCb#gW@tXNA#pVdEi6O)w94kHe-6mn4_nwP!U zLWKYqN1kX<5C<>*UJ)94y#E17MG(K2Y*{EpBf{`_&M^;Fj!g1Z027vT8bP{Y5M=wE z)rNWi{#%!_$0D7k8aT0Uq@<*1AQ%{!114U!9$-#iq&^s*YZwAjr^P}P^dm+CgiHq| z_(=hg-k$1PVz0mi}NIa@S#_#-BZz<`k zR~l++uyxP|iqp)Odt0yeSI?B!3uUEJ;wgX4VYN3jfS?IJcXSAWIv@>*m_;oL4Fc9z zjQ}KJ)M1-1Jo*B|1M%lL63om`*tErfn_HcSbI+GjT|^YQIBl~u#vb;h^m6p!LPbOv z9(Vb!I|c2>Iac|k9Cv#J=T%6Ve5t_FkG+QCvA!QPhpWxq15XA*OP?~rGQ2iV+=@PY zrW47-`5?h;(#FM}=a0`W9u6`eovjc!u>pkbIYYmX*V|{l50YlUPDYc{VUzGmsWiqL zh<3iaU>-6W)J<~sXvW6<6eh!ft@q=CsAm!ZNhUFgV3y8LA$3;&{qeDUQl}X!n~;)H zg7{a%SAE?t$Jgx^MiF>WsTzZb#s2iSgj@PaKYVVxoKkp@!=8!&sHeYw!G?$m02<+A zY?Fgp;)bc2ndM8StZKE;(9kd-O4Ukwjoze>*tCfd1ciRUdGg^h`0~{h$>Gc-8g#=@ zpq3E!Gx6?##3rAVnE2b|E(bw$+{?F7E|&8pyr?AaXrK1)F25%E;|uy?m@0h2dhG@| z#+=JEOOB2N04YFbEOdP&Y=01&8nbVLc!s$VIWv6faW6 zYTrVx?YGfS4IQ_@09lI}(F{ja>3+QYa*U*N_aCC+f~?78KsJwidseM_z}OxBmn>|r2r;$i@{u=j(NT%8v7tc`01FBBgpNth;P=3IG&#q~ z$;seSI;Y62g995iASxgLGACZ+ zZVow7DofdG;0!caR26{oo~Y8eKW0c=c>SCuC~hQEJ!5yjyg(}d7&o@a307t_&cAA7 zWMqW8JwHD`LI2hW8xJqij9!=Z+Dk#(B*Nev7f@A&xV^m-!8NFo3Q_Qe8{5ZM!^P8eZGBHZ4K?fzA%5#3N&5-`kk!%9fCo`Z#5~;X zm)nYWc0N2lGSJgo*zC7KR!GW|v1VYr23De5oo-GH<5uG1!m)91!VCP=BMPD8RN?D` zYheIDh2BNR5g3($*oFJJn;%zBvp)aBvAi~%bMBw}%MlFEU=f-5kcQy6%aX2{T|_{&*pYQQ}`GEPn1^dbK`H ze3e*RQ~B_!X+~_AWOI~+$W_GW+F~kFY}yF?+zrsDPkFS(tt2m(|lF_7T_O{^~GMF(Cm+ zIDsIK?CmPLAyPq$>%7>V989vBCbNm|Bdyq-)1V+^t9p5Fr}--}LJ__x_7s$fsHM)G zKz2A{_G$)hK{~ZI`PrwJRdCm>%PFVm#so`j0f9R$JOy&6Re$0XYYK|C;ykW!V35i6 zm2@N&lhEiu#sXYehQF;9JgPL9}qru?d4RWUrUCw{CH0~ltQ4&_Nsp+VxO@g&PcIY5kKcP*BrpjOhL;lk zo(9L`PI0xJjs&qAS6c%`N z;pC7F&9>RW5P^ZQo?T%Guy*Ug#Kf%(27m0B$De)9=JK4__63=ymFYyKvzfjD9W1vZ z>a3w$36@aR5JsVQXqem2+7fx#LKf;o+n_-y{kKugj1nI*o1lS#F?|G- zYHtI>sV|k^7BVAJ-nt?%i1WLk#Vo~jwkhf9p^7D8_Upd$6IJ@51e$GhhT+h#cPRJt zV_WvJrUM3vCkAB31rqEYo`TZpHa3MKxlBkOu&7pc`j!gzwpC;)b=k>$~ z*(_{};U7SgyIk(6W|uvUqsQh^y>3SXE!4H5$$t{=9IwvJg|?EBl!CGduwnDLa{|pa zx#IJ*d(-N)Ww4nrfIvJI6x3#OYT?HE7=u{>x zUoKtzbHLW_)h|OKko=@PfgvfGh22)~}^g-=S-}P4Atc6-TyKO$XJaq}S z^KhA62*t-mw*zSChSQ~-84T&%ESoFytp@ZbAvqLoC8ecjr?x1_lbki`idWN>#uTTn zfjM5iLl3pqSzZ_h$`>GvW#4oL1SIoU5vt^#o2FT^R{{d`I>^)2UfZCRf@Y3Z%vsiNPh@NtAca(P zY*cg@+w5vJ(szsByfsj9J> zauQXt^6)r`y4(ew+?;~1@t7jh63{U*)HH8B9)Om{^KAf|{nXy)4tkx|ic~0aQz>ZB z#Bwr!N|0;o;&_GMYmw~ZM1R4p#6J6WTl&;F%VW9zbF24t86EI^^D|ijab*P7FLv6T z*3-vF`Mz5Nql&C&DDJ`0zjh&rjYbxa(_Z5#crXA`MD$HN$fpGSaI-+G`wM5Ac>1VOBui_&KxD7eVKU*( zr_Pg}g=T#G>z_rWN;Iz@AKF*C?ylcmj{rdI(cG`CtyEs`FMAMd6%J#%Z&IsYCv9Ee%S+6<|WXdu>*|wUgzuxg>sewBzMh z{jxvQ`et^J;&0rK4DC0Un*NP$@+Kdw4G18@SsH z9@XVrdblmFub)v4FC-B{Sb^fvAE5yks5!6SGz$2LdcVKAzbw!v5%fr5BhLx`5Q)`4 z#(z_Nrq}zKvQKRHatfaON({TUDiND$cYOS7 zYW}+GRw=|k%)b?4^{n@Ye)&?ACeY+@nh-lvmKw8BOd=GCL+*qTP_q!af7QdNsiODP zpIzt>_%wLQU>XRA#*sQ48u>KYY#)0?Jij&URiIV=x#hv99n}}4plVvEjiUP6C{ZJ8 z`Navh<7Lsnw&L%RV~09551oe#zP@eGLxbtn>|-`u^~d9OFLP3Buffj`;If`6<$RU( zGU}D_HZHXYUwQ-38_z`NnXF~Dhc(mDMZiY-v0-5_ekg=UK$8n~t^_v`8P5$2NTZz@ zh1_LZ)>31Z!A9pr(bS^eZc{yGsjAK-LEqdgYV4!=_YC63di!Nmcng`Qg`SfW1Gd~f z2$d5&pyzOVYYPG1R+2*+#;@jQC536g)1D{T_@45dLoXH}K(Kz~6=}{QfpX_HQ}Iml&Mx_m{EX7&!m`VE&eWW8V7z zP004YWcmM6_zjx-H;5FB7Vr&@?7es&z92of5 zqwhceTLR|jyNxPkDv{fw~D^axQ->zmzrpk}oT=f}xX(vW208DLb=~lY@c`1SB@p zfBr%B{j`fj@I^O?=%2dI;MKp_5`qaKmcc~9e^I}CspWw+g8!lpc?0Or!+}7SLjP3v z=lw5g?*EIL)kjmj4jKelmSE(NzqlDg2qBulk|892f?yd!2n@XB zp#I&#H*fwgnnX2i+)p?l5N6FwBIh}=*$)o*O8treM8h;qy|94XDAA48Ign(AulKedrLc)|2NDqk`S^v27D4q_{TPD1R>;c445J8ugKa)5F${; z{>upXO&H!E(2EY4)=R6HrJ0$l^Itx&{U7|0F|pvae_4VECxq;d1(SsTHAuf{Z)3qa z;s2p!iUTKy|A*G-C1>%+cW`F-e`r%)TK}7tu#v(|4YK+I$(; uw>~ehe@u=S91QI5Im+I-2qGZOAN5mT|G#~n z-8uK(_nh~{%+B2NOlS*qY8o__(kmEPY!C>92yzYLRwYHGU-gW`5(Hk+1;=0BUe^#< zmM?#NDowF|0fu|R5jkBld#>~0;rjfgs^6O7HuH`HAVzV{4L3#;StJDb+${3>JmYJ> zJN&`@;enl~O_>S$-D*|_{chyoHU3!lgSy5udPSo~-6%x`zx!L>H~vqt9ImleV{kk3 zPAd@;b|w6A0JH}|Z*t(JDF;C-KH7TX8Pm}D7Lt&+0hkx{U4SzV0}0GAuRwlu?2TEr z?j?IViCEC;D(#5}a!?WvqHl}J3iHhOlOs?H%r^DO4m~M#3ehBZeVoc;kdt|6$-B=1 zVXN;UdR;Y7=u-)J#m|4rC{mW4<_q)OD+Y@t}Y$c;UnzJ3=K((xf zvmNFeZ+-t(48_%>tMHE1-4orMv0#S9dQQ{bXJC=NG)@gcZ{`Sd+EhNt3oY(-(%^(b zgSAaHTaEL{lvLo^>+%Bhqd`|T4%E^o#P3MnBJx8*jz1LU*~nX53^w=IFj0GK!>#k` zU&VY!Z*SS%M&-^Lq`O6}KJHwlKlC}R?sp#%OfAmIIY8sQgU2`dL_)adZBl%R_0j-| z%}wAY{gb)unvh@!Jyi!i(QU^hqlL@OwkjTj7#H(g^N9~|HuCLIB&=Z+<*RVvFgjlXLSLQqb|*4Aa5VI;EJ#k&VU{4)(-?=#5<9kqhRm zVr16Cw-2f>EfltwjiG`!FoV+zZ^nYSVWLNlJD@rIs6EN3SQnI}#d&K(BzT_D3Uw`iYaq(Fc{UV4vh0J$jYJ4|g0_Chw zJPy+GfTCzFrYKykq zeP2#7E?0!*;c%aB7v%fy6&~%A#SUU1`GK#Q*Wc%RhN7<=a1VqCSxiJFZc zWog!;v5-NQaQ8Ryz9H#6AAM4TQ_nWF%$(xK?-rNmnE`e7St0Hxmu*%Z4MNA|<|{3g zZ(d>FT=;Ir3gL^155~1nQiW=3(co-QIWX|>dHHsZzhD&apKQP-%2JVW^r*ni)%iXNYqo|&t^=)3ZU)GFYO&$d< z^_Dfg%H)5(P6`z7t%h+=C90A|==yx%c$$V< z3HZ5w;8R%PNJ-$)UKU;ONit>9=E8&AU^5~*8hP?k9}b>ux-B)whTU?dr!LV5k>> zDGYBl;_1F#^@RIDrJy(vPi9a;+`!@Zwwq51LuAcK;)Sv5%t8I@qVX+5LX5C@iINGo z;T^qewKh__v~Xp+IMyoT9y@U5z%XY$I^7CRPm&O=57tOj-S()8SE470muP?>Z>vhv zH5|!Coc47P?E%W}LymJaKr!kR<)Vua62h9resS_@^o*HzvORd8-F^&1N?vgC z9NZx1L6VDN24#3kFZgL5br7@-Wjr8WAm11LHHgXu%f5rJPQ)}ID&^49Zm8Sf?mcik zb{k8qz<5BG*!f=C3*ApTp%tFA?4UAw^?ALtrz|xNYO@;qLlj;(L9YfUPt&ePfj!#< zsg`qNJUn@Jk?A;ocOy1O)dK9f>R~kDU5Guzg*(yO*mhoS?{qq&Z<1fUeX0scuVzF1n!@>>C0c5f zw9m=$UM*To2`yb{LE7!fGG|=zetEPm7PL&&_x@VIZdr|USR&fSiW-*EWEXwYbE~y>04HpHu&>mJxmwU-GCtTaW|b55sn9dyEgh`E_I zh6rKgk-0@ZbtYO==fsH*W?WY9!}xIV)FQT)8Sj4n-jVc~ER6#o(DABfKwI;RZ%=MgBGeJ>Ak%2yOB6AO zQ?;T^TGADD1!CB)hM~T&-OwOH5H2QiHd3L>|H~Olv#Zl}BZ*>3 ze5>i#rjbe5gxzd|xBj_P_GzRiByZi`AV1mjs}Tc5zB zrn|WYXKMLK>0(78+*uu1PY>h9pEY*SB%6GB?W>!CaE}h{DjA_FR4zE$el5~?VLtmx zdOpVd(#eer>5g=ZJOsHn;zx)SUSlaapywZ-z~tz3>u|PlQbNiRPg+Oa6BKPWCG z8Wo1Z2HEnYx@$0Kc}}Tm7`uf7U(q3Iaw@Alb0JxdIJf{UuL4DAXUO{+rTv=RrjxY( z^$f0M{9;lh2RY}+-5eNd*@#^kc?ena^Ukx7atb>C7@1dtiP83Kx@F<_eVgD!;5v={ zxu4-=U7S53wI~-xU49#@dW-L#gMUd}C9?4-d5)YB!hvKlYMg**eE}r4Zk?~boB+2& zCSi|lN)nA{BF?HAQph{t7|`%T1kdp!jVr4)wqIuTglJV%N-uOW%%X6t!p9)JHX*%- zvwTaAx{~v+UIp;qreR$i;Ll^m03Ou2y1e|g>o>mbNbuzN)%eZi9_{X=yT$@(t@#Ez zh`wTCXew?}%QAQL!&Lf(wpI2inNgN$xDehjgM_U1!1&bAlBb_S^|QAOv-?b(6NK6H zHv2!GWE_#pnt)hdhMvX?XI8H%(`k6z51GQ7X%X{vr*C5T(7?656L?m+3J6iZL&oU1 zG~N-wWbS<{l*s=a?OM8CK0IK~+wa^CG(coUDkr6B3 z{GO+K)jrSX+In;x&*1<}LWV=nyX-MGa87x+BANQNvC@U#Fo7J42w)|H0@r*Eo3n-# zi*f~Zm~(@OY!zS{Bsp+CCxvcbrV6hVU@oVk_3L1GCY$a zQW6*7@-cwfB1;~9k0%%)01{5Ce4G3olFZoBV$_kQ%MqBvr-xTXAo1q^}P!?$|X&qhK? zR>ee@%NewL+06ikux>duY^vOHIm(BkP8skpZ`lbO1+%jKqQqU>8UhJ)>)xpivh6Tz z{(cwPSL5e;C24Q)4yd`4+L}HTe7>)J3vsK=Lnm3G=bxd=eth7~iaf1w1 zM|>qG*oPoz7baSZQM~Nu%6oI9APX@|kqXkKc)g>{7NM|M%L_ImQOEkunc#(*!j@>h z&u)XRW2VJ^OzUzM!!4Cn)chqHcpD$!DD9uox5)z!IG%-=*P-^@FQrjo=#=)pH;)f_ z-nai0W!k~Gc`c_^CWnHqdhLw!SVAMEw1)O$zkFL!X-$~Yj;ZDue7*1NzK|r|zEJgO zpay&bVFjs52HdB2P+3(Y@8VKet?V;O!xm+_jz*n~8UjObkiX(RN3;q<`qFLOh~#$; z3oxN{0Vy|9!*FV>(`t4)H4G>a$LB8XmNsm)6T){3MwSwlwF%~{QXvJTg)wt$P&4V7 zt{QrDISsgnylZB&eiVXdiNW;Y>6|GuO;F{Og_??SvK*3oZE%*e%1xbT_4|DYi_Vge z&V0gBOqx}(5@SicdPYb+t5Xusbht8bd0=t~CdGgQs@qy^xRYNj8_HtMx^+WWi8%-9 zF;Zh}FTUdm?4WHyYPlLh_+0bD=kEBd92Ujd!#0dU18-!Nd_x+~tePK0v=1T_?Q#y* zmtMP$MZ?csJH_}?_P%sJF|DtYE{2%gB@7=|zNZCt$55CmD{-q8c88Wz4g7SO+NRXT z5G~#osHRy8M62W_$bvu!bp z-B%T|DKW*cZ&e(>*kRVmlss}W8=ed_f6&&+MBW~0VT;8e_YWejqj`CKG4b*Q8zi+y zk^K%s8nLC>7JtMNUpM=ey}{cHD&H*}@+RnGfXOSQlOFEzP7BB9d=aM^yoKD-{PR|e zy%)cU)1kjlQGqy!R&hym+6v7v+^1&{%P#i7Fw{{i z`c`seOtvRp=_;<>t!%j`;Wv$a-ZC(?aE0YJ1qsKk6C0y&n`qF|)(xEilu9*g%lVbvP+1(9h z&ErXbK=}^pP-D4?Hd8tmC)NPML>Wc$iniMBJGK#!e(r?xr!KCOUa>v53P`F=GMy3r zFy}d)-rE7VSQEni#G;D@@r57 zaUSf`XE9b?LS-ipTNT+wJV*FPWJ!y5f&iTS5PVTr1t7GYhX_iAY3qv_Q;ODb36h4z zi#VFxwQnqTHk)|MB$>YVl(w)_MT3@wxdtT&v6SwxkhPTVdm`rim1kT==*X#Uqv(3>b4PIhCv;;+v5 z*`>#!r`{GnALBvQPY_3MIt_GuNqrqR6`U&@ZJsU5o7<~<64eu=bsFf5acF~b64Q+% z67PFmAdUVy>y5=yH?F11Ezvi4jeeoUuLeHfDDXczXrK9AN!c6REwmjp0v5yf`kavO zD+GDc+bOeif-%sa^}WicGn#iv_hAkezfOPliGl9!aWqL=?7=_)NAIU59JT5v#>!kl zp5rg_J+jFEiA1GF+f;sP&S-m}?Ztj%xtP#Te(0Yd;Up z?>o=f7^N#B1!|it!_M41KlZ`iR3z(&cG5jI;Aq3Xl|XU1H?KWtf&g103gd>Q6Uurg zLfcKjhOi^}mE`G^5~^WYPZJ~u2G*w@k>PU!Ev#D~6gS*a3BidaUiI>^DO4>^_y;XE zFmNqfKRT<+cF4a)8PK;mE#Zl(596O#EF6e@jFdRn9?3+*cCfmtt?{`&6gnO_EuvPF z?fwwB_h4U@tnA@O(+Uuo8sN>(7WA0Omxg|QLMmOK^j1;(oy-hBbA3`0oSrNAaOvwf zL}{F_)>}!3@Qb@wP2&kQMk!LBTBN8n*S^^3jrO1wUCWQ&?I(ExqP}{mY&vvGWP)H` zdId$MKm6TPh5OZ2tx3YEL5F#4uhz&@EkOMe z=xAtRe+laJe+mA~mN36&4%vEsW2!mke`BI~VGw^UC5Wv4o^h8{fc`bpFO7oyOPE$U zL--|(Xm{WMKU=wn_R~46Pe7o}2M`GTvB4|lW+m=m;$~-N@58x-Btm5mT4yfdhql&KWkZ)&; z)V|%{->+zH|N3xInViS}6Bp+g2kmH&$inDQ-009aGq4bzxo}(F zv~?>V-*XQ+`#`3C_7;{3lc$stqNH*UXPul!O4O%)i<)6A*X!O`!F{6>Xd1^A1~s4H zNC4^M^ufFY)WaUq58avQtt>PXPOcB>P@|VLXhZMf%nFuR+@%i0&-W3M5K2EdD6W1Q&4TdFQ*N_I@Z-IQ~M55ixaOauLUQ*w8a3l*2!%h29C$7=`u2-_(Owjdyd`dHA!RPG9 zJcVJ`il3Lq{c@d)OuGG}$pn6HG@W^{r%3V@nA>1~8%qE`~=u-GtgbZc9Q0JkiG1Y_5w3-(|g5us$#ubdBu;J6YzU)ci z*Dvf@f6qS5OmR4=#ZkAjWwF}mWxZ>3*D95GJo>3$aWuHJO`4c{X1hqf-ikT%s%!dc z9PNbhBHloM=YCpqLW0owJLTEP%`mVQcVNG1%3ot|0X1&+EmxpMrF8h~NOfdFUN9z& zx8_H(!Q8`-5v4Z8BdwR2rbKTtQ(7-PYkAzf6$A=BZcO3lYUHYgEEsyHdqo))3OTP3 z+3>jI#wjLW#-e`OJ6|B4kNs?FmqsXEk|Na>**|sAOBzQNjF)(<<1t<#7&`#)-;Y#> zPYH5*lqKL(Bn@iV-<0pimKllXu%MKjvxn19<4|B8-TXGHKbEQ|mIvlf28}ZV>u9%M#P2=XZ2t%g% z>VIvXe#jIbLDZP`2GW$C^0giq#W+&XIov<8_wF9^JN=4(Tck9kS8}|tS*dN_B%z?P z{^|(7dO>6QNM~m_#pPkV?YVF)EuLDiXmA_gLo%3f2<>g+EK6s!Cf+PFo9M8tfNSWt@+U%YUBQ4%zDr^*kuE2EdgHhdW;o6#9swaXTj%6$V&=1 z4MvOcOvoEwxfCQ4=}aycw$xy+D*!i-v%1O$i>)B)K;96r6cCc5Cr)1s*IcDS9UVZ* z>E5i|H$!dE$u<k3?+U9!$wl;Zs zUDSMNP0~G_PL)=kv`~O)U5hUhlxZHixbP3nz*O@i&+%yon4(3i^Ebq0vy%&U^Gc@j zv-x`)9;f5=p2F%ac-29~`}~dM93698*=zT<@BQ~#%}H&sCSkZKBkG`XlT3GR`NWl` zpUelE!EtYCUcuek4{)b$CMKn~!?X!rUMoY5x%>Je@|M46QI}?4fa-WS=TE+?e2YkS zUn<*q`rbL#3N}TucE;Rhn`7;WdwLNe3^*RS=VrM{m;sFl`rp>RZToB)&ZO)=;5SdF z&2nRSq1i$fs-_0i!Z&y-Yt=o!@|S8>wiCQT*I1>h~2u(*8ro+gB7B8CLJ`J5=T zLzw%b4qzujm7WW%SI*wLiAnYuBNaR{6MonV4k@Q)t(6YJF;dSNc>iA9+eq-%qN%ZS z!dl9pzWy7BA}ehZlw(Ou0wpKy=@9o1dqCj0>sK zo+?^;32^1jikFwRA8>ZcztwqhkU(F&hh6a0d#Z?QelFm!I@gJURj!Q4xxm_(K6FC1 z8Y$AHLNZ~PomqcNE!;3~$D&!8j%Z*@SF8gK+MOPn4RNl3fyZnar?%mNa+9pSU3|$Y0NLYwr01Mr92Q;+z>K=q22` zEVe!#?3YG2LTLAKs6>e-azmOOY$vUAmb%VN+jEwX@pJg;ZtS6h!BxhcCDxxQ{NUC+zg+D_S*&eV#Czjy zy0ah2OyOyEttx9p^?ch&GD$rdpP^l4CaBp)%cya(ZPxQqiPhuWu&{jDn}8G=jCcQm22w@qx!ZdjybX%h$Ku2 z@6~;OV36Ef8hAZRBAaC1-V7grGuquLm&YN9C1dixuk%a+by!j*d1<;txI`5J-v?rQPwTi?+YN7zV1_jhhnslfGj^}{+5rp&k8gIYM6T2#OK*=*2 z`HD9-3oNey=cyoi8(&;u5DMrHCC(%Wj+d|M?!ujr34O3O;_Ha?Zt^IF^?jDtbo2e;y%KSwmhOGP#nQ&y?yz^43qp=f?02s*;zbj z_EfE_V`#$C(c_{>Dev(r{3^;qCI#y(PA0Wv+4c6iNUHvBmopB=m#%QLGLry%bMsh3=_oo@#*(=u1b8mQ z$4k;)riOlpQscu#akrn|^w5^lEm*<)^y<;I;@2;d%^aFjfb*CNMjyq@ooJB6$^hyJE%dAqs}&XF zsS}3Q1tZc}hIi*k)4807dDLbkaJP9JamyV1b%0T~A4M>w{1trO`rhh8YsCN$IDT-Aia^`p)$cn0rnlKFj z(7ll&f1m)|%tUdgH6UTPb_1zP%2AiyTwDTy#R!E0eFLAR=e?RsXgo5@^;G$-OUmlD z^>0NcJ-+c=%C+OZ_~xx@VLPhn@yedPd*+BiIKsSET20?7G_M=D$Gd-(2Yt60$4(75 z=zelB{Q&j%ZtwRiG{}Y7V3ufxpPMnTK{V^HBRHDv*O44d4Bn2${w3f3HOCsm{?A%) z3^wA=z1wF=YzZ(_3;`GtgY|3i^M61Tv40yN1;38{z0Mj#29Ehl{z=mHAI#-{Fidg3 z@jo#}aet5{{k8b{(b%Vdf${Nw;JD)dc8Uya`mzh!dp@08p|lY)!E zC}05mYi|qw89^}^2kB?{zurOu5B!Y;6GG*JpCu6K{9MbD+Rmwh0)em*Kp?coZSCKm z+5jyo5J<_$*;Uoj$kB|I$=KOZ=@k?-Hq5_oH+98n`#!*cKwJ2K>T-f(5{Q3gx*~xP z$`CyK*XZzZ6bVLnPyCC!^=e}(?UB15`A_`|w*PQ*{$JcR>kC7buprPEnm=`0!NB|9 z+*$7lp;Exje~lI&M_J$>e{pNCTE|mCgFu<6f9eNF{)e0Q|Khf_Fuhzy0)hM$f42hu z&DjLlA(7-)+_8y-P;21Qzeb-PN3-DlM3P_i_|-}PVMpg?v;bV+TMW zaVv9kH<#apZ2uRT1prn^`Ars^MhKk_fFo0W=i(>#1OOgP`G=bZ-v3Yk+5fPq`nONe z{Qh8()PF5G`GbMfe=I>4_=6#-e?+nHX#IB-9jX6Dff4Y(QE>jj_$y`N0bs|pf1~gU z0GFlx%UuxwK1}<~{g~h9n~xU%w+dW;+I&m}#k0V_-1)a!{~zvWjuZmHIqCmchYkz` zFQxzMN<$zRKjR-)$c`S3{iU0M!(9FV From 648c55c8a08e091bf2284bb9eaa3691f3b1fd28d Mon Sep 17 00:00:00 2001 From: 0penBrain <48731257+0penBrain@users.noreply.github.com> Date: Tue, 2 Nov 2021 19:07:23 +0100 Subject: [PATCH 114/138] [ViewProvider2DObject] Fix typo --- src/Mod/Part/Gui/ViewProvider2DObject.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Part/Gui/ViewProvider2DObject.h b/src/Mod/Part/Gui/ViewProvider2DObject.h index a6fd7b9221..135265d0b8 100644 --- a/src/Mod/Part/Gui/ViewProvider2DObject.h +++ b/src/Mod/Part/Gui/ViewProvider2DObject.h @@ -21,8 +21,8 @@ ***************************************************************************/ -#ifndef PARTGUI_IEWPROVIDER2DOBJECT_H -#define PARTGUI_IEWPROVIDER2DOBJECT_H +#ifndef PARTGUI_VIEWPROVIDER2DOBJECT_H +#define PARTGUI_VIEWPROVIDER2DOBJECT_H #include "ViewProvider.h" #include @@ -103,5 +103,5 @@ typedef Gui::ViewProviderPythonFeatureT ViewProvider2DObje } // namespace PartGui -#endif // PARTGUI_IEWPROVIDER2DOBJECT_H +#endif // PARTGUI_VIEWPROVIDER2DOBJECT_H From 76130c85ceb9495c56cfbfa59ac9ef4260c50a40 Mon Sep 17 00:00:00 2001 From: 0penBrain <48731257+0penBrain@users.noreply.github.com> Date: Tue, 2 Nov 2021 19:25:12 +0100 Subject: [PATCH 115/138] [ViewProvider2DObject=>Sketcher] Fix grid visibility management Error in grid visibility equation that makes grid to disappear when saving document in Edit mode --- src/Mod/Part/Gui/ViewProvider2DObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Part/Gui/ViewProvider2DObject.cpp b/src/Mod/Part/Gui/ViewProvider2DObject.cpp index 3c1ad047a4..a19a47803c 100644 --- a/src/Mod/Part/Gui/ViewProvider2DObject.cpp +++ b/src/Mod/Part/Gui/ViewProvider2DObject.cpp @@ -274,7 +274,7 @@ void ViewProvider2DObjectGrid::onChanged(const App::Property* prop) ViewProviderPart::onChanged(prop); if (prop == &ShowGrid || prop == &ShowOnlyInEditMode || prop == &Visibility) { - if (ShowGrid.getValue() && Visibility.getValue() && !(ShowOnlyInEditMode.getValue() && !this->isEditing())) + if (ShowGrid.getValue() && ((Visibility.getValue() && !ShowOnlyInEditMode.getValue()) || this->isEditing())) createGrid(); else Gui::coinRemoveAllChildren(GridRoot); From 7cd3930dd8756cac144cf2222ecc3bb2258680d0 Mon Sep 17 00:00:00 2001 From: 0penBrain <48731257+0penBrain@users.noreply.github.com> Date: Tue, 2 Nov 2021 19:46:42 +0100 Subject: [PATCH 116/138] [Sketcher] Removed lines that was introduced to workaround grid visibility management issues --- src/Mod/Sketcher/Gui/TaskSketcherGeneral.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherGeneral.cpp b/src/Mod/Sketcher/Gui/TaskSketcherGeneral.cpp index bebd6a5844..81a2ec0077 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherGeneral.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherGeneral.cpp @@ -264,9 +264,6 @@ void TaskSketcherGeneral::onChangedSketchView(const Gui::ViewProvider& vp, QSignalBlocker block(widget); widget->checkGridView(sketchView->ShowGrid.getValue()); widget->enableGridSettings(sketchView->ShowGrid.getValue()); - if (sketchView->ShowGrid.getValue()) { - sketchView->createGrid(); - } } else if (&sketchView->GridSize == &prop) { QSignalBlocker block(widget); @@ -293,7 +290,6 @@ void TaskSketcherGeneral::onToggleGridView(bool on) Base::ConnectionBlocker block(changedSketchView); sketchView->ShowGrid.setValue(on); widget->enableGridSettings(on); - if (on) sketchView->createGrid(); } void TaskSketcherGeneral::onSetGridSize(double val) From ef100d55e9d50bd74ed8cc505a5d059de02cc146 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 5 Nov 2021 11:10:38 +0100 Subject: [PATCH 117/138] Gui: add support of TinkerCAD navigation style --- src/Gui/CMakeLists.txt | 1 + src/Gui/NavigationStyle.h | 18 ++ src/Gui/SoFCDB.cpp | 1 + src/Gui/TinkerCADNavigationStyle.cpp | 285 +++++++++++++++++++++++++++ 4 files changed, 305 insertions(+) create mode 100644 src/Gui/TinkerCADNavigationStyle.cpp diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index b901e97e8d..8990d7af4f 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -839,6 +839,7 @@ SET(View3D_CPP_SRCS MayaGestureNavigationStyle.cpp OpenCascadeNavigationStyle.cpp OpenSCADNavigationStyle.cpp + TinkerCADNavigationStyle.cpp TouchpadNavigationStyle.cpp GestureNavigationStyle.cpp SplitView3DInventor.cpp diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 5f394c550b..3ef16058d7 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -37,6 +37,7 @@ #include #include #include +#include // forward declarations class SoEvent; @@ -429,6 +430,23 @@ private: SoMouseButtonEvent mouseDownConsumedEvent; }; +class GuiExport TinkerCADNavigationStyle : public UserNavigationStyle { + typedef UserNavigationStyle inherited; + + TYPESYSTEM_HEADER(); + +public: + TinkerCADNavigationStyle(); + ~TinkerCADNavigationStyle(); + const char* mouseButtons(ViewerMode); + +protected: + SbBool processSoEvent(const SoEvent * const ev); + +private: + SoMouseButtonEvent mouseDownConsumedEvent; +}; + } // namespace Gui Q_DECLARE_OPERATORS_FOR_FLAGS(Gui::NavigationStyle::RotationCenterModes) diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 0dc42462fe..92e9045ad3 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -186,6 +186,7 @@ void Gui::SoFCDB::init() GestureNavigationStyle ::init(); OpenCascadeNavigationStyle ::init(); OpenSCADNavigationStyle ::init(); + TinkerCADNavigationStyle ::init(); GLGraphicsItem ::init(); GLFlagWindow ::init(); diff --git a/src/Gui/TinkerCADNavigationStyle.cpp b/src/Gui/TinkerCADNavigationStyle.cpp new file mode 100644 index 0000000000..524d1eb210 --- /dev/null +++ b/src/Gui/TinkerCADNavigationStyle.cpp @@ -0,0 +1,285 @@ +/*************************************************************************** + * Copyright (c) 2021 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include "InventorAll.h" +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include "NavigationStyle.h" +#include "View3DInventorViewer.h" +#include "Application.h" +#include "MenuManager.h" +#include "MouseSelection.h" + +using namespace Gui; + +// ---------------------------------------------------------------------------------- + +/* TRANSLATOR Gui::TinkerCADNavigationStyle */ + +TYPESYSTEM_SOURCE(Gui::TinkerCADNavigationStyle, Gui::UserNavigationStyle) + +TinkerCADNavigationStyle::TinkerCADNavigationStyle() +{ +} + +TinkerCADNavigationStyle::~TinkerCADNavigationStyle() +{ +} + +const char* TinkerCADNavigationStyle::mouseButtons(ViewerMode mode) +{ + switch (mode) { + case NavigationStyle::SELECTION: + return QT_TR_NOOP("Press left mouse button"); + case NavigationStyle::PANNING: + return QT_TR_NOOP("Press middle mouse button"); + case NavigationStyle::DRAGGING: + return QT_TR_NOOP("Press right mouse button"); + case NavigationStyle::ZOOMING: + return QT_TR_NOOP("Scroll middle mouse button"); + default: + return "No description"; + } +} + +SbBool TinkerCADNavigationStyle::processSoEvent(const SoEvent * const ev) +{ + // Events when in "ready-to-seek" mode are ignored, except those + // which influence the seek mode itself -- these are handled further + // up the inheritance hierarchy. + if (this->isSeekMode()) { return inherited::processSoEvent(ev); } + // Switch off viewing mode + if (!this->isSeekMode() && !this->isAnimating() && this->isViewing()) + this->setViewing(false); // by default disable viewing mode to render the scene + + const SoType type(ev->getTypeId()); + + const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); + + const SbVec2f prevnormalized = this->lastmouseposition; + this->lastmouseposition = posn; + + // Set to true if any event processing happened. Note that it is not + // necessary to restrict ourselves to only do one "action" for an + // event, we only need this flag to see if any processing happened + // at all. + SbBool processed = false; + + const ViewerMode curmode = this->currentmode; + ViewerMode newmode = curmode; + + // Mismatches in state of the modifier keys happens if the user + // presses or releases them outside the viewer window. + syncModifierKeys(ev); + + // give the nodes in the foreground root the chance to handle events (e.g color bar) + if (!viewer->isEditing()) { + processed = handleEventInForeground(ev); + if (processed) + return true; + } + + // Keyboard handling + if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { + const SoKeyboardEvent * const event = static_cast(ev); + processed = handleKeyboardEvent(event, posn); + } + + // Mouse Button / Spaceball Button handling + if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { + const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev; + const int button = event->getButton(); + const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; + SbBool canOpenPopupMenu = false; + + switch (button) { + case SoMouseButtonEvent::BUTTON1: + this->button1down = press; + if (press && (curmode == NavigationStyle::SEEK_WAIT_MODE)) { + newmode = NavigationStyle::SEEK_MODE; + this->seekToPoint(pos); // implicitly calls interactiveCountInc() + processed = true; + } + else if (viewer->isEditing() && (curmode == NavigationStyle::SPINNING)) { + processed = true; + } + // issue #0002433: avoid to swallow the UP event if down the + // scene graph somewhere a dialog gets opened + else if (press) { + SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); + float dci = (float)QApplication::doubleClickInterval()/1000.0f; + // a double-click? + if (tmp.getValue() < dci) { + mouseDownConsumedEvent = *event; + mouseDownConsumedEvent.setTime(ev->getTime()); + processed = true; + } + else { + mouseDownConsumedEvent.setTime(ev->getTime()); + // 'ANY' is used to mark that we don't know yet if it will + // be a double-click event. + mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); + } + } + else if (!press) { + if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { + // now handle the postponed event + inherited::processSoEvent(&mouseDownConsumedEvent); + mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); + } + } + break; + case SoMouseButtonEvent::BUTTON2: + // If we are in edit mode then simply ignore the RMB events + // to pass the event to the base class. + this->button2down = press; + if (press) { + mouseDownConsumedEvent = *event; + mouseDownConsumedEvent.setTime(ev->getTime()); + } + else if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON2) { + SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); + float dci = float(QApplication::doubleClickInterval())/1000.0f; + // time between press and release event + if (tmp.getValue() < dci) { + canOpenPopupMenu = true; + } + } + + // About to start rotating + if (press && (curmode == NavigationStyle::IDLE)) { + // Use this variable to spot move events + saveCursorPosition(ev); + this->centerTime = ev->getTime(); + processed = true; + } + else if (!press && (curmode == NavigationStyle::DRAGGING)) { + if (!viewer->isEditing() && canOpenPopupMenu) { + // If we are in drag mode but mouse hasn't been moved open the context-menu + if (this->isPopupMenuEnabled()) { + this->openPopupMenu(event->getPosition()); + } + } + newmode = NavigationStyle::IDLE; + processed = true; + } + break; + case SoMouseButtonEvent::BUTTON3: + this->button3down = press; + if (press) { + this->centerTime = ev->getTime(); + float ratio = vp.getViewportAspectRatio(); + SbViewVolume vv = viewer->getSoRenderManager()->getCamera()->getViewVolume(ratio); + this->panningplane = vv.getPlane(viewer->getSoRenderManager()->getCamera()->focalDistance.getValue()); + } + else if (curmode == NavigationStyle::PANNING) { + newmode = NavigationStyle::IDLE; + processed = true; + } + break; + default: + break; + } + } + + // Mouse Movement handling + if (type.isDerivedFrom(SoLocation2Event::getClassTypeId())) { + const SoLocation2Event * const event = (const SoLocation2Event *) ev; + if (curmode == NavigationStyle::PANNING) { + float ratio = vp.getViewportAspectRatio(); + panCamera(viewer->getSoRenderManager()->getCamera(), ratio, this->panningplane, posn, prevnormalized); + processed = true; + } + else if (curmode == NavigationStyle::DRAGGING) { + this->addToLog(event->getPosition(), event->getTime()); + this->spin(posn); + moveCursorPosition(); + processed = true; + } + } + + // Spaceball & Joystick handling + if (type.isDerivedFrom(SoMotion3Event::getClassTypeId())) { + const SoMotion3Event * const event = static_cast(ev); + if (event) + this->processMotionEvent(event); + processed = true; + } + + enum { + BUTTON1DOWN = 1 << 0, + BUTTON3DOWN = 1 << 1, + CTRLDOWN = 1 << 2, + SHIFTDOWN = 1 << 3, + BUTTON2DOWN = 1 << 4 + }; + unsigned int combo = + (this->button1down ? BUTTON1DOWN : 0) | + (this->button2down ? BUTTON2DOWN : 0) | + (this->button3down ? BUTTON3DOWN : 0) | + (this->ctrldown ? CTRLDOWN : 0) | + (this->shiftdown ? SHIFTDOWN : 0); + + switch (combo) { + case 0: + if (curmode == NavigationStyle::SPINNING) { break; } + newmode = NavigationStyle::IDLE; + break; + case BUTTON1DOWN: + newmode = NavigationStyle::SELECTION; + break; + case BUTTON2DOWN: + newmode = NavigationStyle::DRAGGING; + break; + case BUTTON3DOWN: + newmode = NavigationStyle::PANNING; + break; + default: + break; + } + + if (newmode != curmode) { + this->setViewingMode(newmode); + } + + // If not handled in this class, pass on upwards in the inheritance + // hierarchy. + if (!processed) + processed = inherited::processSoEvent(ev); + return processed; +} From 5b023b0af596de62c4d27b59d8d0ea7f1e64cc50 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 5 Nov 2021 12:58:36 +0100 Subject: [PATCH 118/138] Gui: [skip ci] rename handleKeyboardEvent to processKeyboardEvent --- src/Gui/BlenderNavigationStyle.cpp | 2 +- src/Gui/CADNavigationStyle.cpp | 2 +- src/Gui/InventorNavigationStyle.cpp | 2 +- src/Gui/NavigationStyle.cpp | 104 +++++++++++++------------ src/Gui/NavigationStyle.h | 2 +- src/Gui/OpenCascadeNavigationStyle.cpp | 2 +- src/Gui/OpenSCADNavigationStyle.cpp | 2 +- src/Gui/RevitNavigationStyle.cpp | 2 +- src/Gui/TinkerCADNavigationStyle.cpp | 2 +- src/Gui/TouchpadNavigationStyle.cpp | 2 +- 10 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index 81fc6f5202..eff5b5a43d 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -117,7 +117,7 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index 53d893f0ff..563b4db7f0 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -121,7 +121,7 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index 4c57393cb4..543cd8c522 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -125,7 +125,7 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index dc66d74beb..bdb8d5e111 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -1387,55 +1387,6 @@ void NavigationStyle::syncModifierKeys(const SoEvent * const ev) } } -SbBool NavigationStyle::handleKeyboardEvent(const SoKeyboardEvent * const event, const SbVec2f & posn) -{ - SbBool processed = false; - const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; - switch (event->getKey()) { - case SoKeyboardEvent::LEFT_CONTROL: - case SoKeyboardEvent::RIGHT_CONTROL: - this->ctrldown = press; - break; - case SoKeyboardEvent::LEFT_SHIFT: - case SoKeyboardEvent::RIGHT_SHIFT: - this->shiftdown = press; - break; - case SoKeyboardEvent::LEFT_ALT: - case SoKeyboardEvent::RIGHT_ALT: - this->altdown = press; - break; - case SoKeyboardEvent::H: - processed = true; - viewer->saveHomePosition(); - break; - case SoKeyboardEvent::R: - processed = true; - viewer->resetToHomePosition(); - break; - case SoKeyboardEvent::S: - case SoKeyboardEvent::HOME: - case SoKeyboardEvent::LEFT_ARROW: - case SoKeyboardEvent::UP_ARROW: - case SoKeyboardEvent::RIGHT_ARROW: - case SoKeyboardEvent::DOWN_ARROW: - if (!this->isViewing()) - this->setViewing(true); - break; - case SoKeyboardEvent::PAGE_UP: - processed = true; - doZoom(viewer->getSoRenderManager()->getCamera(), getDelta(), posn); - break; - case SoKeyboardEvent::PAGE_DOWN: - processed = true; - doZoom(viewer->getSoRenderManager()->getCamera(), -getDelta(), posn); - break; - default: - break; - } - - return processed; -} - // The viewer is a state machine, and all changes to the current state // are made through this call. void NavigationStyle::setViewingMode(const ViewerMode newmode) @@ -1664,6 +1615,61 @@ SbBool NavigationStyle::processMotionEvent(const SoMotion3Event * const ev) return true; } +SbBool NavigationStyle::processKeyboardEvent(const SoKeyboardEvent * const event) +{ + SbBool processed = false; + const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; + switch (event->getKey()) { + case SoKeyboardEvent::LEFT_CONTROL: + case SoKeyboardEvent::RIGHT_CONTROL: + this->ctrldown = press; + break; + case SoKeyboardEvent::LEFT_SHIFT: + case SoKeyboardEvent::RIGHT_SHIFT: + this->shiftdown = press; + break; + case SoKeyboardEvent::LEFT_ALT: + case SoKeyboardEvent::RIGHT_ALT: + this->altdown = press; + break; + case SoKeyboardEvent::H: + processed = true; + viewer->saveHomePosition(); + break; + case SoKeyboardEvent::R: + processed = true; + viewer->resetToHomePosition(); + break; + case SoKeyboardEvent::S: + case SoKeyboardEvent::HOME: + case SoKeyboardEvent::LEFT_ARROW: + case SoKeyboardEvent::UP_ARROW: + case SoKeyboardEvent::RIGHT_ARROW: + case SoKeyboardEvent::DOWN_ARROW: + if (!this->isViewing()) + this->setViewing(true); + break; + case SoKeyboardEvent::PAGE_UP: + { + processed = true; + const SbVec2f posn = normalizePixelPos(event->getPosition()); + doZoom(viewer->getSoRenderManager()->getCamera(), getDelta(), posn); + break; + } + case SoKeyboardEvent::PAGE_DOWN: + { + processed = true; + const SbVec2f posn = normalizePixelPos(event->getPosition()); + doZoom(viewer->getSoRenderManager()->getCamera(), -getDelta(), posn); + break; + } + default: + break; + } + + return processed; +} + void NavigationStyle::setPopupMenuEnabled(const SbBool on) { this->menuenabled = on; diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 3ef16058d7..7a6fbfdb9d 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -152,6 +152,7 @@ public: int getViewingMode() const; virtual SbBool processEvent(const SoEvent * const ev); virtual SbBool processMotionEvent(const SoMotion3Event * const ev); + virtual SbBool processKeyboardEvent(const SoKeyboardEvent * const event); void setPopupMenuEnabled(const SbBool on); SbBool isPopupMenuEnabled(void) const; @@ -215,7 +216,6 @@ protected: void addToLog(const SbVec2s pos, const SbTime time); void syncModifierKeys(const SoEvent * const ev); - SbBool handleKeyboardEvent(const SoKeyboardEvent * const event, const SbVec2f & posn); protected: struct { // tracking mouse movement in a log diff --git a/src/Gui/OpenCascadeNavigationStyle.cpp b/src/Gui/OpenCascadeNavigationStyle.cpp index 58501d9d7b..0b79107ea4 100644 --- a/src/Gui/OpenCascadeNavigationStyle.cpp +++ b/src/Gui/OpenCascadeNavigationStyle.cpp @@ -117,7 +117,7 @@ SbBool OpenCascadeNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp index 75b34dac11..629eb0be29 100644 --- a/src/Gui/OpenSCADNavigationStyle.cpp +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -117,7 +117,7 @@ SbBool OpenSCADNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/RevitNavigationStyle.cpp b/src/Gui/RevitNavigationStyle.cpp index 3408929108..99bf448e4c 100644 --- a/src/Gui/RevitNavigationStyle.cpp +++ b/src/Gui/RevitNavigationStyle.cpp @@ -117,7 +117,7 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/TinkerCADNavigationStyle.cpp b/src/Gui/TinkerCADNavigationStyle.cpp index 524d1eb210..14b540dd7a 100644 --- a/src/Gui/TinkerCADNavigationStyle.cpp +++ b/src/Gui/TinkerCADNavigationStyle.cpp @@ -117,7 +117,7 @@ SbBool TinkerCADNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index 606412fdd8..035aa118b0 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -117,7 +117,7 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { const SoKeyboardEvent * const event = static_cast(ev); - processed = handleKeyboardEvent(event, posn); + processed = processKeyboardEvent(event); } // Mouse Button / Spaceball Button handling From b1ab5c3b8fd0243195d3ba9d24e6381bb917083d Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 5 Nov 2021 14:00:09 +0100 Subject: [PATCH 119/138] Gui: [skip ci] in MayaGestureNavigationStyle rename mousedownConsumedEvent to mousedownConsumedEvents --- src/Gui/MayaGestureNavigationStyle.cpp | 10 +++++----- src/Gui/NavigationStyle.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Gui/MayaGestureNavigationStyle.cpp b/src/Gui/MayaGestureNavigationStyle.cpp index 2a34f062e5..8c9bd83c6c 100644 --- a/src/Gui/MayaGestureNavigationStyle.cpp +++ b/src/Gui/MayaGestureNavigationStyle.cpp @@ -378,11 +378,11 @@ SbBool MayaGestureNavigationStyle::processSoEvent(const SoEvent * const ev) this->mouseMoveThresholdBroken = false; pan(viewer->getSoRenderManager()->getCamera());//set up panningplane int &cnt = this->mousedownConsumedCount; - this->mousedownConsumedEvent[cnt] = *event;//hopefully, a shallow copy is enough. There are no pointers stored in events, apparently. Will lose a subclass, though. + this->mousedownConsumedEvents[cnt] = *event;//hopefully, a shallow copy is enough. There are no pointers stored in events, apparently. Will lose a subclass, though. cnt++; assert(cnt<=2); - if(cnt>static_cast(sizeof(mousedownConsumedEvent))){ - cnt=sizeof(mousedownConsumedEvent);//we are in trouble + if(cnt>static_cast(sizeof(mousedownConsumedEvents))){ + cnt=sizeof(mousedownConsumedEvents);//we are in trouble } processed = true;//just consume this event, and wait for the move threshold to be broken to start dragging/panning } @@ -396,7 +396,7 @@ SbBool MayaGestureNavigationStyle::processSoEvent(const SoEvent * const ev) if(! processed) { //re-synthesize all previously-consumed mouseDowns, if any. They might have been re-synthesized already when threshold was broken. for( int i=0; i < this->mousedownConsumedCount; i++ ){ - inherited::processSoEvent(& (this->mousedownConsumedEvent[i]));//simulate the previously-comsumed mousedown. + inherited::processSoEvent(& (this->mousedownConsumedEvents[i]));//simulate the previously-comsumed mousedown. } this->mousedownConsumedCount = 0; processed = inherited::processSoEvent(ev);//explicitly, just for clarity that we are sending a full click sequence. @@ -441,7 +441,7 @@ SbBool MayaGestureNavigationStyle::processSoEvent(const SoEvent * const ev) //no, we are not entering navigation. //re-synthesize all previously-consumed mouseDowns, if any, and propagate this mousemove. for( int i=0; i < this->mousedownConsumedCount; i++ ){ - inherited::processSoEvent(& (this->mousedownConsumedEvent[i]));//simulate the previously-comsumed mousedown. + inherited::processSoEvent(& (this->mousedownConsumedEvents[i]));//simulate the previously-comsumed mousedown. } this->mousedownConsumedCount = 0; processed = inherited::processSoEvent(ev);//explicitly, just for clarity that we are sending a full click sequence. diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 7a6fbfdb9d..9a3b27cd38 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -372,7 +372,7 @@ protected: short mouseMoveThreshold;//setting. Minimum move required to consider it a move (in pixels). bool mouseMoveThresholdBroken;//a flag that the move threshold was surpassed since last mousedown. int mousedownConsumedCount;//a flag for remembering that a mousedown of button1/button2 was consumed. - SoMouseButtonEvent mousedownConsumedEvent[5];//the event that was consumed and is to be refired. 2 should be enough, but just for a case of the maximum 5 buttons... + SoMouseButtonEvent mousedownConsumedEvents[5];//the event that was consumed and is to be refired. 2 should be enough, but just for a case of the maximum 5 buttons... bool testMoveThreshold(const SbVec2s currentPos) const; bool thisClickIsComplex;//a flag that becomes set when a complex clicking pattern is detected (i.e., two or more mouse buttons were down at the same time). From 528ffce5932723e56daa0f36554d64250cc26984 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 5 Nov 2021 14:37:18 +0100 Subject: [PATCH 120/138] Gui: add method NavigationStyle::processClickEvent() to reduce code duplication in sub-classes --- src/Gui/BlenderNavigationStyle.cpp | 26 ++------------------ src/Gui/CADNavigationStyle.cpp | 26 ++------------------ src/Gui/InventorNavigationStyle.cpp | 26 ++------------------ src/Gui/NavigationStyle.cpp | 33 ++++++++++++++++++++++++++ src/Gui/NavigationStyle.h | 20 ++-------------- src/Gui/OpenCascadeNavigationStyle.cpp | 26 ++------------------ src/Gui/OpenSCADNavigationStyle.cpp | 26 ++------------------ src/Gui/RevitNavigationStyle.cpp | 26 ++------------------ src/Gui/TinkerCADNavigationStyle.cpp | 26 ++------------------ src/Gui/TouchpadNavigationStyle.cpp | 26 ++------------------ 10 files changed, 51 insertions(+), 210 deletions(-) diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index eff5b5a43d..563b124e86 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -146,30 +146,8 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) else if (viewer->isEditing() && (this->currentmode == NavigationStyle::SPINNING)) { processed = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index 563b4db7f0..c9e351fb2b 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -158,30 +158,8 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) else if (viewer->isEditing() && (this->currentmode == NavigationStyle::SPINNING)) { processed = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index 543cd8c522..b9bfe2986d 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -179,30 +179,8 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) processed = true; this->lockrecenter = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index bdb8d5e111..ccfefe15ea 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -1670,6 +1670,39 @@ SbBool NavigationStyle::processKeyboardEvent(const SoKeyboardEvent * const event return processed; } +SbBool NavigationStyle::processClickEvent(const SoMouseButtonEvent * const event) +{ + // issue #0002433: avoid to swallow the UP event if down the + // scene graph somewhere a dialog gets opened + SbBool processed = false; + const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; + if (press) { + SbTime tmp = (event->getTime() - mouseDownConsumedEvent.getTime()); + float dci = (float)QApplication::doubleClickInterval()/1000.0f; + // a double-click? + if (tmp.getValue() < dci) { + mouseDownConsumedEvent = *event; + mouseDownConsumedEvent.setTime(event->getTime()); + processed = true; + } + else { + mouseDownConsumedEvent.setTime(event->getTime()); + // 'ANY' is used to mark that we don't know yet if it will + // be a double-click event. + mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); + } + } + else if (!press) { + if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { + // now handle the postponed event + NavigationStyle::processSoEvent(&mouseDownConsumedEvent); + mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); + } + } + + return processed; +} + void NavigationStyle::setPopupMenuEnabled(const SbBool on) { this->menuenabled = on; diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 9a3b27cd38..a8a5f58e4f 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -153,6 +153,7 @@ public: virtual SbBool processEvent(const SoEvent * const ev); virtual SbBool processMotionEvent(const SoMotion3Event * const ev); virtual SbBool processKeyboardEvent(const SoKeyboardEvent * const event); + virtual SbBool processClickEvent(const SoMouseButtonEvent * const event); void setPopupMenuEnabled(const SbBool on); SbBool isPopupMenuEnabled(void) const; @@ -227,6 +228,7 @@ protected: View3DInventorViewer* viewer; ViewerMode currentmode; + SoMouseButtonEvent mouseDownConsumedEvent; SbVec2f lastmouseposition; SbVec2s globalPos; SbVec2s localPos; @@ -296,9 +298,6 @@ public: protected: SbBool processSoEvent(const SoEvent * const ev); - -private: - SoMouseButtonEvent mouseDownConsumedEvent; }; class GuiExport CADNavigationStyle : public UserNavigationStyle { @@ -316,7 +315,6 @@ protected: private: SbBool lockButton1; - SoMouseButtonEvent mouseDownConsumedEvent; }; class GuiExport RevitNavigationStyle : public UserNavigationStyle { @@ -334,7 +332,6 @@ protected: private: SbBool lockButton1; - SoMouseButtonEvent mouseDownConsumedEvent; }; class GuiExport BlenderNavigationStyle : public UserNavigationStyle { @@ -352,7 +349,6 @@ protected: private: SbBool lockButton1; - SoMouseButtonEvent mouseDownConsumedEvent; }; class GuiExport MayaGestureNavigationStyle : public UserNavigationStyle { @@ -391,9 +387,6 @@ public: protected: SbBool processSoEvent(const SoEvent * const ev); - -private: - SoMouseButtonEvent mouseDownConsumedEvent; }; class GuiExport OpenCascadeNavigationStyle : public UserNavigationStyle { @@ -408,9 +401,6 @@ public: protected: SbBool processSoEvent(const SoEvent * const ev); - -private: - SoMouseButtonEvent mouseDownConsumedEvent; }; class GuiExport OpenSCADNavigationStyle : public UserNavigationStyle { @@ -425,9 +415,6 @@ public: protected: SbBool processSoEvent(const SoEvent * const ev); - -private: - SoMouseButtonEvent mouseDownConsumedEvent; }; class GuiExport TinkerCADNavigationStyle : public UserNavigationStyle { @@ -442,9 +429,6 @@ public: protected: SbBool processSoEvent(const SoEvent * const ev); - -private: - SoMouseButtonEvent mouseDownConsumedEvent; }; } // namespace Gui diff --git a/src/Gui/OpenCascadeNavigationStyle.cpp b/src/Gui/OpenCascadeNavigationStyle.cpp index 0b79107ea4..1cbf7b322e 100644 --- a/src/Gui/OpenCascadeNavigationStyle.cpp +++ b/src/Gui/OpenCascadeNavigationStyle.cpp @@ -146,30 +146,8 @@ SbBool OpenCascadeNavigationStyle::processSoEvent(const SoEvent * const ev) else if (viewer->isEditing() && (this->currentmode == NavigationStyle::SPINNING)) { processed = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: diff --git a/src/Gui/OpenSCADNavigationStyle.cpp b/src/Gui/OpenSCADNavigationStyle.cpp index 629eb0be29..c1c1ee1e6b 100644 --- a/src/Gui/OpenSCADNavigationStyle.cpp +++ b/src/Gui/OpenSCADNavigationStyle.cpp @@ -146,30 +146,8 @@ SbBool OpenSCADNavigationStyle::processSoEvent(const SoEvent * const ev) else if (viewer->isEditing() && (curmode == NavigationStyle::SPINNING)) { processed = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: diff --git a/src/Gui/RevitNavigationStyle.cpp b/src/Gui/RevitNavigationStyle.cpp index 99bf448e4c..6bc4ee8ced 100644 --- a/src/Gui/RevitNavigationStyle.cpp +++ b/src/Gui/RevitNavigationStyle.cpp @@ -146,30 +146,8 @@ SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev) else if (viewer->isEditing() && (this->currentmode == NavigationStyle::SPINNING)) { processed = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: diff --git a/src/Gui/TinkerCADNavigationStyle.cpp b/src/Gui/TinkerCADNavigationStyle.cpp index 14b540dd7a..50b28e47ad 100644 --- a/src/Gui/TinkerCADNavigationStyle.cpp +++ b/src/Gui/TinkerCADNavigationStyle.cpp @@ -138,30 +138,8 @@ SbBool TinkerCADNavigationStyle::processSoEvent(const SoEvent * const ev) else if (viewer->isEditing() && (curmode == NavigationStyle::SPINNING)) { processed = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index 035aa118b0..dbb2dc8b6c 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -146,30 +146,8 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) else if (viewer->isEditing() && (this->currentmode == NavigationStyle::SPINNING)) { processed = true; } - // issue #0002433: avoid to swallow the UP event if down the - // scene graph somewhere a dialog gets opened - else if (press) { - SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime()); - float dci = (float)QApplication::doubleClickInterval()/1000.0f; - // a double-click? - if (tmp.getValue() < dci) { - mouseDownConsumedEvent = *event; - mouseDownConsumedEvent.setTime(ev->getTime()); - processed = true; - } - else { - mouseDownConsumedEvent.setTime(ev->getTime()); - // 'ANY' is used to mark that we don't know yet if it will - // be a double-click event. - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } - } - else if (!press) { - if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) { - // now handle the postponed event - inherited::processSoEvent(&mouseDownConsumedEvent); - mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY); - } + else { + processed = processClickEvent(event); } break; case SoMouseButtonEvent::BUTTON2: From 4a1c1c70049c68de08035eab96b523596934ab9b Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 5 Nov 2021 18:16:19 +0100 Subject: [PATCH 121/138] C++11: modernize use nullptr (replaces NULL or 0) --- src/Gui/NavigationStyle.cpp | 65 +++++------- src/Gui/NavigationStyle.h | 22 ++-- src/Gui/Quarter/ContextMenu.cpp | 8 +- src/Gui/Quarter/ContextMenu.h | 2 +- src/Gui/Quarter/DragDropHandler.cpp | 4 +- src/Gui/Quarter/EventFilter.cpp | 2 +- src/Gui/Quarter/ImageReader.cpp | 4 +- src/Gui/Quarter/ImageReader.h | 4 +- src/Gui/Quarter/InputDevice.cpp | 2 +- src/Gui/Quarter/InteractionMode.cpp | 4 +- src/Gui/Quarter/InteractionMode.h | 4 +- src/Gui/Quarter/Keyboard.cpp | 4 +- src/Gui/Quarter/KeyboardP.cpp | 10 +- src/Gui/Quarter/KeyboardP.h | 4 +- src/Gui/Quarter/Mouse.cpp | 6 +- src/Gui/Quarter/NativeEvent.cpp | 2 +- src/Gui/Quarter/QtCoinCompatibility.cpp | 2 +- src/Gui/Quarter/Quarter.cpp | 6 +- src/Gui/Quarter/Quarter.h | 2 +- src/Gui/Quarter/QuarterP.cpp | 14 +-- src/Gui/Quarter/QuarterWidget.cpp | 100 +++++++++--------- src/Gui/Quarter/QuarterWidget.h | 64 +++++------ src/Gui/Quarter/QuarterWidgetP.cpp | 34 +++--- src/Gui/Quarter/QuarterWidgetP.h | 10 +- src/Gui/Quarter/SensorManager.cpp | 12 +-- src/Gui/Quarter/SensorManager.h | 10 +- src/Gui/Quarter/SignalThread.cpp | 8 +- src/Gui/Quarter/SignalThread.h | 10 +- src/Gui/Quarter/SoQTQuarterAdaptor.cpp | 30 +++--- src/Gui/Quarter/SoQTQuarterAdaptor.h | 44 ++++---- src/Gui/Quarter/SpaceNavigatorDevice.cpp | 2 +- src/Gui/Quarter/devices/InputDevice.h | 2 +- src/Gui/Quarter/devices/Keyboard.h | 2 +- src/Gui/Quarter/devices/Mouse.h | 2 +- .../Quarter/devices/SpaceNavigatorDevice.h | 2 +- src/Gui/Quarter/eventhandlers/EventFilter.h | 2 +- 36 files changed, 247 insertions(+), 258 deletions(-) diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index ccfefe15ea..c75ad3869a 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -64,7 +64,7 @@ struct NavigationStyleP { { this->animationsteps = 0; this->animationdelta = 0; - this->animsensor = 0; + this->animsensor = nullptr; this->sensitivity = 2.0f; this->resetcursorpos = false; this->rotationCenterFound = false; @@ -173,7 +173,7 @@ const Base::Type& NavigationStyleEvent::style() const TYPESYSTEM_SOURCE_ABSTRACT(Gui::NavigationStyle,Base::BaseClass) -NavigationStyle::NavigationStyle() : viewer(0), mouseSelection(0) +NavigationStyle::NavigationStyle() : viewer(nullptr), mouseSelection(nullptr) { PRIVATE(this) = new NavigationStyleP(); PRIVATE(this)->animsensor = new SoTimerSensor(NavigationStyleP::viewAnimationCB, this); @@ -261,17 +261,17 @@ void NavigationStyle::finalize() delete[] this->log.time; } -void NavigationStyle::interactiveCountInc(void) +void NavigationStyle::interactiveCountInc() { viewer->interactiveCountInc(); } -void NavigationStyle::interactiveCountDec(void) +void NavigationStyle::interactiveCountDec() { viewer->interactiveCountDec(); } -int NavigationStyle::getInteractiveCount(void) const +int NavigationStyle::getInteractiveCount() const { return viewer->getInteractiveCount(); } @@ -288,7 +288,7 @@ NavigationStyle::OrbitStyle NavigationStyle::getOrbitStyle() const return NavigationStyle::OrbitStyle(projector->getOrbitStyle()); } -SbBool NavigationStyle::isViewing(void) const +SbBool NavigationStyle::isViewing() const { return viewer->isViewing(); } @@ -298,7 +298,7 @@ void NavigationStyle::setViewing(SbBool enable) viewer->setViewing(enable); } -SbBool NavigationStyle::isSeekMode(void) const +SbBool NavigationStyle::isSeekMode() const { return viewer->isSeekMode(); } @@ -321,7 +321,7 @@ void NavigationStyle::seekToPoint(const SbVec3f& scenepos) SbBool NavigationStyle::lookAtPoint(const SbVec2s screenpos) { SoCamera* cam = viewer->getSoRenderManager()->getCamera(); - if (cam == 0) return false; + if (cam == nullptr) return false; SoRayPickAction rpaction(viewer->getSoRenderManager()->getViewportRegion()); rpaction.setPoint(screenpos); @@ -343,7 +343,7 @@ SbBool NavigationStyle::lookAtPoint(const SbVec2s screenpos) void NavigationStyle::lookAtPoint(const SbVec3f& pos) { SoCamera* cam = viewer->getSoRenderManager()->getCamera(); - if (cam == 0) return; + if (cam == nullptr) return; PRIVATE(this)->rotationCenterFound = false; // Find global coordinates of focal point. @@ -401,7 +401,7 @@ void NavigationStyle::lookAtPoint(const SbVec3f& pos) void NavigationStyle::setCameraOrientation(const SbRotation& rot, SbBool moveToCenter) { SoCamera* cam = viewer->getSoRenderManager()->getCamera(); - if (cam == 0) return; + if (cam == nullptr) return; // Find global coordinates of focal point. SbVec3f direction; @@ -611,7 +611,7 @@ void NavigationStyle::viewAll() */ void NavigationStyle::reorientCamera(SoCamera * cam, const SbRotation & rot) { - if (cam == NULL) return; + if (cam == nullptr) return; // Find global coordinates of focal point. SbVec3f direction; @@ -630,7 +630,7 @@ void NavigationStyle::reorientCamera(SoCamera * cam, const SbRotation & rot) void NavigationStyle::panCamera(SoCamera * cam, float aspectratio, const SbPlane & panplane, const SbVec2f & currpos, const SbVec2f & prevpos) { - if (cam == NULL) return; // can happen for empty scenegraph + if (cam == nullptr) return; // can happen for empty scenegraph if (currpos == prevpos) return; // useless invocation @@ -659,7 +659,7 @@ void NavigationStyle::pan(SoCamera* camera) // The plane we're projecting the mouse coordinates to get 3D // coordinates should stay the same during the whole pan // operation, so we should calculate this value here. - if (camera == NULL) { // can happen for empty scenegraph + if (camera == nullptr) { // can happen for empty scenegraph this->panningplane = SbPlane(SbVec3f(0, 0, 1), 0); } else { @@ -689,7 +689,7 @@ void NavigationStyle::panToCenter(const SbPlane & pplane, const SbVec2f & currpo */ void NavigationStyle::zoom(SoCamera * cam, float diffvalue) { - if (cam == NULL) return; // can happen for empty scenegraph + if (cam == nullptr) return; // can happen for empty scenegraph SoType t = cam->getTypeId(); SbName tname = t.getName(); @@ -870,7 +870,7 @@ void NavigationStyle::setRotationCenter(const SbVec3f& cnt) SbVec3f NavigationStyle::getFocalPoint() const { SoCamera* cam = viewer->getSoRenderManager()->getCamera(); - if (cam == 0) + if (cam == nullptr) return SbVec3f(0,0,0); // Find global coordinates of focal point. @@ -887,7 +887,7 @@ SbVec3f NavigationStyle::getFocalPoint() const void NavigationStyle::spin(const SbVec2f & pointerpos) { if (this->log.historysize < 2) return; - assert(this->spinprojector != NULL); + assert(this->spinprojector != nullptr); const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); SbVec2s glsize(vp.getViewportSizePixels()); @@ -965,7 +965,7 @@ void NavigationStyle::spin(const SbVec2f & pointerpos) * \param prevpos previous normalized position of mouse pointer */ void NavigationStyle::spin_simplified(SoCamera* cam, SbVec2f curpos, SbVec2f prevpos){ - assert(this->spinprojector != NULL); + assert(this->spinprojector != nullptr); // 0000333: Turntable camera rotation SbMatrix mat; @@ -1163,7 +1163,7 @@ NavigationStyle::setAnimationEnabled(const SbBool enable) */ SbBool -NavigationStyle::isAnimationEnabled(void) const +NavigationStyle::isAnimationEnabled() const { return this->spinanimatingallowed; } @@ -1172,7 +1172,7 @@ NavigationStyle::isAnimationEnabled(void) const Query if the model in the viewer is currently in spinning mode after a user drag. */ -SbBool NavigationStyle::isAnimating(void) const +SbBool NavigationStyle::isAnimating() const { return this->currentmode == NavigationStyle::SPINNING; } @@ -1195,7 +1195,7 @@ void NavigationStyle::startAnimating(const SbVec3f& axis, float velocity) this->spinRotation = rot; } -void NavigationStyle::stopAnimating(void) +void NavigationStyle::stopAnimating() { if (this->currentmode != NavigationStyle::SPINNING) { return; @@ -1321,7 +1321,7 @@ void NavigationStyle::stopSelection() if (mouseSelection) { mouseSelection->releaseMouseModel(); delete mouseSelection; - mouseSelection = 0; + mouseSelection = nullptr; } } @@ -1367,7 +1367,7 @@ void NavigationStyle::addToLog(const SbVec2s pos, const SbTime time) // This method "clears" the mouse location log, used for spin // animation calculations. -void NavigationStyle::clearLog(void) +void NavigationStyle::clearLog() { this->log.historysize = 0; } @@ -1461,14 +1461,14 @@ SbBool NavigationStyle::processEvent(const SoEvent * const ev) pcPolygon = mouseSelection->getPositions(); selectedRole = mouseSelection->selectedRole(); delete mouseSelection; - mouseSelection = 0; + mouseSelection = nullptr; syncWithEvent(ev); return NavigationStyle::processSoEvent(ev); } else if (hd==AbstractMouseSelection::Cancel) { pcPolygon.clear(); delete mouseSelection; - mouseSelection = 0; + mouseSelection = nullptr; syncWithEvent(ev); return NavigationStyle::processSoEvent(ev); } @@ -1495,11 +1495,8 @@ SbBool NavigationStyle::processEvent(const SoEvent * const ev) SbBool NavigationStyle::processSoEvent(const SoEvent * const ev) { - const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); - const SbVec2s size(vp.getViewportSizePixels()); const SbVec2s pos(ev->getPosition()); - const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1), - (float) pos[1] / (float) std::max((int)(size[1] - 1), 1)); + const SbVec2f posn = normalizePixelPos(pos); bool processed = false; //handle mouse wheel zoom @@ -1529,15 +1526,7 @@ void NavigationStyle::syncWithEvent(const SoEvent * const ev) // Mismatches in state of the modifier keys happens if the user // presses or releases them outside the viewer window. - if (this->ctrldown != ev->wasCtrlDown()) { - this->ctrldown = ev->wasCtrlDown(); - } - if (this->shiftdown != ev->wasShiftDown()) { - this->shiftdown = ev->wasShiftDown(); - } - if (this->altdown != ev->wasAltDown()) { - this->altdown = ev->wasAltDown(); - } + syncModifierKeys(ev); // Keyboard handling if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { @@ -1708,7 +1697,7 @@ void NavigationStyle::setPopupMenuEnabled(const SbBool on) this->menuenabled = on; } -SbBool NavigationStyle::isPopupMenuEnabled(void) const +SbBool NavigationStyle::isPopupMenuEnabled() const { return this->menuenabled; } diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index a8a5f58e4f..f35e61f37a 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -116,11 +116,11 @@ public: void setViewer(View3DInventorViewer*); void setAnimationEnabled(const SbBool enable); - SbBool isAnimationEnabled(void) const; + SbBool isAnimationEnabled() const; void startAnimating(const SbVec3f& axis, float velocity); - void stopAnimating(void); - SbBool isAnimating(void) const; + void stopAnimating(); + SbBool isAnimating() const; void setSensitivity(float); float getSensitivity() const; @@ -156,14 +156,14 @@ public: virtual SbBool processClickEvent(const SoMouseButtonEvent * const event); void setPopupMenuEnabled(const SbBool on); - SbBool isPopupMenuEnabled(void) const; + SbBool isPopupMenuEnabled() const; void startSelection(AbstractMouseSelection*); void startSelection(SelectionMode = Lasso); void abortSelection(); void stopSelection(); SbBool isSelecting() const; - const std::vector& getPolygon(SelectionRole* role=0) const; + const std::vector& getPolygon(SelectionRole* role=nullptr) const; void setOrbitStyle(OrbitStyle style); OrbitStyle getOrbitStyle() const; @@ -172,13 +172,13 @@ protected: void initialize(); void finalize(); - void interactiveCountInc(void); - void interactiveCountDec(void); - int getInteractiveCount(void) const; + void interactiveCountInc(); + void interactiveCountDec(); + int getInteractiveCount() const; - SbBool isViewing(void) const; + SbBool isViewing() const; void setViewing(SbBool); - SbBool isSeekMode(void) const; + SbBool isSeekMode() const; void setSeekMode(SbBool enable); SbBool seekToPoint(const SbVec2s screenpos); void seekToPoint(const SbVec3f& scenepos); @@ -213,7 +213,7 @@ protected: void syncWithEvent(const SoEvent * const ev); virtual void openPopupMenu(const SbVec2s& position); - void clearLog(void); + void clearLog(); void addToLog(const SbVec2s pos, const SbTime time); void syncModifierKeys(const SoEvent * const ev); diff --git a/src/Gui/Quarter/ContextMenu.cpp b/src/Gui/Quarter/ContextMenu.cpp index dc28d9500b..88c3535a5e 100644 --- a/src/Gui/Quarter/ContextMenu.cpp +++ b/src/Gui/Quarter/ContextMenu.cpp @@ -58,9 +58,9 @@ ContextMenu::ContextMenu(QuarterWidget * quarterwidget) SoRenderManager * sorendermanager = quarterwidget->getSoRenderManager(); - QActionGroup * rendermodegroup = NULL; - QActionGroup * stereomodegroup = NULL; - QActionGroup * transparencytypegroup = NULL; + QActionGroup * rendermodegroup = nullptr; + QActionGroup * stereomodegroup = nullptr; + QActionGroup * transparencytypegroup = nullptr; foreach (QAction * action, quarterwidget->renderModeActions()) { if (!rendermodegroup) { @@ -138,7 +138,7 @@ ContextMenu::~ContextMenu() } QMenu * -ContextMenu::getMenu(void) const +ContextMenu::getMenu() const { return this->contextmenu; } diff --git a/src/Gui/Quarter/ContextMenu.h b/src/Gui/Quarter/ContextMenu.h index a9965001e3..13603e194d 100644 --- a/src/Gui/Quarter/ContextMenu.h +++ b/src/Gui/Quarter/ContextMenu.h @@ -48,7 +48,7 @@ public: ContextMenu(QuarterWidget * quarterwidget); ~ContextMenu(); - QMenu * getMenu(void) const; + QMenu * getMenu() const; public Q_SLOTS: void changeRenderMode(QAction * action); diff --git a/src/Gui/Quarter/DragDropHandler.cpp b/src/Gui/Quarter/DragDropHandler.cpp index 55b4d9ee43..796295cde8 100644 --- a/src/Gui/Quarter/DragDropHandler.cpp +++ b/src/Gui/Quarter/DragDropHandler.cpp @@ -50,7 +50,7 @@ #include #include -#include +#include namespace SIM { namespace Coin3D { namespace Quarter { @@ -152,7 +152,7 @@ DragDropHandlerP::dropEvent(QDropEvent * event) // attempt to import it root = SoDB::readAll(&in); - if (root == NULL) return; + if (root == nullptr) return; // set new scenegraph this->quarterwidget->setSceneGraph(root); diff --git a/src/Gui/Quarter/EventFilter.cpp b/src/Gui/Quarter/EventFilter.cpp index 695159ceea..faf056d375 100644 --- a/src/Gui/Quarter/EventFilter.cpp +++ b/src/Gui/Quarter/EventFilter.cpp @@ -177,7 +177,7 @@ EventFilter::eventFilter(QObject * obj, QEvent * qevent) Returns mouse position in global coordinates */ const QPoint & -EventFilter::globalMousePosition(void) const +EventFilter::globalMousePosition() const { return PRIVATE(this)->globalmousepos; } diff --git a/src/Gui/Quarter/ImageReader.cpp b/src/Gui/Quarter/ImageReader.cpp index 9d8336e261..75c121db98 100644 --- a/src/Gui/Quarter/ImageReader.cpp +++ b/src/Gui/Quarter/ImageReader.cpp @@ -39,12 +39,12 @@ using namespace SIM::Coin3D::Quarter; -ImageReader::ImageReader(void) +ImageReader::ImageReader() { SbImage::addReadImageCB(ImageReader::readImageCB, this); } -ImageReader::~ImageReader(void) +ImageReader::~ImageReader() { SbImage::removeReadImageCB(ImageReader::readImageCB, this); } diff --git a/src/Gui/Quarter/ImageReader.h b/src/Gui/Quarter/ImageReader.h index a0da5ad526..9d8ad2c186 100644 --- a/src/Gui/Quarter/ImageReader.h +++ b/src/Gui/Quarter/ImageReader.h @@ -43,8 +43,8 @@ namespace SIM { namespace Coin3D { namespace Quarter { class ImageReader { public: - ImageReader(void); - ~ImageReader(void); + ImageReader(); + ~ImageReader(); SbBool readImage(const SbString & filename, SbImage & image) const; diff --git a/src/Gui/Quarter/InputDevice.cpp b/src/Gui/Quarter/InputDevice.cpp index a29d74d7fa..b2cc008b6a 100644 --- a/src/Gui/Quarter/InputDevice.cpp +++ b/src/Gui/Quarter/InputDevice.cpp @@ -48,7 +48,7 @@ using namespace SIM::Coin3D::Quarter; devices. */ -InputDevice::InputDevice(void) : quarter(nullptr) +InputDevice::InputDevice() : quarter(nullptr) { this->mousepos = SbVec2s(0, 0); } diff --git a/src/Gui/Quarter/InteractionMode.cpp b/src/Gui/Quarter/InteractionMode.cpp index d9a0375bcd..f7a577261f 100644 --- a/src/Gui/Quarter/InteractionMode.cpp +++ b/src/Gui/Quarter/InteractionMode.cpp @@ -34,7 +34,7 @@ InteractionMode::setEnabled(bool yes) } bool -InteractionMode::enabled(void) const +InteractionMode::enabled() const { return this->isenabled; } @@ -62,7 +62,7 @@ InteractionMode::setOn(bool on) } bool -InteractionMode::on(void) const +InteractionMode::on() const { return this->altkeydown; } diff --git a/src/Gui/Quarter/InteractionMode.h b/src/Gui/Quarter/InteractionMode.h index 0397b3290f..d2562844b8 100644 --- a/src/Gui/Quarter/InteractionMode.h +++ b/src/Gui/Quarter/InteractionMode.h @@ -54,10 +54,10 @@ public: virtual ~InteractionMode(); void setEnabled(bool yes); - bool enabled(void) const; + bool enabled() const; void setOn(bool on); - bool on(void) const; + bool on() const; protected: virtual bool eventFilter(QObject *, QEvent * event); diff --git a/src/Gui/Quarter/Keyboard.cpp b/src/Gui/Quarter/Keyboard.cpp index b7fbf9f028..e16e4a62f3 100644 --- a/src/Gui/Quarter/Keyboard.cpp +++ b/src/Gui/Quarter/Keyboard.cpp @@ -55,7 +55,7 @@ using namespace SIM::Coin3D::Quarter; #define PRIVATE(obj) obj->pimpl -Keyboard::Keyboard(void) +Keyboard::Keyboard() { PRIVATE(this) = new KeyboardP(this); } @@ -81,7 +81,7 @@ Keyboard::translateEvent(QEvent * event) case QEvent::KeyRelease: return PRIVATE(this)->keyEvent((QKeyEvent *) event); default: - return NULL; + return nullptr; } } diff --git a/src/Gui/Quarter/KeyboardP.cpp b/src/Gui/Quarter/KeyboardP.cpp index 6854ffae7d..1cbc87907c 100644 --- a/src/Gui/Quarter/KeyboardP.cpp +++ b/src/Gui/Quarter/KeyboardP.cpp @@ -44,7 +44,7 @@ KeyboardP::KeyboardP(Keyboard * publ) PUBLIC(this) = publ; this->keyboard = new SoKeyboardEvent; - if (keyboardmap == NULL) { + if (keyboardmap == nullptr) { keyboardmap = new KeyMap; keypadmap = new KeyMap; this->initKeyMap(); @@ -57,7 +57,7 @@ KeyboardP::~KeyboardP() } bool -KeyboardP::debugKeyEvents(void) +KeyboardP::debugKeyEvents() { const char * env = coin_getenv("QUARTER_DEBUG_KEYEVENTS"); return env && (atoi(env) > 0); @@ -103,11 +103,11 @@ KeyboardP::keyEvent(QKeyEvent * qevent) return this->keyboard; } -KeyboardP::KeyMap * KeyboardP::keyboardmap = NULL; -KeyboardP::KeyMap * KeyboardP::keypadmap = NULL; +KeyboardP::KeyMap * KeyboardP::keyboardmap = nullptr; +KeyboardP::KeyMap * KeyboardP::keypadmap = nullptr; void -KeyboardP::initKeyMap(void) +KeyboardP::initKeyMap() { // keyboard keyboardmap->insert(Qt::Key_Shift, SoKeyboardEvent::LEFT_SHIFT); diff --git a/src/Gui/Quarter/KeyboardP.h b/src/Gui/Quarter/KeyboardP.h index e5dfe6a998..fb0e5d2cbd 100644 --- a/src/Gui/Quarter/KeyboardP.h +++ b/src/Gui/Quarter/KeyboardP.h @@ -49,8 +49,8 @@ public: ~KeyboardP(); const SoEvent * keyEvent(QKeyEvent * event); - void initKeyMap(void); - static bool debugKeyEvents(void); + void initKeyMap(); + static bool debugKeyEvents(); typedef QMap KeyMap; static KeyMap * keyboardmap; diff --git a/src/Gui/Quarter/Mouse.cpp b/src/Gui/Quarter/Mouse.cpp index 6667dee6bd..fa182955fc 100644 --- a/src/Gui/Quarter/Mouse.cpp +++ b/src/Gui/Quarter/Mouse.cpp @@ -95,7 +95,7 @@ using namespace SIM::Coin3D::Quarter; #define PRIVATE(obj) obj->pimpl #define PUBLIC(obj) obj->publ -Mouse::Mouse(void) +Mouse::Mouse() { PRIVATE(this) = new MouseP(this); } @@ -131,9 +131,9 @@ Mouse::translateEvent(QEvent * event) return PRIVATE(this)->mouseWheelEvent((QWheelEvent *) event); case QEvent::Resize: PRIVATE(this)->resizeEvent((QResizeEvent *) event); - return NULL; + return nullptr; default: - return NULL; + return nullptr; } } diff --git a/src/Gui/Quarter/NativeEvent.cpp b/src/Gui/Quarter/NativeEvent.cpp index def9b09c02..790f21cd68 100644 --- a/src/Gui/Quarter/NativeEvent.cpp +++ b/src/Gui/Quarter/NativeEvent.cpp @@ -55,7 +55,7 @@ NativeEvent::getEvent() const NativeEvent::NativeEvent() : QEvent(QEvent::User) { - this->rawevent = NULL; + this->rawevent = nullptr; } #endif // !HAVE_SPACENAV_LIB diff --git a/src/Gui/Quarter/QtCoinCompatibility.cpp b/src/Gui/Quarter/QtCoinCompatibility.cpp index 3ffd38cf81..c62431a462 100644 --- a/src/Gui/Quarter/QtCoinCompatibility.cpp +++ b/src/Gui/Quarter/QtCoinCompatibility.cpp @@ -22,7 +22,7 @@ QtCoinCompatibility::QImageToSbImage(const QImage & image, SbImage & sbimage) } SbVec2s size((short) w, (short) h); - sbimage.setValue(size, c, NULL); + sbimage.setValue(size, c, nullptr); unsigned char * buffer = sbimage.getValue(size, c); if (c == 1) { diff --git a/src/Gui/Quarter/Quarter.cpp b/src/Gui/Quarter/Quarter.cpp index 5d48a0f64a..976b02e478 100644 --- a/src/Gui/Quarter/Quarter.cpp +++ b/src/Gui/Quarter/Quarter.cpp @@ -152,7 +152,7 @@ using namespace SIM::Coin3D::Quarter; -static QuarterP * self = NULL; +static QuarterP * self = nullptr; /*! initialize Quarter, and implicitly Coin @@ -182,14 +182,14 @@ Quarter::init(bool initCoin) clean up resources */ void -Quarter::clean(void) +Quarter::clean() { COMPILE_ONLY_BEFORE(2,0,0,"Should not be encapsulated in double Quarter namespace"); assert(self); bool initCoin = self->initCoin; delete self; - self = NULL; + self = nullptr; if (initCoin) { // SoDB::finish() will clean up everything that has been diff --git a/src/Gui/Quarter/Quarter.h b/src/Gui/Quarter/Quarter.h index 1853b5118e..22e538e7c2 100644 --- a/src/Gui/Quarter/Quarter.h +++ b/src/Gui/Quarter/Quarter.h @@ -39,7 +39,7 @@ namespace SIM { namespace Coin3D { namespace Quarter { namespace Quarter { void QUARTER_DLL_API init(bool initCoin = true); - void QUARTER_DLL_API clean(void); + void QUARTER_DLL_API clean(); void QUARTER_DLL_API setTimerEpsilon(double sec); } diff --git a/src/Gui/Quarter/QuarterP.cpp b/src/Gui/Quarter/QuarterP.cpp index fbfd30e380..f227527065 100644 --- a/src/Gui/Quarter/QuarterP.cpp +++ b/src/Gui/Quarter/QuarterP.cpp @@ -4,13 +4,13 @@ #include "KeyboardP.h" using namespace SIM::Coin3D::Quarter; -QuarterP::StateCursorMap * QuarterP::statecursormap = NULL; +QuarterP::StateCursorMap * QuarterP::statecursormap = nullptr; -QuarterP::QuarterP(void) +QuarterP::QuarterP() { this->sensormanager = new SensorManager; this->imagereader = new ImageReader; - assert(QuarterP::statecursormap == NULL); + assert(QuarterP::statecursormap == nullptr); QuarterP::statecursormap = new StateCursorMap; } @@ -20,17 +20,17 @@ QuarterP::~QuarterP() delete this->imagereader; delete this->sensormanager; - assert(QuarterP::statecursormap != NULL); + assert(QuarterP::statecursormap != nullptr); delete QuarterP::statecursormap; // FIXME: Why not use an atexit mechanism for this? - if (KeyboardP::keyboardmap != NULL) { + if (KeyboardP::keyboardmap != nullptr) { KeyboardP::keyboardmap->clear(); KeyboardP::keypadmap->clear(); delete KeyboardP::keyboardmap; delete KeyboardP::keypadmap; - KeyboardP::keyboardmap = NULL; - KeyboardP::keypadmap = NULL; + KeyboardP::keyboardmap = nullptr; + KeyboardP::keypadmap = nullptr; } diff --git a/src/Gui/Quarter/QuarterWidget.cpp b/src/Gui/Quarter/QuarterWidget.cpp index e7034760b9..b3376d851c 100644 --- a/src/Gui/Quarter/QuarterWidget.cpp +++ b/src/Gui/Quarter/QuarterWidget.cpp @@ -52,7 +52,7 @@ #pragma warning(disable : 4267) #endif -#include +#include #include #include @@ -152,7 +152,7 @@ class CustomGLWidget : public QOpenGLWidget { public: QSurfaceFormat myFormat; - CustomGLWidget(const QSurfaceFormat& format, QWidget* parent = 0, const QOpenGLWidget* shareWidget = 0, Qt::WindowFlags f = Qt::WindowFlags()) + CustomGLWidget(const QSurfaceFormat& format, QWidget* parent = nullptr, const QOpenGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags()) : QOpenGLWidget(parent, f), myFormat(format) { Q_UNUSED(shareWidget); @@ -308,7 +308,7 @@ QuarterWidget::constructor(const QtGLFormat & format, const QtGLWidget * sharewi PRIVATE(this)->eventfilter = new EventFilter(this); PRIVATE(this)->interactionmode = new InteractionMode(this); - PRIVATE(this)->currentStateMachine = NULL; + PRIVATE(this)->currentStateMachine = nullptr; PRIVATE(this)->headlight = new SoDirectionalLight; PRIVATE(this)->headlight->ref(); @@ -364,10 +364,10 @@ QuarterWidget::~QuarterWidget() delete PRIVATE(this)->currentStateMachine; } PRIVATE(this)->headlight->unref(); - PRIVATE(this)->headlight = NULL; - this->setSceneGraph(NULL); - this->setSoRenderManager(NULL); - this->setSoEventManager(NULL); + PRIVATE(this)->headlight = nullptr; + this->setSceneGraph(nullptr); + this->setSoRenderManager(nullptr); + this->setSoEventManager(nullptr); delete PRIVATE(this)->eventfilter; delete PRIVATE(this); } @@ -418,7 +418,7 @@ QuarterWidget::setHeadlightEnabled(bool onoff) Returns true if the headlight is on, false if it is off */ bool -QuarterWidget::headlightEnabled(void) const +QuarterWidget::headlightEnabled() const { return PRIVATE(this)->headlight->on.getValue(); } @@ -427,7 +427,7 @@ QuarterWidget::headlightEnabled(void) const Returns the light used for the headlight. */ SoDirectionalLight * -QuarterWidget::getHeadlight(void) const +QuarterWidget::getHeadlight() const { return PRIVATE(this)->headlight; } @@ -452,7 +452,7 @@ QuarterWidget::setClearZBuffer(bool onoff) Returns true if the z buffer is cleared before rendering. */ bool -QuarterWidget::clearZBuffer(void) const +QuarterWidget::clearZBuffer() const { return PRIVATE(this)->clearzbuffer; } @@ -477,7 +477,7 @@ QuarterWidget::setClearWindow(bool onoff) Returns true if the rendering buffer is cleared before rendering. */ bool -QuarterWidget::clearWindow(void) const +QuarterWidget::clearWindow() const { return PRIVATE(this)->clearwindow; } @@ -503,7 +503,7 @@ QuarterWidget::setInteractionModeEnabled(bool onoff) Returns true if interaction mode is enabled, false otherwise. */ bool -QuarterWidget::interactionModeEnabled(void) const +QuarterWidget::interactionModeEnabled() const { return PRIVATE(this)->interactionmode->enabled(); } @@ -527,7 +527,7 @@ QuarterWidget::setInteractionModeOn(bool onoff) Returns true if interaction mode is on. */ bool -QuarterWidget::interactionModeOn(void) const +QuarterWidget::interactionModeOn() const { return PRIVATE(this)->interactionmode->on(); } @@ -536,7 +536,7 @@ QuarterWidget::interactionModeOn(void) const Returns the Coin cache context id for this widget. */ uint32_t -QuarterWidget::getCacheContextId(void) const +QuarterWidget::getCacheContextId() const { return PRIVATE(this)->getCacheContextId(); } @@ -562,7 +562,7 @@ QuarterWidget::setTransparencyType(TransparencyType type) \retval The current \ref TransparencyType */ QuarterWidget::TransparencyType -QuarterWidget::transparencyType(void) const +QuarterWidget::transparencyType() const { assert(PRIVATE(this)->sorendermanager); SoGLRenderAction * action = PRIVATE(this)->sorendermanager->getGLRenderAction(); @@ -590,7 +590,7 @@ QuarterWidget::setRenderMode(RenderMode mode) \retval The current \ref RenderMode */ QuarterWidget::RenderMode -QuarterWidget::renderMode(void) const +QuarterWidget::renderMode() const { assert(PRIVATE(this)->sorendermanager); return static_cast(PRIVATE(this)->sorendermanager->getRenderMode()); @@ -618,7 +618,7 @@ QuarterWidget::setStereoMode(StereoMode mode) \retval The current \ref StereoMode */ QuarterWidget::StereoMode -QuarterWidget::stereoMode(void) const +QuarterWidget::stereoMode() const { assert(PRIVATE(this)->sorendermanager); return static_cast(PRIVATE(this)->sorendermanager->getStereoMode()); @@ -636,7 +636,7 @@ the widget is located within, and updated whenever any change occurs, emitting a */ qreal -QuarterWidget::devicePixelRatio(void) const +QuarterWidget::devicePixelRatio() const { return PRIVATE(this)->device_pixel_ratio; } @@ -653,11 +653,11 @@ QuarterWidget::setSceneGraph(SoNode * node) if (PRIVATE(this)->scene) { PRIVATE(this)->scene->unref(); - PRIVATE(this)->scene = NULL; + PRIVATE(this)->scene = nullptr; } - SoCamera * camera = NULL; - SoSeparator * superscene = NULL; + SoCamera * camera = nullptr; + SoSeparator * superscene = nullptr; bool viewall = false; if (node) { @@ -690,7 +690,7 @@ QuarterWidget::setSceneGraph(SoNode * node) Returns pointer to root of scene graph */ SoNode * -QuarterWidget::getSceneGraph(void) const +QuarterWidget::getSceneGraph() const { return PRIVATE(this)->scene; } @@ -702,10 +702,10 @@ void QuarterWidget::setSoRenderManager(SoRenderManager * manager) { bool carrydata = false; - SoNode * scene = NULL; - SoCamera * camera = NULL; + SoNode * scene = nullptr; + SoCamera * camera = nullptr; SbViewportRegion vp; - if (PRIVATE(this)->sorendermanager && (manager != NULL)) { + if (PRIVATE(this)->sorendermanager && (manager != nullptr)) { scene = PRIVATE(this)->sorendermanager->getSceneGraph(); camera = PRIVATE(this)->sorendermanager->getCamera(); vp = PRIVATE(this)->sorendermanager->getViewportRegion(); @@ -735,7 +735,7 @@ QuarterWidget::setSoRenderManager(SoRenderManager * manager) Returns a pointer to the render manager. */ SoRenderManager * -QuarterWidget::getSoRenderManager(void) const +QuarterWidget::getSoRenderManager() const { return PRIVATE(this)->sorendermanager; } @@ -747,10 +747,10 @@ void QuarterWidget::setSoEventManager(SoEventManager * manager) { bool carrydata = false; - SoNode * scene = NULL; - SoCamera * camera = NULL; + SoNode * scene = nullptr; + SoCamera * camera = nullptr; SbViewportRegion vp; - if (PRIVATE(this)->soeventmanager && (manager != NULL)) { + if (PRIVATE(this)->soeventmanager && (manager != nullptr)) { scene = PRIVATE(this)->soeventmanager->getSceneGraph(); camera = PRIVATE(this)->soeventmanager->getCamera(); vp = PRIVATE(this)->soeventmanager->getViewportRegion(); @@ -780,7 +780,7 @@ QuarterWidget::setSoEventManager(SoEventManager * manager) Returns a pointer to the event manager */ SoEventManager * -QuarterWidget::getSoEventManager(void) const +QuarterWidget::getSoEventManager() const { return PRIVATE(this)->soeventmanager; } @@ -789,7 +789,7 @@ QuarterWidget::getSoEventManager(void) const Returns a pointer to the event filter */ EventFilter * -QuarterWidget::getEventFilter(void) const +QuarterWidget::getEventFilter() const { return PRIVATE(this)->eventfilter; } @@ -798,7 +798,7 @@ QuarterWidget::getEventFilter(void) const Reposition the current camera to display the entire scene */ void -QuarterWidget::viewAll(void) +QuarterWidget::viewAll() { const SbName viewallevent("sim.coin3d.coin.navigation.ViewAll"); for (int c = 0; c < PRIVATE(this)->soeventmanager->getNumSoScXMLStateMachines(); ++c) { @@ -816,7 +816,7 @@ QuarterWidget::viewAll(void) Camera typically seeks towards what the mouse is pointing at. */ void -QuarterWidget::seek(void) +QuarterWidget::seek() { const SbName seekevent("sim.coin3d.coin.navigation.Seek"); for (int c = 0; c < PRIVATE(this)->soeventmanager->getNumSoScXMLStateMachines(); ++c) { @@ -830,10 +830,10 @@ QuarterWidget::seek(void) } bool -QuarterWidget::updateDevicePixelRatio(void) { +QuarterWidget::updateDevicePixelRatio() { qreal dev_pix_ratio = 1.0; QWidget* winwidg = window(); - QWindow* win = NULL; + QWindow* win = nullptr; if(winwidg) { win = winwidg->windowHandle(); } @@ -1023,7 +1023,7 @@ bool QuarterWidget::viewportEvent(QEvent* event) render manager and render the scene by calling this method. */ void -QuarterWidget::redraw(void) +QuarterWidget::redraw() { // we're triggering the next paintGL(). Set a flag to remember this // to avoid that we process the delay queue in paintGL() @@ -1050,7 +1050,7 @@ QuarterWidget::redraw(void) Overridden from QGLWidget to render the scenegraph */ void -QuarterWidget::actualRedraw(void) +QuarterWidget::actualRedraw() { PRIVATE(this)->sorendermanager->render(PRIVATE(this)->clearwindow, PRIVATE(this)->clearzbuffer); @@ -1102,7 +1102,7 @@ QuarterWidget::setBackgroundColor(const QColor & color) rendering the scene. */ QColor -QuarterWidget::backgroundColor(void) const +QuarterWidget::backgroundColor() const { SbColor4f bg = PRIVATE(this)->sorendermanager->getBackgroundColor(); @@ -1116,7 +1116,7 @@ QuarterWidget::backgroundColor(void) const Returns the context menu used by the widget. */ QMenu * -QuarterWidget::getContextMenu(void) const +QuarterWidget::getContextMenu() const { return PRIVATE(this)->contextMenu(); } @@ -1125,7 +1125,7 @@ QuarterWidget::getContextMenu(void) const \retval Is context menu enabled? */ bool -QuarterWidget::contextMenuEnabled(void) const +QuarterWidget::contextMenuEnabled() const { return PRIVATE(this)->contextmenuenabled; } @@ -1175,8 +1175,8 @@ void QuarterWidget::removeStateMachine(SoScXMLStateMachine * statemachine) { SoEventManager * em = this->getSoEventManager(); - statemachine->setSceneGraphRoot(NULL); - statemachine->setActiveCamera(NULL); + statemachine->setSceneGraphRoot(nullptr); + statemachine->setActiveCamera(nullptr); em->removeSoScXMLStateMachine(statemachine); } @@ -1184,7 +1184,7 @@ QuarterWidget::removeStateMachine(SoScXMLStateMachine * statemachine) See \ref QWidget::minimumSizeHint */ QSize -QuarterWidget::minimumSizeHint(void) const +QuarterWidget::minimumSizeHint() const { return QSize(50, 50); } @@ -1195,7 +1195,7 @@ QuarterWidget::minimumSizeHint(void) const QuarterWidget, add these actions to the menu. */ QList -QuarterWidget::transparencyTypeActions(void) const +QuarterWidget::transparencyTypeActions() const { return PRIVATE(this)->transparencyTypeActions(); } @@ -1206,7 +1206,7 @@ QuarterWidget::transparencyTypeActions(void) const QuarterWidget, add these actions to the menu. */ QList -QuarterWidget::stereoModeActions(void) const +QuarterWidget::stereoModeActions() const { return PRIVATE(this)->stereoModeActions(); } @@ -1217,7 +1217,7 @@ QuarterWidget::stereoModeActions(void) const QuarterWidget, add these actions to the menu. */ QList -QuarterWidget::renderModeActions(void) const +QuarterWidget::renderModeActions() const { return PRIVATE(this)->renderModeActions(); } @@ -1239,7 +1239,7 @@ QuarterWidget::renderModeActions(void) const Removes any navigationModeFile set. */ void -QuarterWidget::resetNavigationModeFile(void) { +QuarterWidget::resetNavigationModeFile() { this->setNavigationModeFile(QUrl()); } @@ -1276,7 +1276,7 @@ QuarterWidget::setNavigationModeFile(const QUrl & url) if (PRIVATE(this)->currentStateMachine) { this->removeStateMachine(PRIVATE(this)->currentStateMachine); delete PRIVATE(this)->currentStateMachine; - PRIVATE(this)->currentStateMachine = NULL; + PRIVATE(this)->currentStateMachine = nullptr; PRIVATE(this)->navigationModeFile = url; } return; @@ -1287,7 +1287,7 @@ QuarterWidget::setNavigationModeFile(const QUrl & url) } QByteArray filenametmp = filename.toLocal8Bit(); - ScXMLStateMachine * stateMachine = NULL; + ScXMLStateMachine * stateMachine = nullptr; if (filenametmp.startsWith("coin:")){ stateMachine = ScXML::readFile(filenametmp.data()); @@ -1350,7 +1350,7 @@ QuarterWidget::setNavigationModeFile(const QUrl & url) \retval The current navigationModeFile */ const QUrl & -QuarterWidget::navigationModeFile(void) const +QuarterWidget::navigationModeFile() const { return PRIVATE(this)->navigationModeFile; } diff --git a/src/Gui/Quarter/QuarterWidget.h b/src/Gui/Quarter/QuarterWidget.h index d69bf2c5b6..80e781b613 100644 --- a/src/Gui/Quarter/QuarterWidget.h +++ b/src/Gui/Quarter/QuarterWidget.h @@ -81,9 +81,9 @@ class QUARTER_DLL_API QuarterWidget : public QGraphicsView { public: - explicit QuarterWidget(QWidget * parent = 0, const QtGLWidget * sharewidget = 0, Qt::WindowFlags f = Qt::WindowFlags()); - explicit QuarterWidget(QtGLContext * context, QWidget * parent = 0, const QtGLWidget * sharewidget = 0, Qt::WindowFlags f = Qt::WindowFlags()); - explicit QuarterWidget(const QtGLFormat & format, QWidget * parent = 0, const QtGLWidget * shareWidget = 0, Qt::WindowFlags f = Qt::WindowFlags()); + explicit QuarterWidget(QWidget * parent = nullptr, const QtGLWidget * sharewidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + explicit QuarterWidget(QtGLContext * context, QWidget * parent = nullptr, const QtGLWidget * sharewidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + explicit QuarterWidget(const QtGLFormat & format, QWidget * parent = nullptr, const QtGLWidget * shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); virtual ~QuarterWidget(); enum TransparencyType { @@ -117,70 +117,70 @@ public: INTERLEAVED_COLUMNS = SoRenderManager::INTERLEAVED_COLUMNS }; - TransparencyType transparencyType(void) const; - RenderMode renderMode(void) const; - StereoMode stereoMode(void) const; + TransparencyType transparencyType() const; + RenderMode renderMode() const; + StereoMode stereoMode() const; void setBackgroundColor(const QColor & color); - QColor backgroundColor(void) const; + QColor backgroundColor() const; - qreal devicePixelRatio(void) const; + qreal devicePixelRatio() const; - void resetNavigationModeFile(void); + void resetNavigationModeFile(); void setNavigationModeFile(const QUrl & url = QUrl(QString::fromLatin1(DEFAULT_NAVIGATIONFILE))); - const QUrl & navigationModeFile(void) const; + const QUrl & navigationModeFile() const; void setContextMenuEnabled(bool yes); - bool contextMenuEnabled(void) const; - QMenu * getContextMenu(void) const; + bool contextMenuEnabled() const; + QMenu * getContextMenu() const; - bool headlightEnabled(void) const; + bool headlightEnabled() const; void setHeadlightEnabled(bool onoff); - SoDirectionalLight * getHeadlight(void) const; + SoDirectionalLight * getHeadlight() const; - bool clearZBuffer(void) const; + bool clearZBuffer() const; void setClearZBuffer(bool onoff); - bool clearWindow(void) const; + bool clearWindow() const; void setClearWindow(bool onoff); - bool interactionModeEnabled(void) const; + bool interactionModeEnabled() const; void setInteractionModeEnabled(bool onoff); - bool interactionModeOn(void) const; + bool interactionModeOn() const; void setInteractionModeOn(bool onoff); void setStateCursor(const SbName & state, const QCursor & cursor); QCursor stateCursor(const SbName & state); - uint32_t getCacheContextId(void) const; + uint32_t getCacheContextId() const; virtual void setSceneGraph(SoNode * root); - virtual SoNode * getSceneGraph(void) const; + virtual SoNode * getSceneGraph() const; void setSoEventManager(SoEventManager * manager); - SoEventManager * getSoEventManager(void) const; + SoEventManager * getSoEventManager() const; void setSoRenderManager(SoRenderManager * manager); - SoRenderManager * getSoRenderManager(void) const; + SoRenderManager * getSoRenderManager() const; - EventFilter * getEventFilter(void) const; + EventFilter * getEventFilter() const; void addStateMachine(SoScXMLStateMachine * statemachine); void removeStateMachine(SoScXMLStateMachine * statemachine); virtual bool processSoEvent(const SoEvent * event); - virtual QSize minimumSizeHint(void) const; + virtual QSize minimumSizeHint() const; - QList transparencyTypeActions(void) const; - QList stereoModeActions(void) const; - QList renderModeActions(void) const; + QList transparencyTypeActions() const; + QList stereoModeActions() const; + QList renderModeActions() const; public Q_SLOTS: - virtual void viewAll(void); - virtual void seek(void); + virtual void viewAll(); + virtual void seek(); - void redraw(void); + void redraw(); void setRenderMode(RenderMode mode); void setStereoMode(StereoMode mode); @@ -197,8 +197,8 @@ protected: virtual void paintEvent(QPaintEvent*); virtual void resizeEvent(QResizeEvent*); virtual bool viewportEvent(QEvent* event); - virtual void actualRedraw(void); - virtual bool updateDevicePixelRatio(void); + virtual void actualRedraw(); + virtual bool updateDevicePixelRatio(); private: void constructor(const QtGLFormat& format, const QtGLWidget* sharewidget); diff --git a/src/Gui/Quarter/QuarterWidgetP.cpp b/src/Gui/Quarter/QuarterWidgetP.cpp index 4cba6614b3..8c5a84c84f 100644 --- a/src/Gui/Quarter/QuarterWidgetP.cpp +++ b/src/Gui/Quarter/QuarterWidgetP.cpp @@ -57,7 +57,7 @@ #include "ContextMenu.h" #include "QuarterP.h" -#include +#include using namespace SIM::Coin3D::Quarter; @@ -67,19 +67,19 @@ public: SbList widgetlist; }; -static SbList * cachecontext_list = NULL; +static SbList * cachecontext_list = nullptr; QuarterWidgetP::QuarterWidgetP(QuarterWidget * masterptr, const QtGLWidget * sharewidget) : master(masterptr), - scene(NULL), - eventfilter(NULL), - interactionmode(NULL), - sorendermanager(NULL), - soeventmanager(NULL), + scene(nullptr), + eventfilter(nullptr), + interactionmode(nullptr), + sorendermanager(nullptr), + soeventmanager(nullptr), initialsorendermanager(false), initialsoeventmanager(false), - headlight(NULL), - cachecontext(NULL), + headlight(nullptr), + cachecontext(nullptr), contextmenuenabled(true), autoredrawenabled(true), interactionmodeenabled(false), @@ -87,7 +87,7 @@ QuarterWidgetP::QuarterWidgetP(QuarterWidget * masterptr, const QtGLWidget * sha clearwindow(true), addactions(true), device_pixel_ratio(1.0), - contextmenu(NULL) + contextmenu(nullptr) { this->cachecontext = findCacheContext(masterptr, sharewidget); @@ -121,11 +121,11 @@ QuarterWidgetP::searchForCamera(SoNode * root) return (SoCamera *) node; } } - return NULL; + return nullptr; } uint32_t -QuarterWidgetP::getCacheContextId(void) const +QuarterWidgetP::getCacheContextId() const { return this->cachecontext->id; } @@ -133,7 +133,7 @@ QuarterWidgetP::getCacheContextId(void) const QuarterWidgetP_cachecontext * QuarterWidgetP::findCacheContext(QuarterWidget * widget, const QtGLWidget * sharewidget) { - if (cachecontext_list == NULL) { + if (cachecontext_list == nullptr) { // FIXME: static memory leak cachecontext_list = new SbList ; } @@ -257,7 +257,7 @@ QuarterWidgetP::statechangecb(void * userdata, ScXMLStateMachine * statemachine, QList -QuarterWidgetP::transparencyTypeActions(void) const +QuarterWidgetP::transparencyTypeActions() const { if (this->transparencytypeactions.isEmpty()) { this->transparencytypegroup = new QActionGroup(this->master); @@ -277,7 +277,7 @@ QuarterWidgetP::transparencyTypeActions(void) const } QList -QuarterWidgetP::stereoModeActions(void) const +QuarterWidgetP::stereoModeActions() const { if (this->stereomodeactions.isEmpty()) { this->stereomodegroup = new QActionGroup(this->master); @@ -291,7 +291,7 @@ QuarterWidgetP::stereoModeActions(void) const } QList -QuarterWidgetP::renderModeActions(void) const +QuarterWidgetP::renderModeActions() const { if (this->rendermodeactions.isEmpty()) { this->rendermodegroup = new QActionGroup(this->master); @@ -308,7 +308,7 @@ QuarterWidgetP::renderModeActions(void) const #undef ADD_ACTION QMenu * -QuarterWidgetP::contextMenu(void) +QuarterWidgetP::contextMenu() { if (!this->contextmenu) { this->contextmenu = new ContextMenu(this->master); diff --git a/src/Gui/Quarter/QuarterWidgetP.h b/src/Gui/Quarter/QuarterWidgetP.h index 7b5c99dc78..d9373961cb 100644 --- a/src/Gui/Quarter/QuarterWidgetP.h +++ b/src/Gui/Quarter/QuarterWidgetP.h @@ -66,12 +66,12 @@ public: ~QuarterWidgetP(); SoCamera * searchForCamera(SoNode * root); - uint32_t getCacheContextId(void) const; - QMenu * contextMenu(void); + uint32_t getCacheContextId() const; + QMenu * contextMenu(); - QList transparencyTypeActions(void) const; - QList renderModeActions(void) const; - QList stereoModeActions(void) const; + QList transparencyTypeActions() const; + QList renderModeActions() const; + QList stereoModeActions() const; QuarterWidget * const master; SoNode * scene; diff --git a/src/Gui/Quarter/SensorManager.cpp b/src/Gui/Quarter/SensorManager.cpp index 59d2d13b55..5d2ebf115f 100644 --- a/src/Gui/Quarter/SensorManager.cpp +++ b/src/Gui/Quarter/SensorManager.cpp @@ -43,7 +43,7 @@ using namespace SIM::Coin3D::Quarter; -SensorManager::SensorManager(void) +SensorManager::SensorManager() : inherited() { this->mainthreadid = cc_thread_id(); @@ -74,7 +74,7 @@ SensorManager::SensorManager(void) SensorManager::~SensorManager() { // remove the Coin callback before shutting down - SoDB::getSensorManager()->setChangedCallback(NULL, NULL); + SoDB::getSensorManager()->setChangedCallback(nullptr, nullptr); if (this->signalthread->isRunning()) { this->signalthread->stopThread(); @@ -104,7 +104,7 @@ SensorManager::sensorQueueChangedCB(void * closure) } void -SensorManager::sensorQueueChanged(void) +SensorManager::sensorQueueChanged() { SoSensorManager * sensormanager = SoDB::getSensorManager(); assert(sensormanager); @@ -144,7 +144,7 @@ SensorManager::sensorQueueChanged(void) } void -SensorManager::idleTimeout(void) +SensorManager::idleTimeout() { SoDB::getSensorManager()->processTimerQueue(); SoDB::getSensorManager()->processDelayQueue(true); @@ -152,14 +152,14 @@ SensorManager::idleTimeout(void) } void -SensorManager::timerQueueTimeout(void) +SensorManager::timerQueueTimeout() { SoDB::getSensorManager()->processTimerQueue(); this->sensorQueueChanged(); } void -SensorManager::delayTimeout(void) +SensorManager::delayTimeout() { SoDB::getSensorManager()->processTimerQueue(); SoDB::getSensorManager()->processDelayQueue(false); diff --git a/src/Gui/Quarter/SensorManager.h b/src/Gui/Quarter/SensorManager.h index 6344cf93e6..6628ba9e3a 100644 --- a/src/Gui/Quarter/SensorManager.h +++ b/src/Gui/Quarter/SensorManager.h @@ -45,14 +45,14 @@ class SensorManager : public QObject { Q_OBJECT typedef QObject inherited; public: - SensorManager(void); + SensorManager(); ~SensorManager(); public Q_SLOTS: - void idleTimeout(void); - void delayTimeout(void); - void timerQueueTimeout(void); - void sensorQueueChanged(void); + void idleTimeout(); + void delayTimeout(); + void timerQueueTimeout(); + void sensorQueueChanged(); void setTimerEpsilon(double sec); private: diff --git a/src/Gui/Quarter/SignalThread.cpp b/src/Gui/Quarter/SignalThread.cpp index 6d18f8636d..a242286258 100644 --- a/src/Gui/Quarter/SignalThread.cpp +++ b/src/Gui/Quarter/SignalThread.cpp @@ -36,7 +36,7 @@ using namespace SIM::Coin3D::Quarter; -SignalThread::SignalThread(void) +SignalThread::SignalThread() : isstopped(false) { } @@ -46,7 +46,7 @@ SignalThread::~SignalThread() } void -SignalThread::trigger(void) +SignalThread::trigger() { // lock first to make sure the QThread is actually waiting for a signal QMutexLocker ml(&this->mutex); @@ -54,7 +54,7 @@ SignalThread::trigger(void) } void -SignalThread::stopThread(void) +SignalThread::stopThread() { QMutexLocker ml(&this->mutex); this->isstopped = true; @@ -63,7 +63,7 @@ SignalThread::stopThread(void) void -SignalThread::run(void) +SignalThread::run() { QMutexLocker ml(&this->mutex); while (!this->isstopped) { diff --git a/src/Gui/Quarter/SignalThread.h b/src/Gui/Quarter/SignalThread.h index e96117ae0c..718383991b 100644 --- a/src/Gui/Quarter/SignalThread.h +++ b/src/Gui/Quarter/SignalThread.h @@ -44,16 +44,16 @@ namespace SIM { namespace Coin3D { namespace Quarter { class SignalThread : public QThread { Q_OBJECT public: - SignalThread(void); + SignalThread(); virtual ~SignalThread(); - virtual void run(void); - void trigger(void); - void stopThread(void); + virtual void run(); + void trigger(); + void stopThread(); Q_SIGNALS: - void triggerSignal(void); + void triggerSignal(); private: QWaitCondition waitcond; diff --git a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp index 552b055ebe..9d787b3694 100644 --- a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp +++ b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp @@ -168,7 +168,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::init() m_seekdistanceabs = false; m_seekperiod = 2.0f; m_inseekmode = false; - m_storedcamera = 0; + m_storedcamera = nullptr; m_viewingflag = false; pickRadius = 5.0; @@ -298,12 +298,12 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::convertPerspective2Ortho(const So out->height = 2.0f * focaldist * (float)tan(in->heightAngle.getValue() / 2.0); } -SoCamera* SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getCamera(void) const +SoCamera* SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getCamera() const { return getSoRenderManager()->getCamera(); } -const SbViewportRegion & SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getViewportRegion(void) const +const SbViewportRegion & SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getViewportRegion() const { return getSoRenderManager()->getViewportRegion(); } @@ -318,17 +318,17 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::setViewing(SbBool enable) if(m_viewingflag) { SoGLRenderAction* action = getSoRenderManager()->getGLRenderAction(); - if(action != NULL) + if(action != nullptr) SoLocateHighlight::turnOffCurrentHighlight(action); } } -SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::isViewing(void) const +SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::isViewing() const { return m_viewingflag; } -void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::interactiveCountInc(void) +void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::interactiveCountInc() { // Catch problems with missing interactiveCountDec() calls. assert(m_interactionnesting < 100); @@ -338,7 +338,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::interactiveCountInc(void) } } -void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::interactiveCountDec(void) +void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::interactiveCountDec() { if(--m_interactionnesting <= 0) { m_interactionEndCallback.invokeCallbacks(this); @@ -346,7 +346,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::interactiveCountDec(void) } } -int SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getInteractiveCount(void) const +int SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getInteractiveCount() const { return m_interactionnesting; } @@ -372,22 +372,22 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::removeFinishCallback(SIM::Coin3D: } -float SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getSeekDistance(void) const +float SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getSeekDistance() const { return m_seekdistance; } -float SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getSeekTime(void) const +float SIM::Coin3D::Quarter::SoQTQuarterAdaptor::getSeekTime() const { return m_seekperiod; } -SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::isSeekMode(void) const +SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::isSeekMode() const { return m_inseekmode; } -SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::isSeekValuePercentage(void) const +SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::isSeekValuePercentage() const { return m_seekdistanceabs ? false : true; } @@ -541,7 +541,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::seeksensorCB(void* data, SoSensor if(end) thisp->setSeekMode(false); } -void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::saveHomePosition(void) +void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::saveHomePosition() { SoCamera* cam = getSoRenderManager()->getCamera(); if (!cam) { @@ -562,7 +562,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::saveHomePosition(void) m_storedcamera->copyFieldValues(getSoRenderManager()->getCamera()); } -void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::resetToHomePosition(void) +void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::resetToHomePosition() { SoCamera* cam = getSoRenderManager()->getCamera(); if (!cam) { @@ -724,7 +724,7 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::paintEvent(QPaintEvent* event) this->framesPerSecond = addFrametime(start); } -void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::resetFrameCounter(void) +void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::resetFrameCounter() { this->framecount = 0; this->frametime = 0.0f; diff --git a/src/Gui/Quarter/SoQTQuarterAdaptor.h b/src/Gui/Quarter/SoQTQuarterAdaptor.h index 84956532d4..e9cee0b949 100644 --- a/src/Gui/Quarter/SoQTQuarterAdaptor.h +++ b/src/Gui/Quarter/SoQTQuarterAdaptor.h @@ -47,9 +47,9 @@ typedef void SoQTQuarterAdaptorCB(void* data, SoQTQuarterAdaptor* viewer); class QUARTER_DLL_API SoQTQuarterAdaptor : public QuarterWidget { public: - explicit SoQTQuarterAdaptor(QWidget* parent = 0, const QtGLWidget* sharewidget = 0, Qt::WindowFlags f = Qt::WindowFlags()); - explicit SoQTQuarterAdaptor(const QtGLFormat& format, QWidget* parent = 0, const QtGLWidget* shareWidget = 0, Qt::WindowFlags f = Qt::WindowFlags()); - explicit SoQTQuarterAdaptor(QtGLContext* context, QWidget* parent = 0, const QtGLWidget* sharewidget = 0, Qt::WindowFlags f = Qt::WindowFlags()); + explicit SoQTQuarterAdaptor(QWidget* parent = nullptr, const QtGLWidget* sharewidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + explicit SoQTQuarterAdaptor(const QtGLFormat& format, QWidget* parent = nullptr, const QtGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + explicit SoQTQuarterAdaptor(QtGLContext* context, QWidget* parent = nullptr, const QtGLWidget* sharewidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); virtual ~SoQTQuarterAdaptor(); //the functions available in soqtviewer but missing in quarter @@ -59,38 +59,38 @@ public: QWidget* getGLWidget() const; virtual void setCameraType(SoType type); - SoCamera * getCamera(void) const; + SoCamera * getCamera() const; - const SbViewportRegion & getViewportRegion(void) const; + const SbViewportRegion & getViewportRegion() const; virtual void setViewing(SbBool enable); - SbBool isViewing(void) const; + SbBool isViewing() const; - void interactiveCountInc(void); - void interactiveCountDec(void); - int getInteractiveCount(void) const; + void interactiveCountInc(); + void interactiveCountDec(); + int getInteractiveCount() const; - void addStartCallback(SoQTQuarterAdaptorCB* func, void* data = NULL); - void addFinishCallback(SoQTQuarterAdaptorCB* func, void* data = NULL); - void removeStartCallback(SoQTQuarterAdaptorCB* func, void* data = NULL); - void removeFinishCallback(SoQTQuarterAdaptorCB* func, void* data = NULL); + void addStartCallback(SoQTQuarterAdaptorCB* func, void* data = nullptr); + void addFinishCallback(SoQTQuarterAdaptorCB* func, void* data = nullptr); + void removeStartCallback(SoQTQuarterAdaptorCB* func, void* data = nullptr); + void removeFinishCallback(SoQTQuarterAdaptorCB* func, void* data = nullptr); virtual void setSeekMode(SbBool enable); - SbBool isSeekMode(void) const; + SbBool isSeekMode() const; SbBool seekToPoint(const SbVec2s screenpos); void seekToPoint(const SbVec3f& scenepos); void setSeekTime(const float seconds); - float getSeekTime(void) const; + float getSeekTime() const; void setSeekDistance(const float distance); - float getSeekDistance(void) const; + float getSeekDistance() const; void setSeekValueAsPercentage(const SbBool on); - SbBool isSeekValuePercentage(void) const; + SbBool isSeekValuePercentage() const; - virtual float getPickRadius(void) const {return this->pickRadius;} + virtual float getPickRadius() const {return this->pickRadius;} virtual void setPickRadius(float pickRadius); - virtual void saveHomePosition(void); - virtual void resetToHomePosition(void); + virtual void saveHomePosition(); + virtual void resetToHomePosition(); virtual void setSceneGraph(SoNode* root) { QuarterWidget::setSceneGraph(root); @@ -100,7 +100,7 @@ public: virtual void paintEvent(QPaintEvent*); //this functions still need to be ported - virtual void afterRealizeHook(void) {} //enables spacenav and joystick in soqt, dunno if this is needed + virtual void afterRealizeHook() {} //enables spacenav and joystick in soqt, dunno if this is needed private: void init(); @@ -109,7 +109,7 @@ private: void getCameraCoordinateSystem(SoCamera * camera, SoNode * root, SbMatrix & matrix, SbMatrix & inverse); static void seeksensorCB(void * data, SoSensor * s); void moveCameraScreen(const SbVec2f & screenpos); - void resetFrameCounter(void); + void resetFrameCounter(); SbVec2f addFrametime(double ft); bool m_viewingflag; diff --git a/src/Gui/Quarter/SpaceNavigatorDevice.cpp b/src/Gui/Quarter/SpaceNavigatorDevice.cpp index 14fe49e871..5726845439 100644 --- a/src/Gui/Quarter/SpaceNavigatorDevice.cpp +++ b/src/Gui/Quarter/SpaceNavigatorDevice.cpp @@ -128,7 +128,7 @@ const SoEvent * SpaceNavigatorDevice::translateEvent(QEvent * event) { Q_UNUSED(event); - SoEvent * ret = NULL; + SoEvent * ret = nullptr; #ifdef HAVE_SPACENAV_LIB NativeEvent * ce = dynamic_cast(event); diff --git a/src/Gui/Quarter/devices/InputDevice.h b/src/Gui/Quarter/devices/InputDevice.h index bf2785d1eb..be46a1649a 100644 --- a/src/Gui/Quarter/devices/InputDevice.h +++ b/src/Gui/Quarter/devices/InputDevice.h @@ -47,7 +47,7 @@ class QuarterWidget; class QUARTER_DLL_API InputDevice { public: InputDevice(QuarterWidget * quarter); - InputDevice(void); + InputDevice(); virtual ~InputDevice() {} /*! diff --git a/src/Gui/Quarter/devices/Keyboard.h b/src/Gui/Quarter/devices/Keyboard.h index 8284798881..2299adf3d9 100644 --- a/src/Gui/Quarter/devices/Keyboard.h +++ b/src/Gui/Quarter/devices/Keyboard.h @@ -44,7 +44,7 @@ namespace SIM { namespace Coin3D { namespace Quarter { class QUARTER_DLL_API Keyboard : public InputDevice { public: Keyboard(QuarterWidget* quarter); - Keyboard(void); + Keyboard(); virtual ~Keyboard(); virtual const SoEvent * translateEvent(QEvent * event); diff --git a/src/Gui/Quarter/devices/Mouse.h b/src/Gui/Quarter/devices/Mouse.h index 49405368c2..6ac878063d 100644 --- a/src/Gui/Quarter/devices/Mouse.h +++ b/src/Gui/Quarter/devices/Mouse.h @@ -44,7 +44,7 @@ namespace SIM { namespace Coin3D { namespace Quarter { class QUARTER_DLL_API Mouse : public InputDevice { public: Mouse(QuarterWidget* quarter); - Mouse(void); + Mouse(); virtual ~Mouse(); virtual const SoEvent * translateEvent(QEvent * event); diff --git a/src/Gui/Quarter/devices/SpaceNavigatorDevice.h b/src/Gui/Quarter/devices/SpaceNavigatorDevice.h index 30224f6446..c8f6e8a5b3 100644 --- a/src/Gui/Quarter/devices/SpaceNavigatorDevice.h +++ b/src/Gui/Quarter/devices/SpaceNavigatorDevice.h @@ -43,7 +43,7 @@ namespace SIM { namespace Coin3D { namespace Quarter { class QUARTER_DLL_API SpaceNavigatorDevice : public InputDevice { public: SpaceNavigatorDevice(QuarterWidget* quarter); - SpaceNavigatorDevice(void); + SpaceNavigatorDevice(); virtual ~SpaceNavigatorDevice(); virtual const SoEvent * translateEvent(QEvent * event); diff --git a/src/Gui/Quarter/eventhandlers/EventFilter.h b/src/Gui/Quarter/eventhandlers/EventFilter.h index 68c7cbdf06..5f4f95bd1b 100644 --- a/src/Gui/Quarter/eventhandlers/EventFilter.h +++ b/src/Gui/Quarter/eventhandlers/EventFilter.h @@ -53,7 +53,7 @@ public: void registerInputDevice(InputDevice * device); void unregisterInputDevice(InputDevice * device); - const QPoint & globalMousePosition(void) const; + const QPoint & globalMousePosition() const; protected: bool eventFilter(QObject * obj, QEvent * event); From 916fe37a6b757e3bf6d51fb65ebc412243125d8e Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 5 Nov 2021 21:37:19 +0100 Subject: [PATCH 122/138] Gui: move handling of SoMouseWheelEvent to its own handler function --- src/Gui/GestureNavigationStyle.cpp | 1 + src/Gui/NavigationStyle.cpp | 32 ++++++++++++++++++------------ src/Gui/NavigationStyle.h | 2 ++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Gui/GestureNavigationStyle.cpp b/src/Gui/GestureNavigationStyle.cpp index 7d0732ae54..946efb1dcf 100644 --- a/src/Gui/GestureNavigationStyle.cpp +++ b/src/Gui/GestureNavigationStyle.cpp @@ -72,6 +72,7 @@ #include "GestureNavigationStyle.h" #include +#include #include #include "View3DInventorViewer.h" #include "Application.h" diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index c75ad3869a..fb971292e1 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -1495,24 +1495,19 @@ SbBool NavigationStyle::processEvent(const SoEvent * const ev) SbBool NavigationStyle::processSoEvent(const SoEvent * const ev) { - const SbVec2s pos(ev->getPosition()); - const SbVec2f posn = normalizePixelPos(pos); bool processed = false; //handle mouse wheel zoom - if(ev->isOfType(SoMouseWheelEvent::getClassTypeId())){ - doZoom( - viewer->getSoRenderManager()->getCamera(), - static_cast(ev)->getDelta(), - posn - ); - processed = true; + if (ev->isOfType(SoMouseWheelEvent::getClassTypeId())) { + const SoMouseWheelEvent * const event = static_cast(ev); + processed = processWheelEvent(event); } - if (! processed) - return viewer->processSoEventBase(ev); - else - return processed; + if (!processed) { + processed = viewer->processSoEventBase(ev); + } + + return processed; } void NavigationStyle::syncWithEvent(const SoEvent * const ev) @@ -1692,6 +1687,17 @@ SbBool NavigationStyle::processClickEvent(const SoMouseButtonEvent * const event return processed; } +SbBool NavigationStyle::processWheelEvent(const SoMouseWheelEvent * const event) +{ + const SbVec2s pos(event->getPosition()); + const SbVec2f posn = normalizePixelPos(pos); + + //handle mouse wheel zoom + doZoom(viewer->getSoRenderManager()->getCamera(), + event->getDelta(), posn); + return true; +} + void NavigationStyle::setPopupMenuEnabled(const SbBool on) { this->menuenabled = on; diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index f35e61f37a..7f493ec55a 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -41,6 +41,7 @@ // forward declarations class SoEvent; +class SoMouseWheelEvent; class SoMotion3Event; class SoQtViewer; class SoCamera; @@ -154,6 +155,7 @@ public: virtual SbBool processMotionEvent(const SoMotion3Event * const ev); virtual SbBool processKeyboardEvent(const SoKeyboardEvent * const event); virtual SbBool processClickEvent(const SoMouseButtonEvent * const event); + virtual SbBool processWheelEvent(const SoMouseWheelEvent * const event); void setPopupMenuEnabled(const SbBool on); SbBool isPopupMenuEnabled() const; From 549e5b56502518d41865465d1e0953e12b10b4d8 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 5 Nov 2021 23:49:14 +0100 Subject: [PATCH 123/138] Tux: support of OpenSCAD and TinkerCAD in navigation indicator --- src/Mod/Tux/NavigationIndicatorGui.py | 52 +++++++++++++++++++++++++++ src/Mod/Tux/Resources/Tux.qrc | 11 ++++++ 2 files changed, 63 insertions(+) diff --git a/src/Mod/Tux/NavigationIndicatorGui.py b/src/Mod/Tux/NavigationIndicatorGui.py index 516c17258b..c49048c42c 100644 --- a/src/Mod/Tux/NavigationIndicatorGui.py +++ b/src/Mod/Tux/NavigationIndicatorGui.py @@ -288,6 +288,42 @@ def retranslateUi(): """ + global t9 + t9 = "

OpenSCAD " + text06 + """

+ + + + + + + + + + + + + + + +
""" + text01 + """""" + text02 + """""" + text02 + """""" + text03 + """""" + text04 + """
""" + + global t10 + t10 = "

TinkerCAD " + text06 + """

+ + + + + + + + + + + + + +
""" + text01 + """""" + text02 + """""" + text03 + """""" + text04 + """
""" + menuSettings.setTitle(translate("NavigationIndicator", "Settings")) menuOrbit.setTitle(translate("NavigationIndicator", "Orbit style")) aCompact.setText(translate("NavigationIndicator", "Compact")) @@ -384,6 +420,18 @@ a8.setText("OpenCascade") a8.setData("Gui::OpenCascadeNavigationStyle") a8.setObjectName("Indicator_NavigationOpenCascade") +a9 = QtGui.QAction(gStyle) +a9.setIcon(QtGui.QIcon(":/icons/NavigationOpenSCAD_dark.svg")) +a9.setText("OpenSCAD") +a9.setData("Gui::OpenSCADNavigationStyle") +a9.setObjectName("Indicator_NavigationOpenSCAD") + +a10 = QtGui.QAction(gStyle) +a10.setIcon(QtGui.QIcon(":/icons/NavigationTinkerCAD_dark.svg")) +a10.setText("TinkerCAD") +a10.setData("Gui::TinkerCADNavigationStyle") +a10.setObjectName("Indicator_NavigationTinkerCAD") + menu.addMenu(menuSettings) menu.addSeparator() menu.addAction(a0) @@ -395,6 +443,8 @@ menu.addAction(a5) menu.addAction(a6) menu.addAction(a7) menu.addAction(a8) +menu.addAction(a9) +menu.addAction(a10) def onCompact(): @@ -430,6 +480,8 @@ def onTooltip(): a6.setToolTip(t6) a7.setToolTip(t7) a8.setToolTip(t8) + a9.setToolTip(t9) + a10.setToolTip(t10) p.SetBool("Tooltip", 1) else: for i in gStyle.actions(): diff --git a/src/Mod/Tux/Resources/Tux.qrc b/src/Mod/Tux/Resources/Tux.qrc index e503675307..471f97fcc8 100644 --- a/src/Mod/Tux/Resources/Tux.qrc +++ b/src/Mod/Tux/Resources/Tux.qrc @@ -58,6 +58,17 @@ icons/NavigationOpenInventor_dark.svg icons/NavigationOpenInventor_ZoomAlt.svg icons/NavigationOpenInventor_Zoom.svg + icons/NavigationCAD_dark.svg + icons/NavigationOpenCascade_Select.svg + icons/NavigationOpenCascade_PanAlt.svg + icons/NavigationOpenCascade_PanAlt.svg + icons/NavigationOpenCascade_Select.svg + icons/NavigationGesture_Pan.svg + icons/NavigationCAD_dark.svg + icons/NavigationOpenCascade_Select.svg + icons/NavigationOpenCascade_Zoom.svg + icons/NavigationGesture_Pan.svg + icons/NavigationOpenCascade_PanAlt.svg icons/NavigationRevit_Pan.svg icons/NavigationRevit_Rotate.svg icons/NavigationRevit_light.svg From e30efba754e0b894558be26d7341045647226061 Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Fri, 5 Nov 2021 12:13:10 -0400 Subject: [PATCH 124/138] [PartDesign] Fix typo in method name --- src/Mod/PartDesign/Gui/TaskLoftParameters.cpp | 4 ++-- src/Mod/PartDesign/Gui/TaskLoftParameters.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp b/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp index 36cab259a2..0335944e4d 100644 --- a/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp @@ -69,7 +69,7 @@ TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView,bool /*newObj* connect(ui->buttonRefAdd, SIGNAL(toggled(bool)), this, SLOT(onRefButtonAdd(bool))); connect(ui->buttonRefRemove, SIGNAL(toggled(bool)), - this, SLOT(onRefButtonRemvove(bool))); + this, SLOT(onRefButtonRemove(bool))); connect(ui->checkBoxRuled, SIGNAL(toggled(bool)), this, SLOT(onRuled(bool))); connect(ui->checkBoxClosed, SIGNAL(toggled(bool)), @@ -324,7 +324,7 @@ void TaskLoftParameters::onRefButtonAdd(bool checked) { } } -void TaskLoftParameters::onRefButtonRemvove(bool checked) { +void TaskLoftParameters::onRefButtonRemove(bool checked) { if (checked) { Gui::Selection().clearSelection(); diff --git a/src/Mod/PartDesign/Gui/TaskLoftParameters.h b/src/Mod/PartDesign/Gui/TaskLoftParameters.h index b99a771edf..20e977dd9e 100644 --- a/src/Mod/PartDesign/Gui/TaskLoftParameters.h +++ b/src/Mod/PartDesign/Gui/TaskLoftParameters.h @@ -56,7 +56,7 @@ public: private Q_SLOTS: void onProfileButton(bool); void onRefButtonAdd(bool); - void onRefButtonRemvove(bool); + void onRefButtonRemove(bool); void onClosed(bool); void onRuled(bool); void onDeleteSection(); From 4543c982670a0d6044ebed7812ac7df43feb1c4a Mon Sep 17 00:00:00 2001 From: luzpaz Date: Fri, 5 Nov 2021 19:15:00 -0400 Subject: [PATCH 125/138] MeshPart: fix header uniformity + remove superfluous whitespace (#5135) * MeshPart: fix header uniformity + remove superfluous whitespace --- src/Mod/MeshPart/App/AppMeshPart.cpp | 2 +- src/Mod/MeshPart/App/AppMeshPartPy.cpp | 2 +- src/Mod/MeshPart/App/CurveProjector.cpp | 2 +- src/Mod/MeshPart/App/CurveProjector.h | 2 +- src/Mod/MeshPart/App/MeshAlgos.cpp | 2 +- src/Mod/MeshPart/App/MeshAlgos.h | 2 +- src/Mod/MeshPart/App/PreCompiled.cpp | 2 +- src/Mod/MeshPart/App/PreCompiled.h | 2 +- src/Mod/MeshPart/Gui/AppMeshPartGui.cpp | 2 +- src/Mod/MeshPart/Gui/Command.cpp | 2 +- src/Mod/MeshPart/Gui/MeshFlatteningCommand.py | 22 ++++++ src/Mod/MeshPart/Gui/PreCompiled.cpp | 2 +- src/Mod/MeshPart/Gui/PreCompiled.h | 2 +- src/Mod/MeshPart/Init.py | 9 +-- src/Mod/MeshPart/InitGui.py | 76 ++++++++++--------- 15 files changed, 77 insertions(+), 54 deletions(-) diff --git a/src/Mod/MeshPart/App/AppMeshPart.cpp b/src/Mod/MeshPart/App/AppMeshPart.cpp index fbd6d1da92..e4cc0d004b 100644 --- a/src/Mod/MeshPart/App/AppMeshPart.cpp +++ b/src/Mod/MeshPart/App/AppMeshPart.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/App/AppMeshPartPy.cpp b/src/Mod/MeshPart/App/AppMeshPartPy.cpp index 489ae76933..a1b96ff51e 100644 --- a/src/Mod/MeshPart/App/AppMeshPartPy.cpp +++ b/src/Mod/MeshPart/App/AppMeshPartPy.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/App/CurveProjector.cpp b/src/Mod/MeshPart/App/CurveProjector.cpp index e559834e66..5731521aa3 100644 --- a/src/Mod/MeshPart/App/CurveProjector.cpp +++ b/src/Mod/MeshPart/App/CurveProjector.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) Juergen Riegel * + * Copyright (c) 2008 Juergen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/App/CurveProjector.h b/src/Mod/MeshPart/App/CurveProjector.h index 20d2479fea..5efcd7566d 100644 --- a/src/Mod/MeshPart/App/CurveProjector.h +++ b/src/Mod/MeshPart/App/CurveProjector.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) Juergen Riegel * + * Copyright (c) 2008 Juergen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/App/MeshAlgos.cpp b/src/Mod/MeshPart/App/MeshAlgos.cpp index 0f1f2439d2..3551766207 100644 --- a/src/Mod/MeshPart/App/MeshAlgos.cpp +++ b/src/Mod/MeshPart/App/MeshAlgos.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) Juergen Riegel * + * Copyright (c) 2008 Juergen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/App/MeshAlgos.h b/src/Mod/MeshPart/App/MeshAlgos.h index d524f7effd..a0f482421d 100644 --- a/src/Mod/MeshPart/App/MeshAlgos.h +++ b/src/Mod/MeshPart/App/MeshAlgos.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) Juergen Riegel * + * Copyright (c) 2008 Juergen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/App/PreCompiled.cpp b/src/Mod/MeshPart/App/PreCompiled.cpp index 46269e9671..1e5d389dd2 100644 --- a/src/Mod/MeshPart/App/PreCompiled.cpp +++ b/src/Mod/MeshPart/App/PreCompiled.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/App/PreCompiled.h b/src/Mod/MeshPart/App/PreCompiled.h index 07eac4d2e8..754261f3a7 100644 --- a/src/Mod/MeshPart/App/PreCompiled.h +++ b/src/Mod/MeshPart/App/PreCompiled.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/Gui/AppMeshPartGui.cpp b/src/Mod/MeshPart/Gui/AppMeshPartGui.cpp index 5bd034c4eb..64aa198feb 100644 --- a/src/Mod/MeshPart/Gui/AppMeshPartGui.cpp +++ b/src/Mod/MeshPart/Gui/AppMeshPartGui.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/Gui/Command.cpp b/src/Mod/MeshPart/Gui/Command.cpp index 22f2ff929e..f91ff09030 100644 --- a/src/Mod/MeshPart/Gui/Command.cpp +++ b/src/Mod/MeshPart/Gui/Command.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py b/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py index ed0d1aec6e..8d09f86c82 100644 --- a/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py +++ b/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py @@ -1,3 +1,25 @@ +#*************************************************************************** +#* Copyright (c) 2017 Lorenz Lechner * +#* * +#* This file is part of the FreeCAD CAx development system. * +#* * +#* This library is free software; you can redistribute it and/or * +#* modify it under the terms of the GNU Library General Public * +#* License as published by the Free Software Foundation; either * +#* version 2 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 Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this library; see the file COPYING.LIB. If not, * +#* write to the Free Software Foundation, Inc., 59 Temple Place, * +#* Suite 330, Boston, MA 02111-1307, USA * +#* * +#***************************************************************************/ + import Mesh import FreeCAD as App import FreeCADGui as Gui diff --git a/src/Mod/MeshPart/Gui/PreCompiled.cpp b/src/Mod/MeshPart/Gui/PreCompiled.cpp index 46269e9671..1e5d389dd2 100644 --- a/src/Mod/MeshPart/Gui/PreCompiled.cpp +++ b/src/Mod/MeshPart/Gui/PreCompiled.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/Gui/PreCompiled.h b/src/Mod/MeshPart/Gui/PreCompiled.h index 7cef9ef237..9f90acb0d8 100644 --- a/src/Mod/MeshPart/Gui/PreCompiled.h +++ b/src/Mod/MeshPart/Gui/PreCompiled.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2008 Jürgen Riegel (juergen.riegel@web.de) * + * Copyright (c) 2008 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Mod/MeshPart/Init.py b/src/Mod/MeshPart/Init.py index c9c37c802b..dd8dea3aee 100644 --- a/src/Mod/MeshPart/Init.py +++ b/src/Mod/MeshPart/Init.py @@ -1,8 +1,8 @@ -# FreeCAD init script of the MeshPart module +# FreeCAD init script of the MeshPart module # (c) 2001 Juergen Riegel #*************************************************************************** -#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 * +#* Copyright (c) 2002 Juergen Riegel * #* * #* This file is part of the FreeCAD CAx development system. * #* * @@ -13,14 +13,13 @@ #* for detail see the LICENCE text file. * #* * #* FreeCAD is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* 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 Library General Public * -#* License along with FreeCAD; if not, write to the Free Software * +#* License along with FreeCAD; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * -#* Juergen Riegel 2002 * #***************************************************************************/ diff --git a/src/Mod/MeshPart/InitGui.py b/src/Mod/MeshPart/InitGui.py index 82a917f2ea..17d0102f1f 100644 --- a/src/Mod/MeshPart/InitGui.py +++ b/src/Mod/MeshPart/InitGui.py @@ -1,4 +1,4 @@ -# MeshPart gui init module +# MeshPart gui init module # (c) 2003 Juergen Riegel # # Gathering all the information to start FreeCAD @@ -6,7 +6,7 @@ # runs when the gui is up #*************************************************************************** -#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 * +#* Copyright (c) 2002 Juergen Riegel * #* * #* This file is part of the FreeCAD CAx development system. * #* * @@ -26,44 +26,46 @@ #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * -#* Juergen Riegel 2002 * #***************************************************************************/ class MeshPartWorkbench ( Workbench ): - "MeshPart workbench object" - Icon = """ - /* XPM */ - static const char *MeshPart_Box[]={ - "16 16 3 1", - ". c None", - "# c #000000", - "a c #c6c642", - "................", - ".......#######..", - "......#aaaaa##..", - ".....#aaaaa###..", - "....#aaaaa##a#..", - "...#aaaaa##aa#..", - "..#aaaaa##aaa#..", - ".########aaaa#..", - ".#aaaaa#aaaaa#..", - ".#aaaaa#aaaa##..", - ".#aaaaa#aaa##...", - ".#aaaaa#aa##....", - ".#aaaaa#a##... .", - ".#aaaaa###......", - ".########.......", - "................"}; - """ - MenuText = "MeshPart" - ToolTip = "MeshPart workbench" + "MeshPart workbench object" + Icon = """ + /* XPM */ + static const char *MeshPart_Box[]={ + "16 16 3 1", + ". c None", + "# c #000000", + "a c #c6c642", + "................", + ".......#######..", + "......#aaaaa##..", + ".....#aaaaa###..", + "....#aaaaa##a#..", + "...#aaaaa##aa#..", + "..#aaaaa##aaa#..", + ".########aaaa#..", + ".#aaaaa#aaaaa#..", + ".#aaaaa#aaaa##..", + ".#aaaaa#aaa##...", + ".#aaaaa#aa##....", + ".#aaaaa#a##... .", + ".#aaaaa###......", + ".########.......", + "................"}; + """ + MenuText = "MeshPart" + ToolTip = "MeshPart workbench" - def Initialize(self): - # load the module - import MeshPartGui - import MeshPart - def GetClassName(self): - return "MeshPartGui::Workbench" -#Gui.addWorkbench(MeshPartWorkbench()) + def Initialize(self): + # load the module + import MeshPartGui + import MeshPart + + + def GetClassName(self): + return "MeshPartGui::Workbench" + +# Gui.addWorkbench(MeshPartWorkbench()) From 6d2aa6acc9f58d8f8cf0d4744d9a0ed86ac18ed5 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 6 Nov 2021 14:31:17 +0100 Subject: [PATCH 126/138] Main: [skip ci] partially fix issue 0004765: FreeCAD failed to build on MSVC with std:c++latest --- src/Main/MainCmd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Main/MainCmd.cpp b/src/Main/MainCmd.cpp index fa230acacc..ae4e36ffd9 100644 --- a/src/Main/MainCmd.cpp +++ b/src/Main/MainCmd.cpp @@ -108,7 +108,7 @@ int main( int argc, char ** argv ) std::string appName = App::Application::Config()["ExeName"]; std::stringstream msg; msg << "While initializing " << appName << " the following exception occurred: '" << e.what() << "'\n\n"; - msg << "Python is searching for its runtime files in the following directories:\n" << Py_GetPath() << "\n\n"; + msg << "Python is searching for its runtime files in the following directories:\n" << Py_EncodeLocale(Py_GetPath(),nullptr) << "\n\n"; msg << "Python version information:\n" << Py_GetVersion() << "\n"; const char* pythonhome = getenv("PYTHONHOME"); if ( pythonhome ) { From 4f56ee263d34c5058d6de279a35ae7ef61c795d5 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 6 Nov 2021 17:48:35 +0100 Subject: [PATCH 127/138] Sketcher: fix some memory leaks --- src/Mod/Sketcher/Gui/CommandCreateGeo.cpp | 16 +++++++++++----- src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp | 2 +- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 2 ++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index f7d00610ee..ba1496e225 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -555,7 +555,8 @@ public: "conList.append(Sketcher.Constraint('Horizontal',%i))\n" "conList.append(Sketcher.Constraint('Vertical',%i))\n" "conList.append(Sketcher.Constraint('Vertical',%i))\n" - "%s.addConstraint(conList)\n", + "%s.addConstraint(conList)\n" + "del geoList, conList\n", EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y, // line 1 EditCurve[1].x,EditCurve[1].y,EditCurve[2].x,EditCurve[2].y, // line 2 EditCurve[2].x,EditCurve[2].y,EditCurve[3].x,EditCurve[3].y, // line 3 @@ -594,7 +595,8 @@ public: "conList.append(Sketcher.Constraint('Vertical',%i))\n" "conList.append(Sketcher.Constraint('Vertical',%i))\n" "conList.append(Sketcher.Constraint('Symmetric',%i,2,%i,1,%i,1))\n" - "%s.addConstraint(conList)\n", + "%s.addConstraint(conList)\n" + "del geoList, conList\n", EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y, // line 1 EditCurve[1].x,EditCurve[1].y,EditCurve[2].x,EditCurve[2].y, // line 2 EditCurve[2].x,EditCurve[2].y,EditCurve[3].x,EditCurve[3].y, // line 3 @@ -930,7 +932,8 @@ public: "conList.append(Sketcher.Constraint('Equal', %i, %i))\n" "conList.append(Sketcher.Constraint('Equal', %i, %i))\n" "conList.append(Sketcher.Constraint('Equal', %i, %i))\n" - "%s.addConstraint(conList)\n", + "%s.addConstraint(conList)\n" + "del geoList, conList\n", StartPos.x + (signX * radius), StartPos.y + (signY * radius), // center of the arc 1 radius, start, end, // start and end angle of arc1 @@ -984,7 +987,8 @@ public: "conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n" "conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n" "conList.append(Sketcher.Constraint('PointOnObject', %i, 1, %i, ))\n" - "%s.addConstraint(conList)\n", + "%s.addConstraint(conList)\n" + "del geoList, conList\n", StartPos.x, StartPos.y, // point at StartPos EndPos.x, EndPos.y, // point at EndPos Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), // the sketch @@ -4843,6 +4847,7 @@ public: } cstream << Gui::Command::getObjectCmd(sketchgui->getObject()) << ".addConstraint(conList)\n"; + cstream << "del conList\n"; Gui::Command::doCommand(Gui::Command::Doc, cstream.str().c_str()); @@ -7129,7 +7134,8 @@ public: "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n" "conList.append(Sketcher.Constraint('Tangent', %i, 2, %i, 1))\n" "conList.append(Sketcher.Constraint('Equal', %i, %i))\n" - "%s.addConstraint(conList)\n", + "%s.addConstraint(conList)\n" + "del geoList, conList\n", StartPos.x, StartPos.y, // center of the arc1 r, // radius arc1 start, end, // start and end angle of arc1 diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index acfb48c942..93ec845a5e 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -379,7 +379,7 @@ public: class ExpressionDelegate : public QStyledItemDelegate { public: - ExpressionDelegate(QListWidget * _view) : view(_view) { } + ExpressionDelegate(QListWidget * _view) : QStyledItemDelegate(_view), view(_view) { } protected: QPixmap getIcon(const char* name, const QSize& size) const { diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index c3a89f5c12..e22254d2af 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -6376,6 +6376,7 @@ bool ViewProviderSketch::setEdit(int ModNum) " tv.sketchClipPlane(ActiveSketch, ActiveSketch.ViewObject.SectionView)\n" "tv.hide(ActiveSketch)\n" "del(tv)\n" + "del(ActiveSketch)\n" ).arg(QString::fromLatin1(getDocument()->getDocument()->getName()), QString::fromLatin1(getSketchObject()->getNameInDocument()), QString::fromLatin1(Gui::Command::getObjectCmd(editObj).c_str()), @@ -6907,6 +6908,7 @@ void ViewProviderSketch::unsetEdit(int ModNum) " tv.restore()\n" "ActiveSketch.ViewObject.TempoVis = None\n" "del(tv)\n" + "del(ActiveSketch)\n" ).arg(QString::fromLatin1(getDocument()->getDocument()->getName())).arg( QString::fromLatin1(getSketchObject()->getNameInDocument())); QByteArray cmdstr_bytearray = cmdstr.toLatin1(); From 84274f5c805ecc289a78ab731a7e715b47546dc8 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 7 Nov 2021 08:27:33 +0100 Subject: [PATCH 128/138] PD: [skip ci] fix ambiguous shortcut overload in TaskDlgPipeParameters --- src/Mod/PartDesign/Gui/TaskPipeParameters.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp b/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp index 66465f3814..6dd611b873 100644 --- a/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPipeParameters.cpp @@ -94,6 +94,7 @@ TaskPipeParameters::TaskPipeParameters(ViewProviderPipe *PipeView, bool /*newObj // Create context menu QAction* remove = new QAction(tr("Remove"), this); remove->setShortcut(QKeySequence::Delete); + remove->setShortcutContext(Qt::WidgetShortcut); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) // display shortcut behind the context menu entry remove->setShortcutVisibleInContextMenu(true); @@ -587,6 +588,7 @@ TaskPipeOrientation::TaskPipeOrientation(ViewProviderPipe* PipeView, bool /*newO // Create context menu QAction* remove = new QAction(tr("Remove"), this); remove->setShortcut(QKeySequence::Delete); + remove->setShortcutContext(Qt::WidgetShortcut); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) // display shortcut behind the context menu entry remove->setShortcutVisibleInContextMenu(true); @@ -902,6 +904,7 @@ TaskPipeScaling::TaskPipeScaling(ViewProviderPipe* PipeView, bool /*newObj*/, QW // Create context menu QAction* remove = new QAction(tr("Remove"), this); remove->setShortcut(QKeySequence::Delete); + remove->setShortcutContext(Qt::WidgetShortcut); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) // display shortcut behind the context menu entry remove->setShortcutVisibleInContextMenu(true); From 0b4405e935bcc668a0f02801891f5022056d3772 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 7 Nov 2021 02:39:11 +0100 Subject: [PATCH 129/138] [PD] simplify Loft code - also two automatic style fixes made by MSVC --- src/Mod/PartDesign/Gui/TaskLoftParameters.cpp | 9 ++++----- src/Mod/PartDesign/Gui/TaskLoftParameters.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp b/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp index 0335944e4d..9ae610ef18 100644 --- a/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp @@ -55,7 +55,7 @@ using namespace Gui; /* TRANSLATOR PartDesignGui::TaskLoftParameters */ -TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView,bool /*newObj*/, QWidget *parent) +TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView, bool /*newObj*/, QWidget *parent) : TaskSketchBasedParameters(LoftView, parent, "PartDesign_AdditiveLoft", tr("Loft parameters")) , ui(new Ui_TaskLoftParameters) { @@ -129,16 +129,15 @@ TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView,bool /*newObj* for (QWidget* child : proxy->findChildren()) child->blockSignals(false); - updateUI(0); + updateUI(); } TaskLoftParameters::~TaskLoftParameters() { } -void TaskLoftParameters::updateUI(int index) +void TaskLoftParameters::updateUI() { - Q_UNUSED(index); } void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg) @@ -359,7 +358,7 @@ bool TaskDlgLoftParameters::accept() // TODO Fill this with commands (2015-09-11, Fat-Zer) PartDesign::Loft* pcLoft = static_cast(vp->getObject()); - for(App::DocumentObject* obj : pcLoft->Sections.getValues()) { + for (App::DocumentObject* obj : pcLoft->Sections.getValues()) { FCMD_OBJ_HIDE(obj); } diff --git a/src/Mod/PartDesign/Gui/TaskLoftParameters.h b/src/Mod/PartDesign/Gui/TaskLoftParameters.h index 20e977dd9e..e04011ef4b 100644 --- a/src/Mod/PartDesign/Gui/TaskLoftParameters.h +++ b/src/Mod/PartDesign/Gui/TaskLoftParameters.h @@ -67,7 +67,7 @@ protected: private: void onSelectionChanged(const Gui::SelectionChanges& msg); - void updateUI(int index); + void updateUI(); bool referenceSelected(const Gui::SelectionChanges& msg) const; void removeFromListWidget(QListWidget*w, QString name); void clearButtons(); From 0b77c2b2b5e2117ae6ebe61bccd2743748dc0e27 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 7 Nov 2021 19:16:46 +0100 Subject: [PATCH 130/138] [PD] fixes loft visibility on creation and modification - the PR fixes the issue reported here: https://forum.freecadweb.org/viewtopic.php?f=3&t=63252 - (also a MSVC code formatting fix) --- src/Mod/PartDesign/Gui/TaskLoftParameters.cpp | 11 +++++++---- src/Mod/PartDesign/Gui/TaskLoftParameters.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp b/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp index 9ae610ef18..8872cd069e 100644 --- a/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskLoftParameters.cpp @@ -121,10 +121,6 @@ TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView, bool /*newObj ui->checkBoxRuled->setChecked(loft->Ruled.getValue()); ui->checkBoxClosed->setChecked(loft->Closed.getValue()); - if (!loft->Sections.getValues().empty()) { - LoftView->makeTemporaryVisible(true); - } - // activate and de-activate dialog elements as appropriate for (QWidget* child : proxy->findChildren()) child->blockSignals(false); @@ -138,6 +134,10 @@ TaskLoftParameters::~TaskLoftParameters() void TaskLoftParameters::updateUI() { + // we must assure the changed loft is kept visible on section changes, + // see https://forum.freecadweb.org/viewtopic.php?f=3&t=63252 + PartDesign::Loft* loft = static_cast(vp->getObject()); + vp->makeTemporaryVisible(!loft->Sections.getValues().empty()); } void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg) @@ -172,6 +172,7 @@ void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg) clearButtons(); exitSelectionMode(); + updateUI(); } } @@ -255,6 +256,7 @@ void TaskLoftParameters::onDeleteSection() //static_cast(vp)->highlightReferences(false, true); recomputeFeature(); + updateUI(); } } } @@ -278,6 +280,7 @@ void TaskLoftParameters::indexesMoved() loft->Sections.setValues(originals); recomputeFeature(); + updateUI(); } void TaskLoftParameters::clearButtons() { diff --git a/src/Mod/PartDesign/Gui/TaskLoftParameters.h b/src/Mod/PartDesign/Gui/TaskLoftParameters.h index e04011ef4b..a3dd8c5ccb 100644 --- a/src/Mod/PartDesign/Gui/TaskLoftParameters.h +++ b/src/Mod/PartDesign/Gui/TaskLoftParameters.h @@ -50,7 +50,7 @@ class TaskLoftParameters : public TaskSketchBasedParameters Q_OBJECT public: - TaskLoftParameters(ViewProviderLoft *LoftView,bool newObj=false,QWidget *parent = 0); + TaskLoftParameters(ViewProviderLoft *LoftView, bool newObj=false, QWidget *parent = 0); ~TaskLoftParameters(); private Q_SLOTS: From 058962362d1f0c2e8c064832755eb3df3fd9b5d7 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sun, 7 Nov 2021 17:13:05 -0600 Subject: [PATCH 131/138] OpenSCAD: Refactor tests to all use the same code path for tempdir generation --- .../OpenSCADTest/app/test_importCSG.py | 166 +++++++----------- 1 file changed, 61 insertions(+), 105 deletions(-) diff --git a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py index 59759a7c47..24dda152a2 100644 --- a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py +++ b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py @@ -38,6 +38,7 @@ __url__ = "https://www.freecadweb.org" class TestImportCSG(unittest.TestCase): MODULE = 'test_importCSG' # file name without extension + temp_dir = tempfile.TemporaryDirectory() def setUp(self): @@ -73,113 +74,78 @@ class TestImportCSG(unittest.TestCase): FreeCAD.closeDocument("CSG") + def utility_create_scad(self, scadCode, name): + filename = self.temp_dir.name + os.path.sep + name + ".scad" + print (f"Creating {filename}") + f = open(filename,"w+") + f.write(scadCode) + f.close() + return importCSG.open(filename) + def test_import_sphere(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "sphere.scad" - f = open(filename,"w+") - f.write("sphere(10.0);") - f.close() - doc = importCSG.open(filename) - sphere = doc.getObject("sphere") - self.assertTrue (sphere is not None) - self.assertTrue (sphere.Radius == 10.0) - FreeCAD.closeDocument(doc.Name) + doc = self.utility_create_scad("sphere(10.0);","sphere") + sphere = doc.getObject("sphere") + self.assertTrue (sphere is not None) + self.assertTrue (sphere.Radius == 10.0) + FreeCAD.closeDocument(doc.Name) def test_import_cylinder(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "cylinder.scad" - f = open(filename,"w+") - f.write("cylinder(50.0,d=10.0);") - f.close() - doc = importCSG.open(filename) - cylinder = doc.getObject("cylinder") - self.assertTrue (cylinder is not None) - self.assertTrue (cylinder.Radius == 5.0) - self.assertTrue (cylinder.Height == 50.0) - FreeCAD.closeDocument(doc.Name) + doc = self.utility_create_scad("cylinder(50.0,d=10.0);","cylinder") + cylinder = doc.getObject("cylinder") + self.assertTrue (cylinder is not None) + self.assertTrue (cylinder.Radius == 5.0) + self.assertTrue (cylinder.Height == 50.0) + FreeCAD.closeDocument(doc.Name) def test_import_cube(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "cube.scad" - f = open(filename,"w+") - f.write("cube([1.0,2.0,3.0]);") - f.close() - doc = importCSG.open(filename) - cube = doc.getObject("cube") - self.assertTrue (cube is not None) - self.assertTrue (cube.Length == 1.0) - self.assertTrue (cube.Width == 2.0) - self.assertTrue (cube.Height == 3.0) - FreeCAD.closeDocument(doc.Name) + doc = self.utility_create_scad("cube([1.0,2.0,3.0]);","cube") + cube = doc.getObject("cube") + self.assertTrue (cube is not None) + self.assertTrue (cube.Length == 1.0) + self.assertTrue (cube.Width == 2.0) + self.assertTrue (cube.Height == 3.0) + FreeCAD.closeDocument(doc.Name) def test_import_circle(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "circle.scad" - f = open(filename,"w+") - f.write("circle(10.0);") - f.close() - doc = importCSG.open(filename) - circle = doc.getObject("circle") - self.assertTrue (circle is not None) - self.assertTrue (circle.Radius == 10.0) - FreeCAD.closeDocument(doc.Name) + doc = self.utility_create_scad("circle(10.0);","circle") + circle = doc.getObject("circle") + self.assertTrue (circle is not None) + self.assertTrue (circle.Radius == 10.0) + FreeCAD.closeDocument(doc.Name) def test_import_square(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "square.scad" - f = open(filename,"w+") - f.write("square([1.0,2.0]);") - f.close() - doc = importCSG.open(filename) - square = doc.getObject("square") - self.assertTrue (square is not None) - self.assertTrue (square.Length == 1.0) - self.assertTrue (square.Width == 2.0) - FreeCAD.closeDocument(doc.Name) + doc = self.utility_create_scad("square([1.0,2.0]);","square") + square = doc.getObject("square") + self.assertTrue (square is not None) + self.assertTrue (square.Length == 1.0) + self.assertTrue (square.Width == 2.0) + FreeCAD.closeDocument(doc.Name) def test_import_text(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "text.scad" - f = open(filename,"w+") - f.write("text(\"X\");") # Keep it short to keep the test fast-ish - f.close() - try: - doc = importCSG.open(filename) - text = doc.getObject("text") - self.assertTrue (text is not None) - FreeCAD.closeDocument(doc.Name) - except Exception: - pass # We may not have the DXF importer available + try: + doc = self.utility_create_scad("text(\"X\");","text") # Keep it short to keep the test fast-ish + text = doc.getObject("text") + self.assertTrue (text is not None) + FreeCAD.closeDocument(doc.Name) + except Exception: + pass # We may not have the DXF importer available def test_import_polygon_nopath(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "polygon_nopath.scad" - f = open(filename,"w+") - f.write("polygon(points=[[0,0],[100,0],[130,50],[30,50]]);") - f.close() - doc = importCSG.open(filename) - polygon = doc.getObject("polygon") - self.assertTrue (polygon is not None) - self.assertAlmostEqual (polygon.Shape.Area, 5000.0) - FreeCAD.closeDocument(doc.Name) + doc = self.utility_create_scad("polygon(points=[[0,0],[100,0],[130,50],[30,50]]);","polygon_nopath") + polygon = doc.getObject("polygon") + self.assertTrue (polygon is not None) + self.assertAlmostEqual (polygon.Shape.Area, 5000.0) + FreeCAD.closeDocument(doc.Name) def test_import_polygon_path(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "polygon_path.scad" - f = open(filename,"w+") - f.write("polygon([[0,0],[100,0],[130,50],[30,50]], paths=[[0,1,2,3]]);") - f.close() - doc = importCSG.open(filename) - wire = doc.ActiveObject # With paths, the polygon gets created as a wire... - self.assertTrue (wire is not None) - self.assertAlmostEqual (wire.Shape.Area, 5000.0) - FreeCAD.closeDocument(doc.Name) + doc = self.utility_create_scad("polygon([[0,0],[100,0],[130,50],[30,50]], paths=[[0,1,2,3]]);","polygon_path") + wire = doc.ActiveObject # With paths, the polygon gets created as a wire... + self.assertTrue (wire is not None) + self.assertAlmostEqual (wire.Shape.Area, 5000.0) + FreeCAD.closeDocument(doc.Name) def test_import_polyhedron(self): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + "polyhedron.scad" - f = open(filename,"w+") - f.write( + doc = self.utility_create_scad( """ polyhedron( points=[ [10,10,0],[10,-10,0],[-10,-10,0],[-10,10,0], // the four points at base @@ -187,22 +153,12 @@ polyhedron( faces=[ [0,1,4],[1,2,4],[2,3,4],[3,0,4], // each triangle side [1,0,3],[2,1,3] ] // two triangles for square base ); -""" +""","polyhedron" ) - f.close() - doc = importCSG.open(filename) - polyhedron = doc.ActiveObject # With paths, the polygon gets created as a wire... - self.assertTrue (polyhedron is not None) - self.assertAlmostEqual (polyhedron.Shape.Volume, 1333.3333, 4) - FreeCAD.closeDocument(doc.Name) - - def utility_create_scad(self, scadCode, name): - with tempfile.TemporaryDirectory() as temp_dir: - filename = temp_dir + os.path.sep + name + ".scad" - f = open(filename,"w+") - f.write(scadCode) - f.close() - return importCSG.open(filename) + polyhedron = doc.ActiveObject # With paths, the polygon gets created as a wire... + self.assertTrue (polyhedron is not None) + self.assertAlmostEqual (polyhedron.Shape.Volume, 1333.3333, 4) + FreeCAD.closeDocument(doc.Name) def test_import_difference(self): doc = self.utility_create_scad("difference() { cube(15, center=true); sphere(10); }", "difference") From 02c1277a1d531bc2f8855c684131a813f5f34447 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sun, 7 Nov 2021 21:50:48 -0600 Subject: [PATCH 132/138] OpenSCAD: Minor cleanup of import code --- src/Mod/OpenSCAD/OpenSCADFeatures.py | 257 ++++++++++++++------------- src/Mod/OpenSCAD/importCSG.py | 16 +- 2 files changed, 136 insertions(+), 137 deletions(-) diff --git a/src/Mod/OpenSCAD/OpenSCADFeatures.py b/src/Mod/OpenSCAD/OpenSCADFeatures.py index 93332b67a2..4f3f4d21d8 100644 --- a/src/Mod/OpenSCAD/OpenSCADFeatures.py +++ b/src/Mod/OpenSCAD/OpenSCADFeatures.py @@ -200,13 +200,11 @@ class Resize : self.createGeometry(fp) def createGeometry(self, fp) : - print("Resize create Geometry") import FreeCAD mat = FreeCAD.Matrix() mat.A11 = self.Vector[0] mat.A22 = self.Vector[1] mat.A33 = self.Vector[2] - print(mat) fp.Shape = self.Target.Shape.transformGeometry(mat) def __getstate__(self): @@ -387,7 +385,7 @@ class Frustum: fp.Placement = plm class Twist: - def __init__(self, obj,child=None,h=1.0,angle=0.0,scale=[1.0,1.0]): + def __init__(self, obj, child=None, h=1.0, angle=0.0, scale=[1.0,1.0]): import FreeCAD obj.addProperty("App::PropertyLink","Base","Base", "The base object that must be transformed") @@ -406,10 +404,10 @@ class Twist: self.createGeometry(fp) def onChanged(self, fp, prop): - if prop in ["Angle","Height"]: + if prop in ["Angle","Height","Scale"]: self.createGeometry(fp) - def createGeometry(self,fp): + def createGeometry(self, fp): import FreeCAD,Part,math,sys if fp.Base and fp.Height and fp.Base.Shape.isValid(): solids = [] @@ -443,7 +441,6 @@ class Twist: pipe_shell.add(wire1) pipe_shell.add(wire2) pipe_shell.setAuxiliarySpine(auxiliary_spine,True,0) - print(pipe_shell.getStatus()) assert(pipe_shell.isReady()) pipe_shell.build() faces.extend(pipe_shell.shape().Faces) @@ -534,10 +531,9 @@ class PrismaticToroid: solid = Part.makeSolid (clean_shell) if solid.Volume < 0: solid.reverse() - print (f"Solid volume is {solid.Volume}") solids.append(solid) except Part.OCCError: - print ("Could not create solid: creating compound instead") + FreeCAD.Console.PrintWarning("Could not create solid: creating compound instead") solids.append(Part.Compound(faces)) fp.Shape = Part.Compound(solids) @@ -587,139 +583,146 @@ class CGALFeature: raise ValueError def makeSurfaceVolume(filename): - import FreeCAD,Part,sys + import FreeCAD + import Part + import sys + coords = [] with open(filename) as f1: - coords = [] min_z = sys.float_info.max for line in f1.readlines(): - sline=line.strip() + sline = line.strip() if sline and not sline.startswith('#'): - ycoord=len(coords) - lcoords=[] + ycoord = len(coords) + lcoords = [] for xcoord, num in enumerate(sline.split()): - fnum=float(num) + fnum = float(num) lcoords.append(FreeCAD.Vector(float(xcoord),float(ycoord),fnum)) min_z = min(fnum,min_z) coords.append(lcoords) - - num_rows = len(coords) - num_cols = len(coords[0]) - # OpenSCAD does not spline this surface, so neither do we: just create a bunch of faces, - # using four triangles per quadrilateral - faces = [] - for row in range(num_rows-1): - for col in range(num_cols-1): - a = coords[row+0][col+0] - b = coords[row+0][col+1] - c = coords[row+1][col+1] - d = coords[row+1][col+0] - centroid = 0.25 * (a + b + c + d) - ab = Part.makeLine(a,b) - bc = Part.makeLine(b,c) - cd = Part.makeLine(c,d) - da = Part.makeLine(d,a) + num_rows = len(coords) + if num_rows == 0: + FreeCAD.Console.PrintWarning(f"No data found in surface file {filename}") + return None,0,0 + num_cols = len(coords[0]) - diag_a = Part.makeLine(a, centroid) - diag_b = Part.makeLine(b, centroid) - diag_c = Part.makeLine(c, centroid) - diag_d = Part.makeLine(d, centroid) + # OpenSCAD does not spline this surface, so neither do we: just create a + # bunch of faces, + # using four triangles per quadrilateral + faces = [] + for row in range(num_rows - 1): + for col in range(num_cols - 1): + a = coords[row + 0][col + 0] + b = coords[row + 0][col + 1] + c = coords[row + 1][col + 1] + d = coords[row + 1][col + 0] + centroid = 0.25 * (a + b + c + d) + ab = Part.makeLine(a,b) + bc = Part.makeLine(b,c) + cd = Part.makeLine(c,d) + da = Part.makeLine(d,a) - wire1 = Part.Wire([ab,diag_a,diag_b]) - wire2 = Part.Wire([bc,diag_b,diag_c]) - wire3 = Part.Wire([cd,diag_c,diag_d]) - wire4 = Part.Wire([da,diag_d,diag_a]) + diag_a = Part.makeLine(a, centroid) + diag_b = Part.makeLine(b, centroid) + diag_c = Part.makeLine(c, centroid) + diag_d = Part.makeLine(d, centroid) - try: - face = Part.Face(wire1) - faces.append(face) - face = Part.Face(wire2) - faces.append(face) - face = Part.Face(wire3) - faces.append(face) - face = Part.Face(wire4) - faces.append(face) - except Exception: - print ("Failed to create the face from {},{},{},{}".format(coords[row+0][col+0],\ - coords[row+0][col+1],coords[row+1][col+1],coords[row+1][col+0])) - - last_row = num_rows-1 - last_col = num_cols-1 + wire1 = Part.Wire([ab,diag_a,diag_b]) + wire2 = Part.Wire([bc,diag_b,diag_c]) + wire3 = Part.Wire([cd,diag_c,diag_d]) + wire4 = Part.Wire([da,diag_d,diag_a]) - # Create the face to close off the y-min border: OpenSCAD places the lower surface of the shell - # at 1 unit below the lowest coordinate in the surface - lines = [] - corner1 = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z-1) - lines.append (Part.makeLine(corner1,coords[0][0])) - for col in range(num_cols-1): - a = coords[0][col] - b = coords[0][col+1] - lines.append (Part.makeLine(a, b)) - corner2 = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z-1) - lines.append (Part.makeLine(corner2,coords[0][last_col])) - lines.append (Part.makeLine(corner1,corner2)) - wire = Part.Wire(lines) - face = Part.Face(wire) - faces.append(face) - - # Create the face to close off the y-max border - lines = [] - corner1 = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z-1) - lines.append (Part.makeLine(corner1,coords[last_row][0])) - for col in range(num_cols-1): - a = coords[last_row][col] - b = coords[last_row][col+1] - lines.append (Part.makeLine(a, b)) - corner2 = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z-1) - lines.append (Part.makeLine(corner2,coords[last_row][last_col])) - lines.append (Part.makeLine(corner1,corner2)) - wire = Part.Wire(lines) - face = Part.Face(wire) - faces.append(face) + try: + face = Part.Face(wire1) + faces.append(face) + face = Part.Face(wire2) + faces.append(face) + face = Part.Face(wire3) + faces.append(face) + face = Part.Face(wire4) + faces.append(face) + except Exception: + FreeCAD.Console.PrintWarning("Failed to create the face from {},{},{},{}".format(coords[row + 0][col + 0],\ + coords[row + 0][col + 1],coords[row + 1][col + 1],coords[row + 1][col + 0])) - # Create the face to close off the x-min border - lines = [] - corner1 = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z-1) - lines.append (Part.makeLine(corner1,coords[0][0])) - for row in range(num_rows-1): - a = coords[row][0] - b = coords[row+1][0] - lines.append (Part.makeLine(a, b)) - corner2 = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z-1) - lines.append (Part.makeLine(corner2,coords[last_row][0])) - lines.append (Part.makeLine(corner1,corner2)) - wire = Part.Wire(lines) - face = Part.Face(wire) - faces.append(face) + last_row = num_rows - 1 + last_col = num_cols - 1 - # Create the face to close off the x-max border - lines = [] - corner1 = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z-1) - lines.append (Part.makeLine(corner1,coords[0][last_col])) - for row in range(num_rows-1): - a = coords[row][last_col] - b = coords[row+1][last_col] - lines.append (Part.makeLine(a, b)) - corner2 = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z-1) - lines.append (Part.makeLine(corner2,coords[last_row][last_col])) - lines.append (Part.makeLine(corner1,corner2)) - wire = Part.Wire(lines) - face = Part.Face(wire) - faces.append(face) + # Create the face to close off the y-min border: OpenSCAD places the lower + # surface of the shell + # at 1 unit below the lowest coordinate in the surface + lines = [] + corner1 = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z - 1) + lines.append(Part.makeLine(corner1,coords[0][0])) + for col in range(num_cols - 1): + a = coords[0][col] + b = coords[0][col + 1] + lines.append(Part.makeLine(a, b)) + corner2 = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z - 1) + lines.append(Part.makeLine(corner2,coords[0][last_col])) + lines.append(Part.makeLine(corner1,corner2)) + wire = Part.Wire(lines) + face = Part.Face(wire) + faces.append(face) - # Create a bottom surface to close off the shell - a = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z-1) - b = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z-1) - c = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z-1) - d = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z-1) - ab = Part.makeLine(a,b) - bc = Part.makeLine(b,c) - cd = Part.makeLine(c,d) - da = Part.makeLine(d,a) - wire = Part.Wire([ab,bc,cd,da]) - face = Part.Face(wire) - faces.append(face) + # Create the face to close off the y-max border + lines = [] + corner1 = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z - 1) + lines.append(Part.makeLine(corner1,coords[last_row][0])) + for col in range(num_cols - 1): + a = coords[last_row][col] + b = coords[last_row][col + 1] + lines.append(Part.makeLine(a, b)) + corner2 = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z - 1) + lines.append(Part.makeLine(corner2,coords[last_row][last_col])) + lines.append(Part.makeLine(corner1,corner2)) + wire = Part.Wire(lines) + face = Part.Face(wire) + faces.append(face) - s = Part.Shell(faces) - solid = Part.Solid(s) - return solid,last_col,last_row + # Create the face to close off the x-min border + lines = [] + corner1 = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z - 1) + lines.append(Part.makeLine(corner1,coords[0][0])) + for row in range(num_rows - 1): + a = coords[row][0] + b = coords[row + 1][0] + lines.append(Part.makeLine(a, b)) + corner2 = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z - 1) + lines.append(Part.makeLine(corner2,coords[last_row][0])) + lines.append(Part.makeLine(corner1,corner2)) + wire = Part.Wire(lines) + face = Part.Face(wire) + faces.append(face) + + # Create the face to close off the x-max border + lines = [] + corner1 = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z - 1) + lines.append(Part.makeLine(corner1,coords[0][last_col])) + for row in range(num_rows - 1): + a = coords[row][last_col] + b = coords[row + 1][last_col] + lines.append(Part.makeLine(a, b)) + corner2 = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z - 1) + lines.append(Part.makeLine(corner2,coords[last_row][last_col])) + lines.append(Part.makeLine(corner1,corner2)) + wire = Part.Wire(lines) + face = Part.Face(wire) + faces.append(face) + + # Create a bottom surface to close off the shell + a = FreeCAD.Vector(coords[0][0].x, coords[0][0].y, min_z - 1) + b = FreeCAD.Vector(coords[0][last_col].x, coords[0][last_col].y, min_z - 1) + c = FreeCAD.Vector(coords[last_row][last_col].x, coords[last_row][last_col].y, min_z - 1) + d = FreeCAD.Vector(coords[last_row][0].x, coords[last_row][0].y, min_z - 1) + ab = Part.makeLine(a,b) + bc = Part.makeLine(b,c) + cd = Part.makeLine(c,d) + da = Part.makeLine(d,a) + wire = Part.Wire([ab,bc,cd,da]) + face = Part.Face(wire) + faces.append(face) + + s = Part.Shell(faces) + solid = Part.Solid(s) + return solid,last_col,last_row diff --git a/src/Mod/OpenSCAD/importCSG.py b/src/Mod/OpenSCAD/importCSG.py index c59dc778ce..9007750f34 100644 --- a/src/Mod/OpenSCAD/importCSG.py +++ b/src/Mod/OpenSCAD/importCSG.py @@ -79,7 +79,7 @@ def setColorRecursively(obj, color, transp): "Part::Common", "Part::MultiCommon"] if obj.TypeId in boolean_features: for currentObject in obj.OutList: - print(f"Fixing up colors for: {currentObject.FullName}") + if printverbose: print(f"Fixing up colors for: {currentObject.FullName}") if currentObject not in hassetcolor: setColorRecursively(currentObject, color, transp) @@ -767,14 +767,15 @@ def p_linear_extrude_with_transform(p): 'linear_extrude_with_transform : linear_extrude LPAREN keywordargument_list RPAREN OBRACE block_list EBRACE' if printverbose: print("Linear Extrude With Transform") h = float(p[3]['height']) - s = 1.0 + if printverbose: print("Height : ",h) + s = [1.0,1.0] t = 0.0 - if printverbose: print("Twist : ",p[3]) if 'scale' in p[3]: s = [float(p[3]['scale'][0]), float(p[3]['scale'][1])] - print ("Scale: " + str(s)) + if printverbose: print ("Scale: " + str(s)) if 'twist' in p[3]: t = float(p[3]['twist']) + if printverbose: print("Twist : ",t) # Test if null object like from null text if (len(p[6]) == 0) : p[0] = [] @@ -783,7 +784,7 @@ def p_linear_extrude_with_transform(p): obj = fuse(p[6],"Linear Extrude Union") else : obj = p[6][0] - if t != 0.0 or s != 1.0: + if t != 0.0 or s[0] != 1.0 or s[1] != 1.0: newobj = process_linear_extrude_with_transform(obj,h,t,s) else: newobj = process_linear_extrude(obj,h) @@ -1286,11 +1287,7 @@ def p_polyhedron_action(p) : pp =[v2(v[k]) for k in i] # Add first point to end of list to close polygon pp.append(pp[0]) - print("pp") - print(pp) w = Part.makePolygon(pp) - print("w") - print(w) try: f = Part.Face(w) except Exception: @@ -1315,7 +1312,6 @@ def p_projection_action(p) : for shape in p[6]: shape.Shape.tessellate(0.05) bbox.add(shape.Shape.BoundBox) - print (bbox) plane = doc.addObject("Part::Plane","xy_plane_used_for_projection") plane.Length = bbox.XLength plane.Width = bbox.YLength From 0b4e1ea55696e24cb34ee100c773f69f21bc682e Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 8 Nov 2021 10:51:10 +0100 Subject: [PATCH 133/138] Draft: Fixed DWG import's QCAD config --- src/Mod/Draft/importDWG.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/importDWG.py b/src/Mod/Draft/importDWG.py index 288dcb0a86..4fb042784f 100644 --- a/src/Mod/Draft/importDWG.py +++ b/src/Mod/Draft/importDWG.py @@ -249,7 +249,7 @@ def convertToDxf(dwgfilename): outdir = tempfile.mkdtemp() basename = os.path.basename(dwgfilename) result = outdir + os.sep + os.path.splitext(basename)[0] + ".dxf" - proc = subprocess.Popen((path, "-o", result, dwgfilename)) + proc = subprocess.Popen((path, "-f", "-o", result, dwgfilename)) proc.communicate() return result except Exception: From 11c5954ebf705e3bf9111b8336e1212e4580e81f Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 8 Nov 2021 11:10:13 +0100 Subject: [PATCH 134/138] App: split Application::ParseOptions into two functions to simplify handling of options that should be processed before determining user directories --- src/App/Application.cpp | 658 ++++++++++++++++++++-------------------- src/App/Application.h | 2 - 2 files changed, 331 insertions(+), 329 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index faac993af3..d47e10666e 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2009,6 +2009,332 @@ void Application::initTypes(void) new ExceptionProducer; } +namespace { +pair customSyntax(const string& s) +{ +#if defined(FC_OS_MACOSX) + if (s.find("-psn_") == 0) + return make_pair(string("psn"), s.substr(5)); +#endif + if (s.find("-display") == 0) + return make_pair(string("display"), string("null")); + else if (s.find("-style") == 0) + return make_pair(string("style"), string("null")); + else if (s.find("-graphicssystem") == 0) + return make_pair(string("graphicssystem"), string("null")); + else if (s.find("-widgetcount") == 0) + return make_pair(string("widgetcount"), string("")); + else if (s.find("-geometry") == 0) + return make_pair(string("geometry"), string("null")); + else if (s.find("-font") == 0) + return make_pair(string("font"), string("null")); + else if (s.find("-fn") == 0) + return make_pair(string("fn"), string("null")); + else if (s.find("-background") == 0) + return make_pair(string("background"), string("null")); + else if (s.find("-bg") == 0) + return make_pair(string("bg"), string("null")); + else if (s.find("-foreground") == 0) + return make_pair(string("foreground"), string("null")); + else if (s.find("-fg") == 0) + return make_pair(string("fg"), string("null")); + else if (s.find("-button") == 0) + return make_pair(string("button"), string("null")); + else if (s.find("-btn") == 0) + return make_pair(string("btn"), string("null")); + else if (s.find("-name") == 0) + return make_pair(string("name"), string("null")); + else if (s.find("-title") == 0) + return make_pair(string("title"), string("null")); + else if (s.find("-visual") == 0) + return make_pair(string("visual"), string("null")); +// else if (s.find("-ncols") == 0) +// return make_pair(string("ncols"), boost::program_options::value(1)); +// else if (s.find("-cmap") == 0) +// return make_pair(string("cmap"), string("null")); + else if ('@' == s[0]) + return std::make_pair(string("response-file"), s.substr(1)); + else + return make_pair(string(), string()); +} + +void parseProgramOptions(int ac, char ** av, const string& exe, variables_map& vm) +{ + // Declare a group of options that will be + // allowed only on the command line + options_description generic("Generic options"); + generic.add_options() + ("version,v", "Prints version string") + ("help,h", "Prints help message") + ("console,c", "Starts in console mode") + ("response-file", value(),"Can be specified with '@name', too") + ("dump-config", "Dumps configuration") + ("get-config", value(), "Prints the value of the requested configuration key") + ; + + // Declare a group of options that will be + // allowed both on the command line and in + // the config file + std::stringstream descr; + descr << "Writes " << exe << ".log to the user directory."; + boost::program_options::options_description config("Configuration"); + config.add_options() + //("write-log,l", value(), "write a log file") + ("write-log,l", descr.str().c_str()) + ("log-file", value(), "Unlike --write-log this allows logging to an arbitrary file") + ("user-cfg,u", value(),"User config file to load/save user settings") + ("system-cfg,s", value(),"System config file to load/save system settings") + ("run-test,t", value() ,"Test case - or 0 for all") + ("module-path,M", value< vector >()->composing(),"Additional module paths") + ("python-path,P", value< vector >()->composing(),"Additional python paths") + ("single-instance", "Allow to run a single instance of the application") + ; + + + // Hidden options, will be allowed both on the command line and + // in the config file, but will not be shown to the user. + boost::program_options::options_description hidden("Hidden options"); + hidden.add_options() + ("input-file", boost::program_options::value< vector >(), "input file") + ("output", boost::program_options::value(),"output file") + ("hidden", "don't show the main window") + // this are to ignore for the window system (QApplication) + ("style", boost::program_options::value< string >(), "set the application GUI style") + ("stylesheet", boost::program_options::value< string >(), "set the application stylesheet") + ("session", boost::program_options::value< string >(), "restore the application from an earlier session") + ("reverse", "set the application's layout direction from right to left") + ("widgetcount", "print debug messages about widgets") + ("graphicssystem", boost::program_options::value< string >(), "backend to be used for on-screen widgets and pixmaps") + ("display", boost::program_options::value< string >(), "set the X-Server") + ("geometry ", boost::program_options::value< string >(), "set the X-Window geometry") + ("font", boost::program_options::value< string >(), "set the X-Window font") + ("fn", boost::program_options::value< string >(), "set the X-Window font") + ("background", boost::program_options::value< string >(), "set the X-Window background color") + ("bg", boost::program_options::value< string >(), "set the X-Window background color") + ("foreground", boost::program_options::value< string >(), "set the X-Window foreground color") + ("fg", boost::program_options::value< string >(), "set the X-Window foreground color") + ("button", boost::program_options::value< string >(), "set the X-Window button color") + ("btn", boost::program_options::value< string >(), "set the X-Window button color") + ("name", boost::program_options::value< string >(), "set the X-Window name") + ("title", boost::program_options::value< string >(), "set the X-Window title") + ("visual", boost::program_options::value< string >(), "set the X-Window to color scheme") + ("ncols", boost::program_options::value< int >(), "set the X-Window to color scheme") + ("cmap", "set the X-Window to color scheme") +#if defined(FC_OS_MACOSX) + ("psn", boost::program_options::value< string >(), "process serial number") +#endif + ; + + // Ignored options, will be safely ignored. Mostly used by underlying libs. + //boost::program_options::options_description x11("X11 options"); + //x11.add_options() + // ("display", boost::program_options::value< string >(), "set the X-Server") + // ; + //0000723: improper handling of qt specific command line arguments + std::vector args; + bool merge=false; + for (int i=1; i().c_str()); + if (!ifs) { + Base::Console().Error("Could no open the response file\n"); + std::stringstream str; + str << "Could no open the response file: '" + << vm["response-file"].as() << "'" << endl; + throw Base::UnknownProgramOption(str.str()); + } + // Read the whole file into a string + stringstream ss; + ss << ifs.rdbuf(); + // Split the file content + char_separator sep(" \n\r"); + tokenizer > tok(ss.str(), sep); + vector args; + copy(tok.begin(), tok.end(), back_inserter(args)); + // Parse the file and store the options + store( boost::program_options::command_line_parser(args). + options(cmdline_options).positional(p).extra_parser(customSyntax).run(), vm); + } +} + +void processProgramOptions(const variables_map& vm, std::map& mConfig) +{ + if (vm.count("version")) { + std::stringstream str; + str << mConfig["ExeName"] << " " << mConfig["ExeVersion"] + << " Revision: " << mConfig["BuildRevision"] << std::endl; + throw Base::ProgramInformation(str.str()); + } + + if (vm.count("console")) { + mConfig["Console"] = "1"; + mConfig["RunMode"] = "Cmd"; + } + + if (vm.count("module-path")) { + vector Mods = vm["module-path"].as< vector >(); + string temp; + for (vector::const_iterator It= Mods.begin();It != Mods.end();++It) + temp += *It + ";"; + temp.erase(temp.end()-1); + mConfig["AdditionalModulePaths"] = temp; + } + + if (vm.count("python-path")) { + vector Paths = vm["python-path"].as< vector >(); + for (vector::const_iterator It= Paths.begin();It != Paths.end();++It) + Base::Interpreter().addPythonPath(It->c_str()); + } + + if (vm.count("input-file")) { + vector files(vm["input-file"].as< vector >()); + int OpenFileCount=0; + for (vector::const_iterator It = files.begin();It != files.end();++It) { + + //cout << "Input files are: " + // << vm["input-file"].as< vector >() << "\n"; + + std::ostringstream temp; + temp << "OpenFile" << OpenFileCount; + mConfig[temp.str()] = *It; + OpenFileCount++; + } + std::ostringstream buffer; + buffer << OpenFileCount; + mConfig["OpenFileCount"] = buffer.str(); + } + + if (vm.count("output")) { + string file = vm["output"].as(); + mConfig["SaveFile"] = file; + } + + if (vm.count("hidden")) { + mConfig["StartHidden"] = "1"; + } + + if (vm.count("write-log")) { + mConfig["LoggingFile"] = "1"; + //mConfig["LoggingFileName"] = vm["write-log"].as(); + mConfig["LoggingFileName"] = mConfig["UserAppData"] + mConfig["ExeName"] + ".log"; + } + + if (vm.count("log-file")) { + mConfig["LoggingFile"] = "1"; + mConfig["LoggingFileName"] = vm["log-file"].as(); + } + + if (vm.count("user-cfg")) { + mConfig["UserParameter"] = vm["user-cfg"].as(); + } + + if (vm.count("system-cfg")) { + mConfig["SystemParameter"] = vm["system-cfg"].as(); + } + + if (vm.count("run-test")) { + string testCase = vm["run-test"].as(); + if ( "0" == testCase) { + testCase = "TestApp.All"; + } + mConfig["TestCase"] = testCase; + mConfig["RunMode"] = "Internal"; + mConfig["ScriptFileName"] = "FreeCADTest"; + //sScriptName = FreeCADTest; + } + + if (vm.count("single-instance")) { + mConfig["SingleInstance"] = "1"; + } + + if (vm.count("dump-config")) { + std::stringstream str; + for (std::map::iterator it=mConfig.begin(); it != mConfig.end(); ++it) { + str << it->first << "=" << it->second << std::endl; + } + throw Base::ProgramInformation(str.str()); + } + + if (vm.count("get-config")) { + std::string configKey = vm["get-config"].as(); + std::stringstream str; + std::map::iterator pos; + pos = mConfig.find(configKey); + if (pos != mConfig.end()) { + str << pos->second; + } + str << std::endl; + throw Base::ProgramInformation(str.str()); + } +} +} + void Application::initConfig(int argc, char ** argv) { // find the home path.... @@ -2047,6 +2373,9 @@ void Application::initConfig(int argc, char ** argv) } } + variables_map vm; + parseProgramOptions(argc, argv, mConfig["ExeName"], vm); + // extract home paths ExtractUserPath(); @@ -2065,8 +2394,8 @@ void Application::initConfig(int argc, char ** argv) else Base::Console().Warning("Encoding of Python paths failed\n"); - // Parse the options that have impact on the init process - ParseOptions(argc,argv); + // Handle the options that have impact on the init process + processProgramOptions(vm, mConfig); // Init console =========================================================== Base::PyGILStateLocker lock; @@ -2470,55 +2799,6 @@ namespace boost { namespace program_options { } } #endif -pair customSyntax(const string& s) -{ -#if defined(FC_OS_MACOSX) - if (s.find("-psn_") == 0) - return make_pair(string("psn"), s.substr(5)); -#endif - if (s.find("-display") == 0) - return make_pair(string("display"), string("null")); - else if (s.find("-style") == 0) - return make_pair(string("style"), string("null")); - else if (s.find("-graphicssystem") == 0) - return make_pair(string("graphicssystem"), string("null")); - else if (s.find("-widgetcount") == 0) - return make_pair(string("widgetcount"), string("")); - else if (s.find("-geometry") == 0) - return make_pair(string("geometry"), string("null")); - else if (s.find("-font") == 0) - return make_pair(string("font"), string("null")); - else if (s.find("-fn") == 0) - return make_pair(string("fn"), string("null")); - else if (s.find("-background") == 0) - return make_pair(string("background"), string("null")); - else if (s.find("-bg") == 0) - return make_pair(string("bg"), string("null")); - else if (s.find("-foreground") == 0) - return make_pair(string("foreground"), string("null")); - else if (s.find("-fg") == 0) - return make_pair(string("fg"), string("null")); - else if (s.find("-button") == 0) - return make_pair(string("button"), string("null")); - else if (s.find("-btn") == 0) - return make_pair(string("btn"), string("null")); - else if (s.find("-name") == 0) - return make_pair(string("name"), string("null")); - else if (s.find("-title") == 0) - return make_pair(string("title"), string("null")); - else if (s.find("-visual") == 0) - return make_pair(string("visual"), string("null")); -// else if (s.find("-ncols") == 0) -// return make_pair(string("ncols"), boost::program_options::value(1)); -// else if (s.find("-cmap") == 0) -// return make_pair(string("cmap"), string("null")); - else if ('@' == s[0]) - return std::make_pair(string("response-file"), s.substr(1)); - else - return make_pair(string(), string()); - -} - // A helper function to simplify the main part. template ostream& operator<<(ostream& os, const vector& v) @@ -2527,282 +2807,6 @@ ostream& operator<<(ostream& os, const vector& v) return os; } -void Application::ParseOptions(int ac, char ** av) -{ - // Declare a group of options that will be - // allowed only on the command line - options_description generic("Generic options"); - generic.add_options() - ("version,v", "Prints version string") - ("help,h", "Prints help message") - ("console,c", "Starts in console mode") - ("response-file", value(),"Can be specified with '@name', too") - ("dump-config", "Dumps configuration") - ("get-config", value(), "Prints the value of the requested configuration key") - ; - - // Declare a group of options that will be - // allowed both on the command line and in - // the config file - std::string descr("Writes a log file to:\n"); - descr += mConfig["UserAppData"]; - descr += mConfig["ExeName"]; - descr += ".log"; - boost::program_options::options_description config("Configuration"); - config.add_options() - //("write-log,l", value(), "write a log file") - ("write-log,l", descr.c_str()) - ("log-file", value(), "Unlike --write-log this allows logging to an arbitrary file") - ("user-cfg,u", value(),"User config file to load/save user settings") - ("system-cfg,s", value(),"System config file to load/save system settings") - ("run-test,t", value() ,"Test case - or 0 for all") - ("module-path,M", value< vector >()->composing(),"Additional module paths") - ("python-path,P", value< vector >()->composing(),"Additional python paths") - ("single-instance", "Allow to run a single instance of the application") - ; - - - // Hidden options, will be allowed both on the command line and - // in the config file, but will not be shown to the user. - boost::program_options::options_description hidden("Hidden options"); - hidden.add_options() - ("input-file", boost::program_options::value< vector >(), "input file") - ("output", boost::program_options::value(),"output file") - ("hidden", "don't show the main window") - // this are to ignore for the window system (QApplication) - ("style", boost::program_options::value< string >(), "set the application GUI style") - ("stylesheet", boost::program_options::value< string >(), "set the application stylesheet") - ("session", boost::program_options::value< string >(), "restore the application from an earlier session") - ("reverse", "set the application's layout direction from right to left") - ("widgetcount", "print debug messages about widgets") - ("graphicssystem", boost::program_options::value< string >(), "backend to be used for on-screen widgets and pixmaps") - ("display", boost::program_options::value< string >(), "set the X-Server") - ("geometry ", boost::program_options::value< string >(), "set the X-Window geometry") - ("font", boost::program_options::value< string >(), "set the X-Window font") - ("fn", boost::program_options::value< string >(), "set the X-Window font") - ("background", boost::program_options::value< string >(), "set the X-Window background color") - ("bg", boost::program_options::value< string >(), "set the X-Window background color") - ("foreground", boost::program_options::value< string >(), "set the X-Window foreground color") - ("fg", boost::program_options::value< string >(), "set the X-Window foreground color") - ("button", boost::program_options::value< string >(), "set the X-Window button color") - ("btn", boost::program_options::value< string >(), "set the X-Window button color") - ("name", boost::program_options::value< string >(), "set the X-Window name") - ("title", boost::program_options::value< string >(), "set the X-Window title") - ("visual", boost::program_options::value< string >(), "set the X-Window to color scheme") - ("ncols", boost::program_options::value< int >(), "set the X-Window to color scheme") - ("cmap", "set the X-Window to color scheme") -#if defined(FC_OS_MACOSX) - ("psn", boost::program_options::value< string >(), "process serial number") -#endif - ; - - // Ignored options, will be safely ignored. Mostly used by underlying libs. - //boost::program_options::options_description x11("X11 options"); - //x11.add_options() - // ("display", boost::program_options::value< string >(), "set the X-Server") - // ; - //0000723: improper handling of qt specific command line arguments - std::vector args; - bool merge=false; - for (int i=1; i().c_str()); - if (!ifs) { - Base::Console().Error("Could no open the response file\n"); - std::stringstream str; - str << "Could no open the response file: '" - << vm["response-file"].as() << "'" << endl; - throw Base::UnknownProgramOption(str.str()); - } - // Read the whole file into a string - stringstream ss; - ss << ifs.rdbuf(); - // Split the file content - char_separator sep(" \n\r"); - tokenizer > tok(ss.str(), sep); - vector args; - copy(tok.begin(), tok.end(), back_inserter(args)); - // Parse the file and store the options - store( boost::program_options::command_line_parser(args). - options(cmdline_options).positional(p).extra_parser(customSyntax).run(), vm); - } - - if (vm.count("version")) { - std::stringstream str; - str << mConfig["ExeName"] << " " << mConfig["ExeVersion"] - << " Revision: " << mConfig["BuildRevision"] << std::endl; - throw Base::ProgramInformation(str.str()); - } - - if (vm.count("console")) { - mConfig["Console"] = "1"; - mConfig["RunMode"] = "Cmd"; - } - - if (vm.count("module-path")) { - vector Mods = vm["module-path"].as< vector >(); - string temp; - for (vector::const_iterator It= Mods.begin();It != Mods.end();++It) - temp += *It + ";"; - temp.erase(temp.end()-1); - mConfig["AdditionalModulePaths"] = temp; - } - - if (vm.count("python-path")) { - vector Paths = vm["python-path"].as< vector >(); - for (vector::const_iterator It= Paths.begin();It != Paths.end();++It) - Base::Interpreter().addPythonPath(It->c_str()); - } - - if (vm.count("input-file")) { - vector files(vm["input-file"].as< vector >()); - int OpenFileCount=0; - for (vector::const_iterator It = files.begin();It != files.end();++It) { - - //cout << "Input files are: " - // << vm["input-file"].as< vector >() << "\n"; - - std::ostringstream temp; - temp << "OpenFile" << OpenFileCount; - mConfig[temp.str()] = *It; - OpenFileCount++; - } - std::ostringstream buffer; - buffer << OpenFileCount; - mConfig["OpenFileCount"] = buffer.str(); - } - - if (vm.count("output")) { - string file = vm["output"].as(); - mConfig["SaveFile"] = file; - } - - if (vm.count("hidden")) { - mConfig["StartHidden"] = "1"; - } - - if (vm.count("write-log")) { - mConfig["LoggingFile"] = "1"; - //mConfig["LoggingFileName"] = vm["write-log"].as(); - mConfig["LoggingFileName"] = mConfig["UserAppData"] + mConfig["ExeName"] + ".log"; - } - - if (vm.count("log-file")) { - mConfig["LoggingFile"] = "1"; - mConfig["LoggingFileName"] = vm["log-file"].as(); - } - - if (vm.count("user-cfg")) { - mConfig["UserParameter"] = vm["user-cfg"].as(); - } - - if (vm.count("system-cfg")) { - mConfig["SystemParameter"] = vm["system-cfg"].as(); - } - - if (vm.count("run-test")) { - string testCase = vm["run-test"].as(); - if ( "0" == testCase) { - testCase = "TestApp.All"; - } - mConfig["TestCase"] = testCase; - mConfig["RunMode"] = "Internal"; - mConfig["ScriptFileName"] = "FreeCADTest"; - //sScriptName = FreeCADTest; - } - - if (vm.count("single-instance")) { - mConfig["SingleInstance"] = "1"; - } - - if (vm.count("dump-config")) { - std::stringstream str; - for (std::map::iterator it=mConfig.begin(); it != mConfig.end(); ++it) { - str << it->first << "=" << it->second << std::endl; - } - throw Base::ProgramInformation(str.str()); - } - - if (vm.count("get-config")) { - std::string configKey = vm["get-config"].as(); - std::stringstream str; - std::map::iterator pos; - pos = mConfig.find(configKey); - if (pos != mConfig.end()) { - str << pos->second; - } - str << std::endl; - throw Base::ProgramInformation(str.str()); - } -} - void Application::ExtractUserPath() { // std paths diff --git a/src/App/Application.h b/src/App/Application.h index 2e7237ea9e..54872ecec7 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -555,8 +555,6 @@ private: static void logStatus(void); // the one and only pointer to the application object static Application *_pcSingleton; - /// argument helper function - static void ParseOptions(int argc, char ** argv); /// checks if the environment is alright //static void CheckEnv(void); /// Search for the FreeCAD home path based on argv[0] From ab1220b6634a6dd559cf2490a5acc884ea252d9a Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 8 Nov 2021 16:17:08 +0100 Subject: [PATCH 135/138] App: code refactoring in Application::ExtractUserPath() --- src/App/Application.cpp | 315 ++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 161 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index d47e10666e..1db585f346 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2807,82 +2807,86 @@ ostream& operator<<(ostream& os, const vector& v) return os; } -void Application::ExtractUserPath() +namespace { + +boost::filesystem::path stringToPath(std::string str) { - // std paths - mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP; - mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP; +#if defined(FC_OS_WIN32) + std::wstring_convert> converter; + boost::filesystem::path path(converter.from_bytes(str)); +#else + boost::filesystem::path path(str); +#endif + return path; +} - // Set application tmp. directory - mConfig["AppTempPath"] = Base::FileInfo::getTempPath(); +std::string pathToString(const boost::filesystem::path& p) +{ +#if defined(FC_OS_WIN32) + std::wstring_convert> converter; + return converter.to_bytes(p.wstring()); +#else + return p.string(); +#endif +} - // this is to support a portable version of FreeCAD - QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); - QString userHome = env.value(QString::fromLatin1("FREECAD_USER_HOME")); - QString userData = env.value(QString::fromLatin1("FREECAD_USER_DATA")); - QString userTemp = env.value(QString::fromLatin1("FREECAD_USER_TEMP")); - - // verify env. variables - if (!userHome.isEmpty()) { - QDir dir(userHome); - if (dir.exists()) - userHome = QDir::toNativeSeparators(dir.canonicalPath()); - else - userHome.clear(); - } - - if (!userData.isEmpty()) { - QDir dir(userData); - if (dir.exists()) - userData = QDir::toNativeSeparators(dir.canonicalPath()); - else - userData.clear(); - } - else if (!userHome.isEmpty()) { - // if FREECAD_USER_HOME is set but not FREECAD_USER_DATA - userData = userHome; - } - - // override temp directory if set by env. variable - if (!userTemp.isEmpty()) { - QDir dir(userTemp); - if (dir.exists()) { - userTemp = dir.canonicalPath(); - userTemp += QDir::separator(); - userTemp = QDir::toNativeSeparators(userTemp); - mConfig["AppTempPath"] = userTemp.toUtf8().data(); - } - } - else if (!userHome.isEmpty()) { - // if FREECAD_USER_HOME is set but not FREECAD_USER_TEMP - QDir dir(userHome); - dir.mkdir(QString::fromLatin1("temp")); - QFileInfo fi(dir, QString::fromLatin1("temp")); - QString tmp(fi.absoluteFilePath()); - tmp += QDir::separator(); - tmp = QDir::toNativeSeparators(tmp); - mConfig["AppTempPath"] = tmp.toUtf8().data(); - } - -#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) +QString getDefaultHome() +{ + QString path; +#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) || defined(FC_OS_MACOSX) // Default paths for the user specific stuff struct passwd *pwd = getpwuid(getuid()); - if (pwd == NULL) + if (!pwd) + throw Base::RuntimeError("Getting HOME path from system failed!"); + path = QString::fromUtf8(pwd->pw_dir); + +#elif defined(FC_OS_WIN32) + WCHAR szPath[MAX_PATH]; + std::wstring_convert> converter; + // Get the default path where we can save our documents. It seems that + // 'CSIDL_MYDOCUMENTS' doesn't work on all machines, so we use 'CSIDL_PERSONAL' + // which does the same. + if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) { + path = QString::fromStdString(converter.to_bytes(szPath)); + } + else { throw Base::RuntimeError("Getting HOME path from system failed!"); - mConfig["UserHomePath"] = pwd->pw_dir; - if (!userHome.isEmpty()) { - mConfig["UserHomePath"] = userHome.toUtf8().data(); } - boost::filesystem::path appData(pwd->pw_dir); - if (!userData.isEmpty()) - appData = userData.toUtf8().data(); +#else +# error "Implement getDefaultHome() for your platform." +#endif - if (!boost::filesystem::exists(appData)) { - // This should never ever happen - throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); + return path; +} + +QString getDefaultUserData(const QString& home) +{ +#if defined(FC_OS_WIN32) + WCHAR szPath[MAX_PATH]; + std::wstring_convert> converter; + if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath))) { + return QString::fromStdString(converter.to_bytes(szPath)); } +#endif + return home; +} + +void getUserData(boost::filesystem::path& appData) +{ +#if defined(FC_OS_MACOSX) + appData = appData / "Library" / "Preferences"; +#else + Q_UNUSED(appData) +#endif +} + +void getApplicationData(std::map& mConfig, boost::filesystem::path& appData) +{ + // Actually the name of the directory where the parameters are stored should be the name of + // the application due to branding reasons. +#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of // the path. if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { @@ -2892,52 +2896,83 @@ void Application::ExtractUserPath() appData /= "." + mConfig["ExeName"]; } - // Actually the name of the directory where the parameters are stored should be the name of - // the application due to branding reasons. - - // In order to write to our data path, we must create some directories, first. - if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { - try { - boost::filesystem::create_directories(appData); - } catch (const boost::filesystem::filesystem_error& e) { - throw Base::FileSystemError("Could not create app data directories. Failed with: " + e.code().message()); - } - } - - mConfig["UserAppData"] = appData.string() + PATHSEP; - -#elif defined(FC_OS_MACOSX) - // Default paths for the user specific stuff on the platform - struct passwd *pwd = getpwuid(getuid()); - if (pwd == NULL) - throw Base::RuntimeError("Getting HOME path from system failed!"); - mConfig["UserHomePath"] = pwd->pw_dir; - if (!userHome.isEmpty()) { - mConfig["UserHomePath"] = userHome.toUtf8().data(); - } - - boost::filesystem::path appData(pwd->pw_dir); - if (!userData.isEmpty()) - appData = userData.toUtf8().data(); - - appData = appData / "Library" / "Preferences"; - - if (!boost::filesystem::exists(appData)) { - // This should never ever happen - throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); - } - +#elif defined(FC_OS_MACOSX) || defined(FC_OS_WIN32) // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of // the path. if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { appData /= mConfig["ExeVendor"]; } appData /= mConfig["ExeName"]; +#endif +} +} + +void Application::ExtractUserPath() +{ + // std paths + mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP; + mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP; + + // this is to support a portable version of FreeCAD + QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); + QString userHome = env.value(QString::fromLatin1("FREECAD_USER_HOME")); + QString userData = env.value(QString::fromLatin1("FREECAD_USER_DATA")); + QString userTemp = env.value(QString::fromLatin1("FREECAD_USER_TEMP")); + + auto toNativePath = [](QString& path) { + if (!path.isEmpty()) { + QDir dir(path); + if (dir.exists()) + path = QDir::toNativeSeparators(dir.canonicalPath()); + else + path.clear(); + } + }; + + // verify env. variables + toNativePath(userHome); + toNativePath(userData); + toNativePath(userTemp); + + // if FREECAD_USER_HOME is set but not FREECAD_USER_DATA + if (!userHome.isEmpty() && userData.isEmpty()) { + userData = userHome; + } + + // if FREECAD_USER_HOME is set but not FREECAD_USER_TEMP + if (!userHome.isEmpty() && userTemp.isEmpty()) { + QDir dir(userHome); + dir.mkdir(QString::fromLatin1("temp")); + QFileInfo fi(dir, QString::fromLatin1("temp")); + userTemp = fi.absoluteFilePath(); + } - // Actually the name of the directory where the parameters are stored should be the name of - // the application due to branding reasons. - + // User home path + // + if (userHome.isEmpty()) { + userHome = getDefaultHome(); + } + mConfig["UserHomePath"] = userHome.toUtf8().data(); + + + // User data path + // + if (userData.isEmpty()) { + userData = getDefaultUserData(userHome); + } + boost::filesystem::path appData(stringToPath(userData.toStdString())); + getUserData(appData); + if (!boost::filesystem::exists(appData)) { + // This should never ever happen + throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); + } + + // In the second step we want the directory where user settings of the application can be + // kept. There we create a directory with name of the vendor and a sub-directory with name + // of the application. + getApplicationData(mConfig, appData); + // In order to write to our data path, we must create some directories, first. if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { try { @@ -2947,72 +2982,30 @@ void Application::ExtractUserPath() } } - mConfig["UserAppData"] = appData.string() + PATHSEP; + mConfig["UserAppData"] = pathToString(appData) + PATHSEP; -#elif defined(FC_OS_WIN32) - WCHAR szPath[MAX_PATH]; - std::wstring_convert> converter; - // Get the default path where we can save our documents. It seems that - // 'CSIDL_MYDOCUMENTS' doesn't work on all machines, so we use 'CSIDL_PERSONAL' - // which does the same. - if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) { - mConfig["UserHomePath"] = converter.to_bytes(szPath); + + // Set application tmp. directory + // + if (!userTemp.isEmpty()) { + userTemp += QDir::separator(); + mConfig["AppTempPath"] = userTemp.toUtf8().data(); } else { - mConfig["UserHomePath"] = mConfig["AppHomePath"]; + mConfig["AppTempPath"] = Base::FileInfo::getTempPath(); } - if (!userHome.isEmpty()) { - mConfig["UserHomePath"] = userHome.toUtf8().data(); - } - // In the second step we want the directory where user settings of the application can be - // kept. There we create a directory with name of the vendor and a sub-directory with name - // of the application. - if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath))) { - boost::filesystem::path appData(szPath); - if (!userData.isEmpty()) - appData = userData.toStdWString(); - - if (!boost::filesystem::exists(appData)) { - // This should never ever happen - throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); - } - - // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of - // the path. - if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { - appData /= mConfig["ExeVendor"]; - } - appData /= mConfig["ExeName"]; - - // Actually the name of the directory where the parameters are stored should be the name of - // the application due to branding reasons. - - // In order to write to our data path, we must create some directories, first. - if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { - try { - boost::filesystem::create_directories(appData); - } catch (const boost::filesystem::filesystem_error& e) { - throw Base::FileSystemError("Could not create app data directories. Failed with: " + e.code().message()); - } - } - - mConfig["UserAppData"] = converter.to_bytes(appData.wstring()) + PATHSEP; - - // Create the default macro directory - boost::filesystem::path macroDir = converter.from_bytes(getUserMacroDir()); - if (!boost::filesystem::exists(macroDir) && !Py_IsInitialized()) { - try { - boost::filesystem::create_directories(macroDir); - } catch (const boost::filesystem::filesystem_error& e) { - throw Base::FileSystemError("Could not create macro directory. Failed with: " + e.code().message()); - } + // Create the default macro directory + // + boost::filesystem::path macroDir = stringToPath(getUserMacroDir()); + if (!boost::filesystem::exists(macroDir) && !Py_IsInitialized()) { + try { + boost::filesystem::create_directories(macroDir); + } catch (const boost::filesystem::filesystem_error& e) { + throw Base::FileSystemError("Could not create macro directory. Failed with: " + e.code().message()); } } -#else -# error "Implement ExtractUserPath() for your platform." -#endif } #if defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) From 7375be66adb18472c13e130034e2c302134b0658 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 8 Nov 2021 18:28:02 +0100 Subject: [PATCH 136/138] App: keep custom user data path as is if FREECAD_USER_HOME or FREECAD_USER_DATA is set --- src/App/Application.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 1db585f346..8cd9248a3d 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2958,11 +2958,16 @@ void Application::ExtractUserPath() // User data path // - if (userData.isEmpty()) { - userData = getDefaultUserData(userHome); + QString dataPath = userData; + if (dataPath.isEmpty()) { + dataPath = getDefaultUserData(userHome); } - boost::filesystem::path appData(stringToPath(userData.toStdString())); - getUserData(appData); + boost::filesystem::path appData(stringToPath(dataPath.toStdString())); + + // If a custom user data path is given then don't modify it + if (userData.isEmpty()) + getUserData(appData); + if (!boost::filesystem::exists(appData)) { // This should never ever happen throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); @@ -2971,7 +2976,10 @@ void Application::ExtractUserPath() // In the second step we want the directory where user settings of the application can be // kept. There we create a directory with name of the vendor and a sub-directory with name // of the application. - getApplicationData(mConfig, appData); + // + // If a custom user data path is given then don't modify it + if (userData.isEmpty()) + getApplicationData(mConfig, appData); // In order to write to our data path, we must create some directories, first. if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { From d244df4f79fe0d0c4e4db4f5327861caa8bacd10 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 8 Nov 2021 22:17:06 +0100 Subject: [PATCH 137/138] App: further code refactoring in Application::ExtractUserPath() --- src/App/Application.cpp | 126 +++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 47 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 8cd9248a3d..86b474aaad 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2905,15 +2905,69 @@ void getApplicationData(std::map& mConfig, boost::files appData /= mConfig["ExeName"]; #endif } + +QString findUserHomePath(const QString& userHome) +{ + if (userHome.isEmpty()) { + return getDefaultHome(); + } + else { + return userHome; + } } -void Application::ExtractUserPath() +boost::filesystem::path findUserDataPath(std::map& mConfig, const QString& userHome, const QString& userData) { - // std paths - mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP; - mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP; + QString dataPath = userData; + if (dataPath.isEmpty()) { + dataPath = getDefaultUserData(userHome); + } + boost::filesystem::path appData(stringToPath(dataPath.toStdString())); - // this is to support a portable version of FreeCAD + // If a custom user data path is given then don't modify it + if (userData.isEmpty()) + getUserData(appData); + + if (!boost::filesystem::exists(appData)) { + // This should never ever happen + throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); + } + + // In the second step we want the directory where user settings of the application can be + // kept. There we create a directory with name of the vendor and a sub-directory with name + // of the application. + // + // If a custom user data path is given then don't modify it + if (userData.isEmpty()) + getApplicationData(mConfig, appData); + + // In order to write to our data path, we must create some directories, first. + if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { + try { + boost::filesystem::create_directories(appData); + } catch (const boost::filesystem::filesystem_error& e) { + throw Base::FileSystemError("Could not create app data directories. Failed with: " + e.code().message()); + } + } + + return appData; +} + +QString findUserTempPath(const QString& userTemp) +{ + QString path = userTemp; + if (!path.isEmpty()) { + path += QDir::separator(); + } + else { + path = QString::fromStdString(Base::FileInfo::getTempPath()); + } + + return path; +} + +std::tuple getCustomPaths() +{ QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); QString userHome = env.value(QString::fromLatin1("FREECAD_USER_HOME")); QString userData = env.value(QString::fromLatin1("FREECAD_USER_DATA")); @@ -2947,61 +3001,39 @@ void Application::ExtractUserPath() userTemp = fi.absoluteFilePath(); } + return std::tuple(userHome, userData, userTemp); +} +} + +void Application::ExtractUserPath() +{ + // std paths + mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP; + mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP; + + // this is to support a portable version of FreeCAD + auto paths = getCustomPaths(); + QString userHome = std::get<0>(paths); + QString userData = std::get<1>(paths); + QString userTemp = std::get<2>(paths); + // User home path // - if (userHome.isEmpty()) { - userHome = getDefaultHome(); - } + userHome = findUserHomePath(userHome); mConfig["UserHomePath"] = userHome.toUtf8().data(); // User data path // - QString dataPath = userData; - if (dataPath.isEmpty()) { - dataPath = getDefaultUserData(userHome); - } - boost::filesystem::path appData(stringToPath(dataPath.toStdString())); - - // If a custom user data path is given then don't modify it - if (userData.isEmpty()) - getUserData(appData); - - if (!boost::filesystem::exists(appData)) { - // This should never ever happen - throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); - } - - // In the second step we want the directory where user settings of the application can be - // kept. There we create a directory with name of the vendor and a sub-directory with name - // of the application. - // - // If a custom user data path is given then don't modify it - if (userData.isEmpty()) - getApplicationData(mConfig, appData); - - // In order to write to our data path, we must create some directories, first. - if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { - try { - boost::filesystem::create_directories(appData); - } catch (const boost::filesystem::filesystem_error& e) { - throw Base::FileSystemError("Could not create app data directories. Failed with: " + e.code().message()); - } - } - + boost::filesystem::path appData = findUserDataPath(mConfig, userHome, userData); mConfig["UserAppData"] = pathToString(appData) + PATHSEP; // Set application tmp. directory // - if (!userTemp.isEmpty()) { - userTemp += QDir::separator(); - mConfig["AppTempPath"] = userTemp.toUtf8().data(); - } - else { - mConfig["AppTempPath"] = Base::FileInfo::getTempPath(); - } + userTemp = findUserTempPath(userTemp); + mConfig["AppTempPath"] = userTemp.toUtf8().data(); // Create the default macro directory From 2cc570757144422a189b9881e650c3878ec58b13 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 9 Nov 2021 00:20:55 +0100 Subject: [PATCH 138/138] App: issue 0002956: implement XDG_CACHE_HOME --- src/App/Application.cpp | 98 ++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 16 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 86b474aaad..c42d186de0 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2882,6 +2882,16 @@ void getUserData(boost::filesystem::path& appData) #endif } +void getConfigOrChache(std::map& mConfig, boost::filesystem::path& appData) +{ + // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of + // the path. + if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { + appData /= mConfig["ExeVendor"]; + } + appData /= mConfig["ExeName"]; +} + void getApplicationData(std::map& mConfig, boost::filesystem::path& appData) { // Actually the name of the directory where the parameters are stored should be the name of @@ -2897,12 +2907,7 @@ void getApplicationData(std::map& mConfig, boost::files } #elif defined(FC_OS_MACOSX) || defined(FC_OS_WIN32) - // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of - // the path. - if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { - appData /= mConfig["ExeVendor"]; - } - appData /= mConfig["ExeName"]; + getConfigOrChache(mConfig, appData); #endif } @@ -2953,17 +2958,33 @@ boost::filesystem::path findUserDataPath(std::map& mCon return appData; } -QString findUserTempPath(const QString& userTemp) +boost::filesystem::path findCachePath(std::map& mConfig, const QString& cacheHome, const QString& userTemp) { - QString path = userTemp; - if (!path.isEmpty()) { - path += QDir::separator(); - } - else { - path = QString::fromStdString(Base::FileInfo::getTempPath()); + QString dataPath = userTemp; + if (dataPath.isEmpty()) { + dataPath = cacheHome; } - return path; + boost::filesystem::path appData(stringToPath(dataPath.toStdString())); + +#if !defined(FC_OS_WIN32) + // If a custom user temp path is given then don't modify it + if (userTemp.isEmpty()) { + getConfigOrChache(mConfig, appData); + appData /= "Cache"; + } +#endif + + // In order to write to our data path, we must create some directories, first. + if (!boost::filesystem::exists(appData)) { + try { + boost::filesystem::create_directories(appData); + } catch (const boost::filesystem::filesystem_error& e) { + throw Base::FileSystemError("Could not create cache directories. Failed with: " + e.code().message()); + } + } + + return appData; } std::tuple getCustomPaths() @@ -3003,6 +3024,48 @@ std::tuple getCustomPaths() return std::tuple(userHome, userData, userTemp); } + +std::tuple getXDGPaths() +{ + auto checkXdgPath = [](QString& path) { + if (!path.isEmpty()) { + QDir dir(path); + if (!dir.exists() || dir.isRelative()) + path.clear(); + } + }; + + QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); + QString configHome = env.value(QString::fromLatin1("XDG_CONFIG_HOME")); + QString cacheHome = env.value(QString::fromLatin1("XDG_CACHE_HOME")); + + checkXdgPath(configHome); + checkXdgPath(cacheHome); + + // If env. are not set or invalid + QDir home(getDefaultHome()); + if (configHome.isEmpty()) { +#if defined(FC_OS_MACOSX) + QFileInfo fi(home, QString::fromLatin1("Library/Preferences")); +#else + QFileInfo fi(home, QString::fromLatin1(".config")); +#endif + configHome = fi.absoluteFilePath(); + } + + if (cacheHome.isEmpty()) { +#if defined(FC_OS_MACOSX) + QFileInfo fi(home, QString::fromLatin1("Library/Caches")); +#elif defined(FC_OS_WIN32) + QFileInfo fi(QString::fromStdString(Base::FileInfo::getTempPath())); +#else + QFileInfo fi(home, QString::fromLatin1(".cache")); +#endif + cacheHome = fi.absoluteFilePath(); + } + + return std::make_tuple(configHome, cacheHome); +} } void Application::ExtractUserPath() @@ -3017,6 +3080,9 @@ void Application::ExtractUserPath() QString userData = std::get<1>(paths); QString userTemp = std::get<2>(paths); + auto xdgPaths = getXDGPaths(); + //QString configHome = std::get<0>(xdgPaths); + QString cacheHome = std::get<1>(xdgPaths); // User home path // @@ -3032,8 +3098,8 @@ void Application::ExtractUserPath() // Set application tmp. directory // - userTemp = findUserTempPath(userTemp); - mConfig["AppTempPath"] = userTemp.toUtf8().data(); + boost::filesystem::path cache = findCachePath(mConfig, cacheHome, userTemp); + mConfig["AppTempPath"] = pathToString(cache) + PATHSEP; // Create the default macro directory