Skip to content

Commit

Permalink
(GeoFeature)GroupExtension: track children visibility
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
realthunder authored and wwmayer committed Aug 17, 2019
1 parent 94c2289 commit c5112ec
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 88 deletions.
39 changes: 10 additions & 29 deletions src/App/GeoFeatureGroupExtension.cpp
Expand Up @@ -191,9 +191,10 @@ std::vector<DocumentObject*> 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();
Expand All @@ -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<Property::Status, Property> guard(Property::User3, &Group);
Group.setValues(corrected);
throw Base::RuntimeError("Object can only be in a single GeoFeatureGroup");
}
Expand Down Expand Up @@ -252,35 +254,14 @@ std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLin
return std::vector< DocumentObject* >();

std::vector< App::DocumentObject* > result;
auto link = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
if(link && link->getScope()==scope)
link->getLinks(result);

if(prop->getTypeId().isDerivedFrom(App::PropertyLink::getClassTypeId()) &&
static_cast<App::PropertyLink*>(prop)->getScope() == scope) {

result.push_back(static_cast<App::PropertyLink*>(prop)->getValue());
}

if(prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId()) &&
static_cast<App::PropertyLinkList*>(prop)->getScope() == scope) {

auto vec = static_cast<App::PropertyLinkList*>(prop)->getValues();
result.insert(result.end(), vec.begin(), vec.end());
}

if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSub::getClassTypeId()) &&
static_cast<App::PropertyLinkSub*>(prop)->getScope() == scope) {

result.push_back(static_cast<App::PropertyLinkSub*>(prop)->getValue());
}

if(prop->getTypeId().isDerivedFrom(App::PropertyLinkSubList::getClassTypeId()) &&
static_cast<App::PropertyLinkSubList*>(prop)->getScope() == scope) {

auto vec = static_cast<App::PropertyLinkSubList*>(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;
}

Expand Down
65 changes: 56 additions & 9 deletions src/App/GroupExtension.cpp
Expand Up @@ -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()
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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<Property::Status, Property> 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
{
Expand Down Expand Up @@ -382,6 +401,34 @@ bool GroupExtension::extensionGetSubObjects(std::vector<std::string> &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<App::DocumentObject*> GroupExtension::getAllChildren() const {
std::vector<DocumentObject*> res;
std::set<DocumentObject*> rset;
getAllChildren(res,rset);
return res;
}

void GroupExtension::getAllChildren(std::vector<App::DocumentObject*> &res,
std::set<App::DocumentObject*> &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<GroupExtension>(true,false);
if(ext)
ext->getAllChildren(res,rset);
}
}

namespace App {
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension)

Expand Down
11 changes: 11 additions & 0 deletions src/App/GroupExtension.h
Expand Up @@ -24,6 +24,7 @@
#ifndef APP_GROUPEXTENSION_H
#define APP_GROUPEXTENSION_H

#include <boost/signals2.hpp>
#include "FeaturePython.h"
#include "DocumentObject.h"
#include "PropertyLinks.h"
Expand Down Expand Up @@ -116,14 +117,24 @@ class AppExport GroupExtension : public DocumentObjectExtension

virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;

virtual App::DocumentObjectExecReturn *extensionExecute(void) override;

std::vector<DocumentObject*> getAllChildren() const;
void getAllChildren(std::vector<DocumentObject*> &, std::set<DocumentObject*> &) 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<const GroupExtension*> history) const;

// for tracking children visibility
void slotChildChanged(const App::DocumentObject&, const App::Property&);
std::unordered_map<const App::DocumentObject*, boost::signals2::scoped_connection> _Conns;
};


Expand Down
57 changes: 9 additions & 48 deletions src/Gui/ViewProviderGroupExtension.cpp
Expand Up @@ -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());
}
Expand Down Expand Up @@ -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<App::DocumentObject*>(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<App::GroupExtension>();
Expand All @@ -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<App::GroupExtension>();

const std::vector<App::DocumentObject*> & links = group->Group.getValues();
Gui::Document* doc = Application::Instance->getDocument(group->getExtendedObject()->getDocument());
for (std::vector<App::DocumentObject*>::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) {
Expand All @@ -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<App::GroupExtension>();

const std::vector<App::DocumentObject*> & 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<App::DocumentObject*>::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 >& ) {
Expand Down
2 changes: 0 additions & 2 deletions src/Gui/ViewProviderGroupExtension.h
Expand Up @@ -46,15 +46,13 @@ class GuiExport ViewProviderGroupExtension : public ViewProviderExtension
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;

virtual bool extensionOnDelete(const std::vector<std::string> &) override;

private:
bool visible; // helper variable
bool guard;
std::vector<ViewProvider*> nodes;
};
Expand Down

0 comments on commit c5112ec

Please sign in to comment.