From c5112ecdc578341008f747cc195b79af1bc9f60c Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Sat, 6 Jul 2019 15:20:16 +0800 Subject: [PATCH] (GeoFeature)GroupExtension: track children visibility The future patch will introduce Part::getTopoShape() to construct a compound shape from a group. It will rely on the children visibility to determine whether to include the child shape or not. This patch adds children visibility tracking capability to group, and makes sure that the group object will be marked for recomputation in case of any change in group member, and their visibility status. * Remove Prop_Output from 'Group' property. * Added hidden property _GroupTouched to help propagate children change. * Track children visibility change using signal * GeoFeatureGroupExtension uses new PropertyLinkBase interface for scope checking. --- src/App/GeoFeatureGroupExtension.cpp | 39 ++++------------ src/App/GroupExtension.cpp | 65 ++++++++++++++++++++++---- src/App/GroupExtension.h | 11 +++++ src/Gui/ViewProviderGroupExtension.cpp | 57 ++++------------------ src/Gui/ViewProviderGroupExtension.h | 2 - 5 files changed, 86 insertions(+), 88 deletions(-) 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; };