diff --git a/CMakeLists.txt b/CMakeLists.txt index 788042ca442d..97c0f4b6a312 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,9 +28,9 @@ endif() project(FreeCAD) set(PACKAGE_VERSION_MAJOR "1") -set(PACKAGE_VERSION_MINOR "0") +set(PACKAGE_VERSION_MINOR "1") set(PACKAGE_VERSION_PATCH "0") # number of patch release (e.g. "4" for the 0.18.4 release) -set(PACKAGE_VERSION_SUFFIX "RC1") # either "dev" for development snapshot or "" (empty string) +set(PACKAGE_VERSION_SUFFIX "dev") # either "dev" for development snapshot or "" (empty string) set(PACKAGE_BUILD_VERSION "0") # used when the same FreeCAD version will be re-released (for example using an updated LibPack) set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}") diff --git a/CMakePresets.json b/CMakePresets.json index 43c45c1890a6..4dc3309a1cda 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -151,6 +151,10 @@ }, "cmakeExecutable": "${sourceDir}/conda/cmake.sh", "cacheVariables": { + "CMAKE_IGNORE_PREFIX_PATH": { + "type": "STRING", + "value": "/opt/homebrew;/usr/local/homebrew" + }, "CMAKE_INCLUDE_PATH": { "type": "FILEPATH", "value": "${sourceDir}/.conda/freecad/include" diff --git a/cMake/FreeCAD_Helpers/SetGlobalCompilerAndLinkerSettings.cmake b/cMake/FreeCAD_Helpers/SetGlobalCompilerAndLinkerSettings.cmake index 44a9dd2b4e34..7710219fe0b3 100644 --- a/cMake/FreeCAD_Helpers/SetGlobalCompilerAndLinkerSettings.cmake +++ b/cMake/FreeCAD_Helpers/SetGlobalCompilerAndLinkerSettings.cmake @@ -34,6 +34,7 @@ macro(SetGlobalCompilerAndLinkerSettings) if(FREECAD_RELEASE_PDB) set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") set (CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG") + set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG") endif(FREECAD_RELEASE_PDB) if(FREECAD_RELEASE_SEH) # remove /EHsc or /EHs flags because they are incompatible with /EHa diff --git a/src/3rdParty/libE57Format/src/CheckedFile.cpp b/src/3rdParty/libE57Format/src/CheckedFile.cpp index 0822ae7c96a6..857167623c61 100644 --- a/src/3rdParty/libE57Format/src/CheckedFile.cpp +++ b/src/3rdParty/libE57Format/src/CheckedFile.cpp @@ -25,6 +25,11 @@ * DEALINGS IN THE SOFTWARE. */ +// convenience for all the BSDs +#if defined( __FreeBSD__) || defined( __NetBSD__) || defined( __OpenBSD__) +#define __BSD +#endif + #if defined( _WIN32 ) #if defined( _MSC_VER ) #include @@ -44,7 +49,7 @@ #include #include #include -#elif defined(__OpenBSD__) +#elif defined(__BSD) #include #include #include @@ -487,7 +492,7 @@ uint64_t CheckedFile::lseek64( int64_t offset, int whence ) #endif #elif defined( __linux__ ) int64_t result = ::lseek64( fd_, offset, whence ); -#elif defined( __APPLE__ ) || defined(__OpenBSD__) +#elif defined( __APPLE__ ) || defined(__BSD) int64_t result = ::lseek( fd_, offset, whence ); #else #error "no supported OS platform defined" diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index af4867a5bd37..8c5ae0215551 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -442,6 +442,12 @@ class AppExport DocumentObject: public App::TransactionalObject /* Return true to cause PropertyView to show linked object's property */ virtual bool canLinkProperties() const {return true;} + /* Return whether this object is a link */ + virtual bool isLink() const {return false;}; + + /* Return whether this object is a link group */ + virtual bool isLinkGroup() const {return false;}; + /* Return true to bypass duplicate label checking */ virtual bool allowDuplicateLabel() const {return false;} diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index 03f3ccfe4f00..2a096321992f 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -25,6 +25,8 @@ #include +#include + #include "ComplexGeoData.h" #include "Document.h" #include "GeoFeature.h" @@ -270,3 +272,63 @@ GeoFeature::getHigherElements(const char *element, bool silent) const return {}; return prop->getComplexData()->getHigherElements(element, silent); } + +Base::Placement GeoFeature::getPlacementFromProp(App::DocumentObject* obj, const char* propName) +{ + Base::Placement plc = Base::Placement(); + auto* propPlacement = dynamic_cast(obj->getPropertyByName(propName)); + if (propPlacement) { + plc = propPlacement->getValue(); + } + return plc; +} + +Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj, + App::DocumentObject* rootObj, + const std::string& sub) +{ + if (!targetObj || !rootObj || sub.empty()) { + return Base::Placement(); + } + std::vector names = Base::Tools::splitSubName(sub); + + App::Document* doc = rootObj->getDocument(); + Base::Placement plc = getPlacementFromProp(rootObj, "Placement"); + + if (targetObj == rootObj) return plc; + + for (auto& name : names) { + App::DocumentObject* obj = doc->getObject(name.c_str()); + if (!obj) { + return Base::Placement(); + } + + plc = plc * getPlacementFromProp(obj, "Placement"); + + if (obj == targetObj) { + return plc; + } + if (obj->isLink()) { + // Update doc in case its an external link. + doc = obj->getLinkedObject()->getDocument(); + } + } + + // If targetObj has not been found there's a problem + return Base::Placement(); +} + +Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj, + App::PropertyXLinkSub* prop) +{ + if (!targetObj || !prop) { + return Base::Placement(); + } + + std::vector subs = prop->getSubValues(); + if (subs.empty()) { + return Base::Placement(); + } + + return getGlobalPlacement(targetObj, prop->getValue(), subs[0]); +} diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index 9c0443611fa4..216f741dcd54 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -179,6 +179,10 @@ class AppExport GeoFeature : public App::DocumentObject /// Return the higher level element names of the given element virtual std::vector getHigherElements(const char *name, bool silent=false) const; + static Base::Placement getPlacementFromProp(DocumentObject* obj, const char* propName); + static Base::Placement getGlobalPlacement(DocumentObject* targetObj, DocumentObject* rootObj, const std::string& sub); + static Base::Placement getGlobalPlacement(DocumentObject* targetObj, PropertyXLinkSub* prop); + protected: void onChanged(const Property* prop) override; // void onDocumentRestored() override; diff --git a/src/App/Link.cpp b/src/App/Link.cpp index 12fb793b9f7b..815787de66fa 100644 --- a/src/App/Link.cpp +++ b/src/App/Link.cpp @@ -2282,6 +2282,16 @@ bool Link::canLinkProperties() const { return true; } +bool Link::isLink() const +{ + return ElementCount.getValue() == 0; +} + +bool Link::isLinkGroup() const +{ + return ElementCount.getValue() > 0; +} + ////////////////////////////////////////////////////////////////////////////////////////// namespace App { @@ -2309,6 +2319,11 @@ bool LinkElement::canDelete() const { return !owner || !owner->getDocument()->getObjectByID(_LinkOwner.getValue()); } +bool LinkElement::isLink() const +{ + return true; +} + App::Link* LinkElement::getLinkGroup() const { std::vector inList = getInList(); diff --git a/src/App/Link.h b/src/App/Link.h index 4dcb433ee66d..7cb879f75a31 100644 --- a/src/App/Link.h +++ b/src/App/Link.h @@ -556,6 +556,10 @@ class AppExport Link : public App::DocumentObject, public App::LinkExtension } bool canLinkProperties() const override; + + bool isLink() const override; + + bool isLinkGroup() const override; }; using LinkPython = App::FeaturePythonT; @@ -600,6 +604,8 @@ class AppExport LinkElement : public App::DocumentObject, public App::LinkBaseEx _handleChangedPropertyName(reader,TypeName,PropName); } + bool isLink() const override; + App::Link* getLinkGroup() const; }; diff --git a/src/Base/Parameter.cpp b/src/Base/Parameter.cpp index 5bff9473170d..b2308a7804db 100644 --- a/src/Base/Parameter.cpp +++ b/src/Base/Parameter.cpp @@ -50,7 +50,7 @@ #endif #include -#include "fmt/printf.h" +#include #include "Parameter.h" #include "Parameter.inl" diff --git a/src/Base/Tools.cpp b/src/Base/Tools.cpp index b16416448932..eab37f58601f 100644 --- a/src/Base/Tools.cpp +++ b/src/Base/Tools.cpp @@ -372,3 +372,24 @@ std::string Base::Tools::currentDateTimeString() .toString(Qt::ISODate) .toStdString(); } + +std::vector Base::Tools::splitSubName(const std::string& subname) +{ + // Turns 'Part.Part001.Body.Pad.Edge1' + // Into ['Part', 'Part001', 'Body', 'Pad', 'Edge1'] + std::vector subNames; + std::string subName; + std::istringstream subNameStream(subname); + while (std::getline(subNameStream, subName, '.')) { + subNames.push_back(subName); + } + + // Check if the last character of the input string is the delimiter. + // If so, add an empty string to the subNames vector. + // Because the last subname is the element name and can be empty. + if (!subname.empty() && subname.back() == '.') { + subNames.push_back(""); // Append empty string for trailing dot. + } + + return subNames; +} diff --git a/src/Base/Tools.h b/src/Base/Tools.h index b17907bbc4a4..e2018b988919 100644 --- a/src/Base/Tools.h +++ b/src/Base/Tools.h @@ -325,6 +325,8 @@ struct BaseExport Tools static std::string joinList(const std::vector& vec, const std::string& sep = ", "); static std::string currentDateTimeString(); + + static std::vector splitSubName(const std::string& subname); }; diff --git a/src/Gui/BitmapFactory.cpp b/src/Gui/BitmapFactory.cpp index ddbb8fa2400b..c698f6231d75 100644 --- a/src/Gui/BitmapFactory.cpp +++ b/src/Gui/BitmapFactory.cpp @@ -54,6 +54,8 @@ class BitmapFactoryInstP public: QMap xpmMap; QMap xpmCache; + + bool useIconTheme; }; } @@ -93,7 +95,9 @@ void BitmapFactoryInst::destruct () BitmapFactoryInst::BitmapFactoryInst() { d = new BitmapFactoryInstP; + restoreCustomPaths(); + configureUseIconTheme(); } BitmapFactoryInst::~BitmapFactoryInst() @@ -111,6 +115,14 @@ void BitmapFactoryInst::restoreCustomPaths() } } +void Gui::BitmapFactoryInst::configureUseIconTheme() +{ + Base::Reference group = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Bitmaps/Theme"); + + d->useIconTheme = group->GetBool("UseIconTheme", group->GetBool("ThemeSearchPaths", false)); +} + void BitmapFactoryInst::addPath(const QString& path) { QDir::addSearchPath(QString::fromLatin1("icons"), path); @@ -174,6 +186,10 @@ bool BitmapFactoryInst::findPixmapInCache(const char* name, QPixmap& px) const QIcon BitmapFactoryInst::iconFromTheme(const char* name, const QIcon& fallback) { + if (!d->useIconTheme) { + return iconFromDefaultTheme(name, fallback); + } + QString iconName = QString::fromUtf8(name); QIcon icon = QIcon::fromTheme(iconName, fallback); if (icon.isNull()) { @@ -206,6 +222,21 @@ bool BitmapFactoryInst::loadPixmap(const QString& filename, QPixmap& icon) const return !icon.isNull(); } +QIcon Gui::BitmapFactoryInst::iconFromDefaultTheme(const char* name, const QIcon& fallback) +{ + QIcon icon; + QPixmap px = pixmap(name); + + if (!px.isNull()) { + icon.addPixmap(px); + return icon; + } else { + return fallback; + } + + return icon; +} + QPixmap BitmapFactoryInst::pixmap(const char* name) const { if (!name || *name == '\0') diff --git a/src/Gui/BitmapFactory.h b/src/Gui/BitmapFactory.h index b17239484cf6..58cfc7372001 100644 --- a/src/Gui/BitmapFactory.h +++ b/src/Gui/BitmapFactory.h @@ -75,6 +75,10 @@ class GuiExport BitmapFactoryInst : public Base::Factory * If no such icon is found in the current theme fallback is returned instead. */ QIcon iconFromTheme(const char* name, const QIcon& fallback = QIcon()); + /** Returns the QIcon corresponding to name in the default (FreeCAD's) icon theme. + * If no such icon is found in the current theme fallback is returned instead. + */ + QIcon iconFromDefaultTheme(const char* name, const QIcon& fallback = QIcon()); /// Retrieves a pixmap by name QPixmap pixmap(const char* name) const; /** Retrieves a pixmap by name and size created by an @@ -150,6 +154,7 @@ class GuiExport BitmapFactoryInst : public Base::Factory private: bool loadPixmap(const QString& path, QPixmap&) const; void restoreCustomPaths(); + void configureUseIconTheme(); static BitmapFactoryInst* _pcSingleton; BitmapFactoryInst(); diff --git a/src/Gui/Selection.cpp b/src/Gui/Selection.cpp index 29b634df6502..f056642801d9 100644 --- a/src/Gui/Selection.cpp +++ b/src/Gui/Selection.cpp @@ -24,6 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include #endif diff --git a/src/Gui/SoFCCSysDragger.cpp b/src/Gui/SoFCCSysDragger.cpp index d057ea31bd43..af2a0421751a 100644 --- a/src/Gui/SoFCCSysDragger.cpp +++ b/src/Gui/SoFCCSysDragger.cpp @@ -96,7 +96,7 @@ TDragger::TDragger() { SO_KIT_CONSTRUCTOR(TDragger); -#if defined(Q_OS_MACOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) this->ref(); #endif @@ -400,7 +400,7 @@ void TPlanarDragger::initClass() TPlanarDragger::TPlanarDragger() { SO_KIT_CONSTRUCTOR(TPlanarDragger); -#if defined(Q_OS_MACOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) this->ref(); #endif @@ -698,7 +698,7 @@ void RDragger::initClass() RDragger::RDragger() { SO_KIT_CONSTRUCTOR(RDragger); -#if defined(Q_OS_MACOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) this->ref(); #endif @@ -1008,6 +1008,10 @@ SoFCCSysDragger::SoFCCSysDragger() { SO_KIT_CONSTRUCTOR(SoFCCSysDragger); +#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) + this->ref(); +#endif + SO_KIT_ADD_CATALOG_ENTRY(annotation, So3DAnnotation, TRUE, geomSeparator, "", TRUE); SO_KIT_ADD_CATALOG_ENTRY(scaleNode, SoScale, TRUE, annotation, "", TRUE); diff --git a/src/Gui/StartupProcess.cpp b/src/Gui/StartupProcess.cpp index 7b7e131410c5..65f46e9b07bd 100644 --- a/src/Gui/StartupProcess.cpp +++ b/src/Gui/StartupProcess.cpp @@ -178,13 +178,8 @@ void StartupProcess::setThemePaths() QIcon::setThemeSearchPaths(searchPaths); } - // KDE file dialog needs icons from the desktop theme - QIcon::setFallbackThemeName(QIcon::themeName()); - std::string name = hTheme->GetASCII("Name"); - if (name.empty()) { - QIcon::setThemeName(QLatin1String("FreeCAD-default")); - } else { + if (!name.empty()) { QIcon::setThemeName(QString::fromLatin1(name.c_str())); } } diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 57f389984bd5..01d39314ff31 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -1677,10 +1677,6 @@ void TreeWidget::keyPressEvent(QKeyEvent* event) void TreeWidget::mousePressEvent(QMouseEvent* event) { - QTreeWidget::mousePressEvent(event); - - // Handle the visibility icon after the normal event processing to not interfere with - // the selection logic. if (isVisibilityIconEnabled()) { QTreeWidgetItem* item = itemAt(event->pos()); if (item && item->type() == TreeWidget::ObjectType && event->button() == Qt::LeftButton) { @@ -1728,12 +1724,11 @@ void TreeWidget::mousePressEvent(QMouseEvent* event) visible = obj->Visibility.getValue(); obj->Visibility.setValue(!visible); } - - event->setAccepted(true); - return; } } } + + QTreeWidget::mousePressEvent(event); } void TreeWidget::mouseDoubleClickEvent(QMouseEvent* event) diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 28a26923826f..5e8c5412c2a6 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -127,9 +127,9 @@ #include "Utilities.h" -FC_LOG_LEVEL_INIT("3DViewer",true,true) +FC_LOG_LEVEL_INIT("3DViewer", true, true) -//#define FC_LOGGING_CB +// #define FC_LOGGING_CB using namespace Gui; @@ -3358,7 +3358,7 @@ void View3DInventorViewer::alignToSelection() return; } - const auto selection = Selection().getSelection(); + const auto selection = Selection().getSelection(nullptr, ResolveMode::NoResolve); // Empty selection if (selection.empty()) { @@ -3373,13 +3373,18 @@ void View3DInventorViewer::alignToSelection() // Get the geo feature App::GeoFeature* geoFeature = nullptr; App::ElementNamePair elementName; - App::GeoFeature::resolveElement(selection[0].pObject, selection[0].SubName, elementName, false, App::GeoFeature::ElementNameType::Normal, nullptr, nullptr, &geoFeature); + App::GeoFeature::resolveElement(selection[0].pObject, selection[0].SubName, elementName, true, App::GeoFeature::ElementNameType::Normal, nullptr, nullptr, &geoFeature); if (!geoFeature) { return; } + const auto globalPlacement = App::GeoFeature::getGlobalPlacement(selection[0].pResolvedObject, selection[0].pObject, elementName.oldName); + const auto rotation = globalPlacement.getRotation() * geoFeature->Placement.getValue().getRotation().inverse(); + const auto geoFeatureSubName = Base::Tools::splitSubName(elementName.oldName).back(); + Base::Vector3d direction; - if (geoFeature->getCameraAlignmentDirection(direction, selection[0].SubName)) { + if (geoFeature->getCameraAlignmentDirection(direction, geoFeatureSubName.c_str())) { + rotation.multVec(direction, direction); const auto orientation = SbRotation(SbVec3f(0, 0, 1), Base::convertTo(direction)); setCameraOrientation(orientation); } diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index 70c1c3c136db..ce1a631a7d74 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -111,39 +111,6 @@ static void printPlacement(Base::Placement plc, const char* name) angle); }*/ -static bool isLink(App::DocumentObject* obj) -{ - if (!obj) { - return false; - } - - auto* link = dynamic_cast(obj); - if (link) { - return link->ElementCount.getValue() == 0; - } - - auto* linkEl = dynamic_cast(obj); - if (linkEl) { - return true; - } - - return false; -} - -static bool isLinkGroup(App::DocumentObject* obj) -{ - if (!obj) { - return false; - } - - auto* link = dynamic_cast(obj); - if (link) { - return link->ElementCount.getValue() > 0; - } - - return false; -} - // ================================ Assembly Object ============================ PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part) @@ -1733,7 +1700,7 @@ void AssemblyObject::ensureIdentityPlacements() std::vector group = Group.getValues(); for (auto* obj : group) { // When used in assembly, link groups must have identity placements. - if (isLinkGroup(obj)) { + if (obj->isLinkGroup()) { auto* link = dynamic_cast(obj); auto* pPlc = dynamic_cast(obj->getPropertyByName("Placement")); if (!pPlc || !link) { @@ -2078,6 +2045,7 @@ void AssemblyObject::setJointActivated(App::DocumentObject* joint, bool val) propActivated->setValue(val); } } + bool AssemblyObject::getJointActivated(App::DocumentObject* joint) { auto* propActivated = dynamic_cast(joint->getPropertyByName("Activated")); @@ -2087,65 +2055,6 @@ bool AssemblyObject::getJointActivated(App::DocumentObject* joint) return false; } -Base::Placement AssemblyObject::getPlacementFromProp(App::DocumentObject* obj, const char* propName) -{ - Base::Placement plc = Base::Placement(); - auto* propPlacement = dynamic_cast(obj->getPropertyByName(propName)); - if (propPlacement) { - plc = propPlacement->getValue(); - } - return plc; -} - - -Base::Placement AssemblyObject::getGlobalPlacement(App::DocumentObject* targetObj, - App::DocumentObject* rootObj, - const std::string& sub) -{ - if (!targetObj || !rootObj || sub == "") { - return Base::Placement(); - } - std::vector names = splitSubName(sub); - - App::Document* doc = rootObj->getDocument(); - Base::Placement plc = getPlacementFromProp(rootObj, "Placement"); - - for (auto& name : names) { - App::DocumentObject* obj = doc->getObject(name.c_str()); - if (!obj) { - return Base::Placement(); - } - - plc = plc * getPlacementFromProp(obj, "Placement"); - - if (obj == targetObj) { - return plc; - } - if (isLink(obj)) { - // Update doc in case its an external link. - doc = obj->getLinkedObject()->getDocument(); - } - } - - // If targetObj has not been found there's a problem - return Base::Placement(); -} - -Base::Placement AssemblyObject::getGlobalPlacement(App::DocumentObject* targetObj, - App::PropertyXLinkSub* prop) -{ - if (!targetObj || !prop) { - return Base::Placement(); - } - - std::vector subs = prop->getSubValues(); - if (subs.empty()) { - return Base::Placement(); - } - - return getGlobalPlacement(targetObj, prop->getValue(), subs[0]); -} - double AssemblyObject::getJointDistance(App::DocumentObject* joint) { double distance = 0.0; @@ -2193,7 +2102,7 @@ std::vector AssemblyObject::getSubAsList(App::PropertyXLinkSub* pro return {}; } - return splitSubName(subs[0]); + return Base::Tools::splitSubName(subs[0]); } std::vector AssemblyObject::getSubAsList(App::DocumentObject* obj, const char* pName) @@ -2203,27 +2112,6 @@ std::vector AssemblyObject::getSubAsList(App::DocumentObject* obj, return getSubAsList(prop); } -std::vector AssemblyObject::splitSubName(const std::string& sub) -{ - // Turns 'Part.Part001.Body.Pad.Edge1' - // Into ['Part', 'Part001','Body','Pad','Edge1'] - std::vector subNames; - std::string subName; - std::istringstream subNameStream(sub); - while (std::getline(subNameStream, subName, '.')) { - subNames.push_back(subName); - } - - // Check if the last character of the input string is the delimiter. - // If so, add an empty string to the subNames vector. - // Because the last subname is the element name and can be empty. - if (!sub.empty() && sub.back() == '.') { - subNames.push_back(""); // Append empty string for trailing dot. - } - - return subNames; -} - std::string AssemblyObject::getElementFromProp(App::DocumentObject* obj, const char* pName) { std::vector names = getSubAsList(obj, pName); @@ -2264,7 +2152,7 @@ App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* obj, std App::Document* doc = obj->getDocument(); - std::vector names = splitSubName(sub); + std::vector names = Base::Tools::splitSubName(sub); // Lambda function to check if the typeId is a BodySubObject auto isBodySubObject = [](App::DocumentObject* obj) -> bool { @@ -2306,7 +2194,7 @@ App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* obj, std return obj; } - if (obj->isDerivedFrom() || isLinkGroup(obj)) { + if (obj->isDerivedFrom() || obj->isLinkGroup()) { continue; } else if (obj->isDerivedFrom()) { @@ -2316,7 +2204,7 @@ App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* obj, std // Primitive, fastener, gear, etc. return obj; } - else if (isLink(obj)) { + else if (obj->isLink()) { App::DocumentObject* linked_obj = obj->getLinkedObject(); if (linked_obj->isDerivedFrom()) { auto* retObj = handlePartDesignBody(linked_obj, it); @@ -2370,7 +2258,7 @@ App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* o App::Document* doc = obj->getDocument(); - std::vector names = splitSubName(sub); + std::vector names = Base::Tools::splitSubName(sub); names.insert(names.begin(), obj->getNameInDocument()); bool assemblyPassed = false; @@ -2381,7 +2269,7 @@ App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* o continue; } - if (isLink(obj)) { // update the document if necessary for next object + if (obj->isLink()) { // update the document if necessary for next object doc = obj->getLinkedObject()->getDocument(); } @@ -2398,7 +2286,7 @@ App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* o continue; // we ignore groups. } - if (isLinkGroup(obj)) { + if (obj->isLinkGroup()) { continue; } diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index 1615ffae31b8..4fc218546082 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -280,14 +280,6 @@ class AssemblyExport AssemblyObject: public App::Part const char* propName); static std::vector getSubAsList(App::PropertyXLinkSub* prop); static std::vector getSubAsList(App::DocumentObject* joint, const char* propName); - static std::vector splitSubName(const std::string& subName); - static Base::Placement getPlacementFromProp(App::DocumentObject* obj, const char* propName); - - static Base::Placement getGlobalPlacement(App::DocumentObject* targetObj, - App::DocumentObject* rootObj, - const std::string& sub); - static Base::Placement getGlobalPlacement(App::DocumentObject* targetObj, - App::PropertyXLinkSub* prop); }; // using AssemblyObjectPython = App::FeaturePythonT; diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 834006cb8424..45abfeffd89d 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -46,6 +46,8 @@ #include #include +#include + #include #include #include @@ -618,7 +620,7 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection std::vector objsSubNames = selObj.getSubNames(); for (auto& subNamesStr : objsSubNames) { - std::vector subNames = AssemblyObject::splitSubName(subNamesStr); + std::vector subNames = Base::Tools::splitSubName(subNamesStr); if (subNames.empty()) { continue; } @@ -764,7 +766,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() const char* plcPropName = (pName == "Reference1") ? "Placement1" : "Placement2"; // jcsPlc is relative to the Object - jcsPlc = AssemblyObject::getPlacementFromProp(movingJoint, plcPropName); + jcsPlc = App::GeoFeature::getPlacementFromProp(movingJoint, plcPropName); // Make jcsGlobalPlc relative to the origin of the doc auto* ref = @@ -773,7 +775,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() return DragMode::Translation; } auto* obj = assemblyPart->getObjFromRef(movingJoint, pName.c_str()); - Base::Placement global_plc = AssemblyObject::getGlobalPlacement(obj, ref); + Base::Placement global_plc = App::GeoFeature::getGlobalPlacement(obj, ref); jcsGlobalPlc = global_plc * jcsPlc; // Add downstream parts so that they move together @@ -920,7 +922,7 @@ void ViewProviderAssembly::initMoveDragger() App::DocumentObject* part = docsToMove[0].obj; draggerInitPlc = - AssemblyObject::getGlobalPlacement(part, docsToMove[0].rootObj, docsToMove[0].sub); + App::GeoFeature::getGlobalPlacement(part, docsToMove[0].rootObj, docsToMove[0].sub); std::vector listOfObjs; std::vector listOfRefs; for (auto& movingObj : docsToMove) { @@ -1130,11 +1132,11 @@ ViewProviderAssembly::getCenterOfBoundingBox(const std::vector& mo // bboxCenter does not take into account obj global placement Base::Placement plc(bboxCenter, Base::Rotation()); // Change plc to be relative to the object placement. - Base::Placement objPlc = AssemblyObject::getPlacementFromProp(movingObj.obj, "Placement"); + Base::Placement objPlc = App::GeoFeature::getPlacementFromProp(movingObj.obj, "Placement"); plc = objPlc.inverse() * plc; // Change plc to be relative to the origin of the document. Base::Placement global_plc = - AssemblyObject::getGlobalPlacement(movingObj.obj, movingObj.rootObj, movingObj.sub); + App::GeoFeature::getGlobalPlacement(movingObj.obj, movingObj.rootObj, movingObj.sub); plc = global_plc * plc; bboxCenter = plc.getPosition(); diff --git a/src/Mod/BIM/ArchMaterial.py b/src/Mod/BIM/ArchMaterial.py index 5cea4087863c..0c1a1262344f 100644 --- a/src/Mod/BIM/ArchMaterial.py +++ b/src/Mod/BIM/ArchMaterial.py @@ -515,6 +515,7 @@ def reject(self): def chooseMat(self, card): "sets self.material from a card" + card = self.form.comboBox_MaterialsInDir.currentText() if card in self.cards: import importFCMat self.material = importFCMat.read(self.cards[card]) diff --git a/src/Mod/BIM/bimcommands/BimWelcome.py b/src/Mod/BIM/bimcommands/BimWelcome.py index a2cb8a6fc2b2..f06e52e2c446 100644 --- a/src/Mod/BIM/bimcommands/BimWelcome.py +++ b/src/Mod/BIM/bimcommands/BimWelcome.py @@ -43,16 +43,14 @@ def GetResources(self): def Activated(self): from PySide import QtCore, QtGui - # load dialog self.form = FreeCADGui.PySideUic.loadUi(":ui/dialogWelcome.ui") - # set the title image - self.form.image.setPixmap(QtGui.QPixmap(":/icons/banner.png")) - # handle the tutorial links self.form.label_4.linkActivated.connect(self.handleLink) self.form.label_7.linkActivated.connect(self.handleLink) + self.form.adjustSize() + # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.form.move( diff --git a/src/Mod/BIM/importers/exportIFC.py b/src/Mod/BIM/importers/exportIFC.py index d92466ad36b7..9e475fc6d19b 100644 --- a/src/Mod/BIM/importers/exportIFC.py +++ b/src/Mod/BIM/importers/exportIFC.py @@ -308,9 +308,9 @@ def export(exportList, filename, colors=None, preferences=None): project = contextCreator.project objectslist = [obj for obj in objectslist if obj != contextCreator.project_object] - if Draft.getObjectsOfType(objectslist, "Site"): # we assume one site and one representation context only - decl = Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs(FreeCAD.Units.Radian) - contextCreator.model_context.TrueNorth.DirectionRatios = (math.cos(decl+math.pi/2), math.sin(decl+math.pi/2)) + if Draft.getObjectsOfType(objectslist, "Site"): # we assume one site and one representation context only + decl = Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs(FreeCAD.Units.Radian) + contextCreator.model_context.TrueNorth.DirectionRatios = (math.cos(decl+math.pi/2), math.sin(decl+math.pi/2)) # reusable entity system diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 487d8f703d0f..4536b3a56012 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -1305,18 +1305,24 @@ def displayPoint(self, point=None, last=None, plane=None, mask=None): self.xValue.setEnabled(True) self.yValue.setEnabled(False) self.zValue.setEnabled(False) + self.yValue.setText("0") + self.zValue.setText("0") self.angleValue.setEnabled(False) self.setFocus() elif (mask == "y") or (self.mask == "y"): self.xValue.setEnabled(False) self.yValue.setEnabled(True) self.zValue.setEnabled(False) + self.xValue.setText("0") + self.zValue.setText("0") self.angleValue.setEnabled(False) self.setFocus("y") elif (mask == "z") or (self.mask == "z"): self.xValue.setEnabled(False) self.yValue.setEnabled(False) self.zValue.setEnabled(True) + self.xValue.setText("0") + self.yValue.setText("0") self.angleValue.setEnabled(False) self.setFocus("z") else: diff --git a/src/Mod/Draft/draftfunctions/svg.py b/src/Mod/Draft/draftfunctions/svg.py index 11896b34ae0e..93571f71a19c 100644 --- a/src/Mod/Draft/draftfunctions/svg.py +++ b/src/Mod/Draft/draftfunctions/svg.py @@ -904,14 +904,26 @@ def get_svg(obj, return svg +# Similar function as in view_layer.py +def _get_layer(obj): + """Get the layer the object belongs to.""" + finds = obj.Document.findObjects(Name="LayerContainer") + if not finds: + return None + for layer in finds[0].Group: + if utils.get_type(layer) == "Layer" and obj in layer.Group: + return layer + return None + + def get_print_color(obj): - """returns the print color of the parent layer, if available""" - for parent in obj.InListRecursive: - if (hasattr(parent,"ViewObject") - and hasattr(parent.ViewObject,"UsePrintColor") - and parent.ViewObject.UsePrintColor): - if hasattr(parent.ViewObject,"LinePrintColor"): - return parent.ViewObject.LinePrintColor + """Return the print color of the parent layer, if available.""" + # Layers are not in the Inlist of obj because a layer's Group is App::PropertyLinkListHidden: + layer = _get_layer(obj) + if layer is None: + return None + if layer.ViewObject.UsePrintColor: + return layer.ViewObject.LinePrintColor return None diff --git a/src/Mod/Draft/draftguitools/gui_trackers.py b/src/Mod/Draft/draftguitools/gui_trackers.py index 8ecc3748a378..4fe4d406c153 100644 --- a/src/Mod/Draft/draftguitools/gui_trackers.py +++ b/src/Mod/Draft/draftguitools/gui_trackers.py @@ -89,13 +89,24 @@ def finalize(self): ToDo.delay(self._removeSwitch, self.switch) self.switch = None + def get_scene_graph(self): + """Returns the current scenegraph or None if this is not a 3D view + """ + v = Draft.get3DView() + if v: + return v.getSceneGraph() + else: + return None + def _insertSwitch(self, switch): """Insert self.switch into the scene graph. Must not be called from an event handler (or other scene graph traversal). """ - sg = Draft.get3DView().getSceneGraph() + sg = self.get_scene_graph() + if not sg: + return if self.ontop: sg.insertChild(switch, 0) else: @@ -107,7 +118,9 @@ def _removeSwitch(self, switch): As with _insertSwitch, must not be called during scene graph traversal). """ - sg = Draft.get3DView().getSceneGraph() + sg = self.get_scene_graph() + if not sg: + return if sg.findChild(switch) >= 0: sg.removeChild(switch) @@ -127,7 +140,9 @@ def lowerTracker(self): So it doesn't obscure the other objects. """ if self.switch: - sg = Draft.get3DView().getSceneGraph() + sg = self.get_scene_graph() + if not sg: + return sg.removeChild(self.switch) sg.addChild(self.switch) @@ -137,7 +152,9 @@ def raiseTracker(self): So it obscures the other objects. """ if self.switch: - sg = Draft.get3DView().getSceneGraph() + sg = self.get_scene_graph() + if not sg: + return sg.removeChild(self.switch) sg.insertChild(self.switch, 0) diff --git a/src/Mod/Measure/Gui/TaskMeasure.cpp b/src/Mod/Measure/Gui/TaskMeasure.cpp index 455ea6f3af08..a227c31f4e25 100644 --- a/src/Mod/Measure/Gui/TaskMeasure.cpp +++ b/src/Mod/Measure/Gui/TaskMeasure.cpp @@ -251,7 +251,6 @@ void TaskMeasure::saveObject() _mDocument = App::GetApplication().getActiveDocument(); _mDocument->addObject(_mMeasureObject, _mMeasureType->label.c_str()); - _mMeasureObject = nullptr; } @@ -387,8 +386,6 @@ bool TaskMeasure::apply() { saveObject(); ensureGroup(_mMeasureObject); - _mMeasureType = nullptr; - _mMeasureObject = nullptr; reset(); // Commit transaction @@ -411,6 +408,7 @@ void TaskMeasure::reset() { // Reset tool state _mMeasureType = nullptr; + _mMeasureObject = nullptr; this->clearSelection(); // Should the explicit mode also be reset? @@ -481,7 +479,9 @@ bool TaskMeasure::eventFilter(QObject* obj, QEvent* event) } if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { - this->apply(); + // Save object. Indirectly dependent on whether the apply button is enabled + // enabled if valid measurement object. + this->buttonBox->button(QDialogButtonBox::Apply)->click(); return true; } } diff --git a/src/Mod/Part/App/Geometry2d.cpp b/src/Mod/Part/App/Geometry2d.cpp index eb04a397206e..4095d4afb346 100644 --- a/src/Mod/Part/App/Geometry2d.cpp +++ b/src/Mod/Part/App/Geometry2d.cpp @@ -174,7 +174,7 @@ void Geom2dPoint::Save(Base::Writer &writer) const << "" << endl; + << "/>" << std::endl; } void Geom2dPoint::Restore(Base::XMLReader &reader) @@ -848,7 +848,7 @@ void Geom2dCircle::Save(Base::Writer& writer) const SaveAxis(writer, axis); writer.Stream() << "Radius=\"" << c.Radius() << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dCircle::Restore(Base::XMLReader& reader) @@ -1007,7 +1007,7 @@ void Geom2dArcOfCircle::Save(Base::Writer &writer) const SaveAxis(writer, axis, u, v); writer.Stream() << "Radius=\"" << c.Radius() << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dArcOfCircle::Restore(Base::XMLReader &reader) @@ -1167,7 +1167,7 @@ void Geom2dEllipse::Save(Base::Writer& writer) const writer.Stream() << "MajorRadius=\"" << e.MajorRadius() << "\" " << "MinorRadius=\"" << e.MinorRadius() << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dEllipse::Restore(Base::XMLReader& reader) @@ -1342,7 +1342,7 @@ void Geom2dArcOfEllipse::Save(Base::Writer &writer) const writer.Stream() << "MajorRadius=\"" << e.MajorRadius() << "\" " << "MinorRadius=\"" << e.MinorRadius() << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dArcOfEllipse::Restore(Base::XMLReader &reader) @@ -1469,7 +1469,7 @@ void Geom2dHyperbola::Save(Base::Writer& writer) const writer.Stream() << "MajorRadius=\"" << h.MajorRadius() << "\" " << "MinorRadius=\"" << h.MinorRadius() << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dHyperbola::Restore(Base::XMLReader& reader) @@ -1600,7 +1600,7 @@ void Geom2dArcOfHyperbola::Save(Base::Writer &writer) const writer.Stream() << "MajorRadius=\"" << h.MajorRadius() << "\" " << "MinorRadius=\"" << h.MinorRadius() << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dArcOfHyperbola::Restore(Base::XMLReader &reader) @@ -1709,7 +1709,7 @@ void Geom2dParabola::Save(Base::Writer& writer) const SaveAxis(writer, axis); writer.Stream() << "Focal=\"" << focal << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dParabola::Restore(Base::XMLReader& reader) @@ -1820,7 +1820,7 @@ void Geom2dArcOfParabola::Save(Base::Writer &writer) const SaveAxis(writer, axis, u, v); writer.Stream() << "Focal=\"" << focal << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dArcOfParabola::Restore(Base::XMLReader &reader) @@ -1933,7 +1933,7 @@ void Geom2dLine::Save(Base::Writer &writer) const << "PosY=\"" << Pos.y << "\" " << "DirX=\"" << Dir.x << "\" " << "DirY=\"" << Dir.y << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dLine::Restore(Base::XMLReader &reader) @@ -2065,7 +2065,7 @@ void Geom2dLineSegment::Save(Base::Writer &writer) const << "StartY=\"" << Start.y << "\" " << "EndX=\"" << End.x << "\" " << "EndY=\"" << End.y << "\" " - << "/>" << endl; + << "/>" << std::endl; } void Geom2dLineSegment::Restore(Base::XMLReader &reader)