diff --git a/src/App/GeoFeatureGroupExtension.cpp b/src/App/GeoFeatureGroupExtension.cpp index 0024cd5d3f..bceefa9a08 100644 --- a/src/App/GeoFeatureGroupExtension.cpp +++ b/src/App/GeoFeatureGroupExtension.cpp @@ -191,9 +191,10 @@ std::vector GeoFeatureGroupExtension::removeObjects(std::vector void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) { //objects are only allowed in a single GeoFeatureGroup - if((strcmp(p->getName(), "Group")==0)) { + if(p == &Group && !Group.testStatus(Property::User3)) { - if(!getExtendedObject()->getDocument()->isPerformingTransaction()) { + if(!getExtendedObject()->isRestoring() && + !getExtendedObject()->getDocument()->isPerformingTransaction()) { bool error = false; auto corrected = Group.getValues(); @@ -215,6 +216,7 @@ void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) { //if an error was found we need to correct the values and inform the user if(error) { + Base::ObjectStatusLocker guard(Property::User3, &Group); Group.setValues(corrected); throw Base::RuntimeError("Object can only be in a single GeoFeatureGroup"); } @@ -252,35 +254,14 @@ std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLin return std::vector< DocumentObject* >(); std::vector< App::DocumentObject* > result; + auto link = Base::freecad_dynamic_cast(prop); + if(link && link->getScope()==scope) + link->getLinks(result); - if(prop->getTypeId().isDerivedFrom(App::PropertyLink::getClassTypeId()) && - static_cast(prop)->getScope() == scope) { - - result.push_back(static_cast(prop)->getValue()); - } - - if(prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId()) && - static_cast(prop)->getScope() == scope) { - - auto vec = static_cast(prop)->getValues(); - result.insert(result.end(), vec.begin(), vec.end()); - } - - if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSub::getClassTypeId()) && - static_cast(prop)->getScope() == scope) { - - result.push_back(static_cast(prop)->getValue()); - } - - if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSubList::getClassTypeId()) && - static_cast(prop)->getScope() == scope) { - - auto vec = static_cast(prop)->getValues(); - result.insert(result.end(), vec.begin(), vec.end()); - } - + //getLinks() guarantees no nullptrs + // //it is important to remove all nullptrs - result.erase(std::remove(result.begin(), result.end(), nullptr), result.end()); + // result.erase(std::remove(result.begin(), result.end(), nullptr), result.end()); return result; } diff --git a/src/App/GroupExtension.cpp b/src/App/GroupExtension.cpp index 45a956b837..e716c315a7 100644 --- a/src/App/GroupExtension.cpp +++ b/src/App/GroupExtension.cpp @@ -43,7 +43,10 @@ GroupExtension::GroupExtension() { initExtensionType(GroupExtension::getExtensionClassTypeId()); - EXTENSION_ADD_PROPERTY_TYPE(Group,(0),"Base",(App::PropertyType)(Prop_Output),"List of referenced objects"); + EXTENSION_ADD_PROPERTY_TYPE(Group,(0),"Base",(App::PropertyType)(Prop_None),"List of referenced objects"); + + EXTENSION_ADD_PROPERTY_TYPE(_GroupTouched, (false), "Base", + PropertyType(Prop_Hidden|Prop_Transient),0); } GroupExtension::~GroupExtension() @@ -291,10 +294,9 @@ DocumentObject* GroupExtension::getGroupOfObject(const DocumentObject* obj) //note that we return here only Groups, but nothing derived from it, e.g. no GeoFeatureGroups. //That is important as there are clear differences between groups/geofeature groups (e.g. an object //can be in only one group, and only one geofeaturegroup, however, it can be in both at the same time) - auto list = obj->getInList(); - for (auto obj : list) { - if(obj->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false)) - return obj; + for (auto o : obj->getInList()) { + if(o->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false)) + return o; } return nullptr; @@ -314,10 +316,11 @@ void GroupExtension::extensionOnChanged(const Property* p) { //objects are only allowed in a single group. Note that this check must only be done for normal //groups, not any derived classes - if((this->getExtensionTypeId() == GroupExtension::getExtensionClassTypeId()) && - (strcmp(p->getName(), "Group")==0)) { - - if(!getExtendedObject()->getDocument()->isPerformingTransaction()) { + if((this->getExtensionTypeId() == GroupExtension::getExtensionClassTypeId()) + && p == &Group && !Group.testStatus(Property::User3)) + { + if(!getExtendedObject()->isRestoring() && + !getExtendedObject()->getDocument()->isPerformingTransaction()) { bool error = false; auto corrected = Group.getValues(); @@ -337,15 +340,31 @@ void GroupExtension::extensionOnChanged(const Property* p) { //if an error was found we need to correct the values and inform the user if(error) { + Base::ObjectStatusLocker guard(Property::User3, &Group); Group.setValues(corrected); throw Base::RuntimeError("Object can only be in a single Group"); } } } + if(p == &Group) { + _Conns.clear(); + for(auto obj : Group.getValue()) { + if(obj && obj->getNameInDocument()) { + _Conns[obj] = obj->signalChanged.connect(boost::bind( + &GroupExtension::slotChildChanged,this,_1,_2)); + } + } + } + App::Extension::extensionOnChanged(p); } +void GroupExtension::slotChildChanged(const DocumentObject &obj, const Property &prop) { + if(&prop == &obj.Visibility) + _GroupTouched.touch(); +} + bool GroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj, Base::Matrix4D *mat, bool /*transform*/, int depth) const { @@ -382,6 +401,34 @@ bool GroupExtension::extensionGetSubObjects(std::vector &ret, int) return true; } +App::DocumentObjectExecReturn *GroupExtension::extensionExecute(void) { + // This touch property is for propagating changes to upper group + _GroupTouched.touch(); + return inherited::extensionExecute(); +} + +std::vector GroupExtension::getAllChildren() const { + std::vector res; + std::set rset; + getAllChildren(res,rset); + return res; +} + +void GroupExtension::getAllChildren(std::vector &res, + std::set &rset) const +{ + for(auto obj : Group.getValues()) { + if(!obj || !obj->getNameInDocument()) + continue; + if(!rset.insert(obj).second) + continue; + res.push_back(obj); + auto ext = obj->getExtensionByType(true,false); + if(ext) + ext->getAllChildren(res,rset); + } +} + namespace App { EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension) diff --git a/src/App/GroupExtension.h b/src/App/GroupExtension.h index c1c936b317..e5a3e7f52a 100644 --- a/src/App/GroupExtension.h +++ b/src/App/GroupExtension.h @@ -24,6 +24,7 @@ #ifndef APP_GROUPEXTENSION_H #define APP_GROUPEXTENSION_H +#include #include "FeaturePython.h" #include "DocumentObject.h" #include "PropertyLinks.h" @@ -116,14 +117,24 @@ public: virtual bool extensionGetSubObjects(std::vector &ret, int reason) const override; + virtual App::DocumentObjectExecReturn *extensionExecute(void) override; + + std::vector getAllChildren() const; + void getAllChildren(std::vector &, std::set &) const; + /// Properties PropertyLinkList Group; + PropertyBool _GroupTouched; private: void removeObjectFromDocument(DocumentObject*); //this version if has object stores the already searched objects to prevent infinite recursion //in case of a cyclic group graph bool recursiveHasObject(const DocumentObject* obj, const GroupExtension* group, std::vector history) const; + + // for tracking children visibility + void slotChildChanged(const App::DocumentObject&, const App::Property&); + std::unordered_map _Conns; }; diff --git a/src/Gui/ViewProviderGroupExtension.cpp b/src/Gui/ViewProviderGroupExtension.cpp index 23b3f33701..81f043e89d 100644 --- a/src/Gui/ViewProviderGroupExtension.cpp +++ b/src/Gui/ViewProviderGroupExtension.cpp @@ -45,7 +45,7 @@ using namespace Gui; EXTENSION_PROPERTY_SOURCE(Gui::ViewProviderGroupExtension, Gui::ViewProviderExtension) -ViewProviderGroupExtension::ViewProviderGroupExtension() : visible(false), guard(false) +ViewProviderGroupExtension::ViewProviderGroupExtension() : guard(false) { initExtensionType(ViewProviderGroupExtension::getExtensionClassTypeId()); } @@ -110,29 +110,6 @@ void ViewProviderGroupExtension::extensionDropObject(App::DocumentObject* obj) { Gui::Command::doCommand(Gui::Command::App, cmd.toUtf8()); } -void ViewProviderGroupExtension::extensionReplaceObject(App::DocumentObject* oldValue, App::DocumentObject* newValue) { - - App::DocumentObject* grp = static_cast(getExtendedViewProvider()->getObject()); - App::Document* doc = grp->getDocument(); - - // build Python command for execution - QString cmd; - cmd = QString::fromLatin1("App.getDocument(\"%1\").getObject(\"%2\").removeObject(" - "App.getDocument(\"%1\").getObject(\"%3\"))") - .arg(QString::fromLatin1(doc->getName()), - QString::fromLatin1(grp->getNameInDocument()), - QString::fromLatin1(oldValue->getNameInDocument())); - - Gui::Command::doCommand(Gui::Command::App, cmd.toUtf8()); - cmd = QString::fromLatin1("App.getDocument(\"%1\").getObject(\"%2\").addObject(" - "App.getDocument(\"%1\").getObject(\"%3\"))") - .arg(QString::fromLatin1(doc->getName()), - QString::fromLatin1(grp->getNameInDocument()), - QString::fromLatin1(newValue->getNameInDocument())); - - Gui::Command::doCommand(Gui::Command::App, cmd.toUtf8()); -} - std::vector< App::DocumentObject* > ViewProviderGroupExtension::extensionClaimChildren(void) const { auto* group = getExtendedViewProvider()->getObject()->getExtensionByType(); @@ -148,20 +125,15 @@ void ViewProviderGroupExtension::extensionShow(void) { // when reading the Visibility property from file then do not hide the // objects of this group because they have stored their visibility status, too - if (!getExtendedViewProvider()->isRestoring() && !this->visible) { + if (!getExtendedViewProvider()->isRestoring() ) { auto* group = getExtendedViewProvider()->getObject()->getExtensionByType(); - - const std::vector & links = group->Group.getValues(); - Gui::Document* doc = Application::Instance->getDocument(group->getExtendedObject()->getDocument()); - for (std::vector::const_iterator it = links.begin(); it != links.end(); ++it) { - ViewProvider* view = doc->getViewProvider(*it); - if (view) - view->show(); + for(auto obj : group->Group.getValues()) { + if(obj && !obj->Visibility.getValue()) + obj->Visibility.setValue(true); } } ViewProviderExtension::extensionShow(); - this->visible = true; } void ViewProviderGroupExtension::extensionHide(void) { @@ -173,25 +145,14 @@ void ViewProviderGroupExtension::extensionHide(void) { // when reading the Visibility property from file then do not hide the // objects of this group because they have stored their visibility status, too - if (!getExtendedViewProvider()->isRestoring() && this->visible) { - + if (!getExtendedViewProvider()->isRestoring()) { auto* group = getExtendedViewProvider()->getObject()->getExtensionByType(); - - const std::vector & links = group->Group.getValues(); - Gui::Document* doc = Application::Instance->getDocument(getExtendedViewProvider()->getObject()->getDocument()); - // doc pointer can be null in case the document is about to be destroyed - // See https://forum.freecadweb.org/viewtopic.php?f=22&t=26797&p=218804#p218521 - if (doc) { - for (std::vector::const_iterator it = links.begin(); it != links.end(); ++it) { - ViewProvider* view = doc->getViewProvider(*it); - if (view) - view->hide(); - } + for(auto obj : group->Group.getValues()) { + if(obj && obj->Visibility.getValue()) + obj->Visibility.setValue(false); } } - ViewProviderExtension::extensionHide(); - this->visible = false; } bool ViewProviderGroupExtension::extensionOnDelete(const std::vector< std::string >& ) { diff --git a/src/Gui/ViewProviderGroupExtension.h b/src/Gui/ViewProviderGroupExtension.h index 2e25622841..2023f4e9d9 100644 --- a/src/Gui/ViewProviderGroupExtension.h +++ b/src/Gui/ViewProviderGroupExtension.h @@ -46,7 +46,6 @@ public: virtual bool extensionCanDropObjects() const override; virtual bool extensionCanDropObject(App::DocumentObject*) const override; virtual void extensionDropObject(App::DocumentObject*) override; - virtual void extensionReplaceObject(App::DocumentObject* oldValue, App::DocumentObject* newValue) override; virtual void extensionHide(void) override; virtual void extensionShow(void) override; @@ -54,7 +53,6 @@ public: virtual bool extensionOnDelete(const std::vector &) override; private: - bool visible; // helper variable bool guard; std::vector nodes; };