App: add hiddenref() expression built-in function
Any object reference inside this function is treated as hidden to exclude it from dependency calculation. This function allows some form of cyclic depdenency. Merger note: renamed from "HREF" to "HIDDENREF" to avoid confusion with the standard "hypertext reference" use of HREF.
This commit is contained in:
@@ -444,13 +444,14 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
|
||||
auto i = expressions.begin();
|
||||
while (i != expressions.end()) {
|
||||
std::set<ObjectIdentifier> deps;
|
||||
std::map<ObjectIdentifier,bool> deps;
|
||||
|
||||
i->second->getIdentifiers(deps);
|
||||
|
||||
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
|
||||
while (j != deps.end()) {
|
||||
DocumentObject * o = j->getDocumentObject();
|
||||
for(auto j=deps.begin(); j!=deps.end(); ++j) {
|
||||
if(j->second)
|
||||
continue;
|
||||
DocumentObject * o = j->first.getDocumentObject();
|
||||
|
||||
// Doesn't exist already?
|
||||
if (o && !GraphList[o]) {
|
||||
@@ -469,7 +470,6 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
}
|
||||
|
||||
}
|
||||
++j;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
@@ -554,24 +554,23 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
while (i != expressions.end()) {
|
||||
|
||||
// Get dependencies
|
||||
std::set<ObjectIdentifier> deps;
|
||||
std::map<ObjectIdentifier,bool> deps;
|
||||
i->second->getIdentifiers(deps);
|
||||
|
||||
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
|
||||
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
|
||||
while (j != deps.end()) {
|
||||
DocumentObject * depObjDoc = j->getDocumentObject();
|
||||
std::map<std::string, Vertex>::const_iterator k = GlobalVertexList.find(getId(*j));
|
||||
for(auto j=deps.begin(); j!=deps.end(); ++j) {
|
||||
if(j->second)
|
||||
continue;
|
||||
DocumentObject * depObjDoc = j->first.getDocumentObject();
|
||||
std::map<std::string, Vertex>::const_iterator k = GlobalVertexList.find(getId(j->first));
|
||||
|
||||
if (k == GlobalVertexList.end()) {
|
||||
Graph * depSgraph = GraphList[depObjDoc] ? GraphList[depObjDoc] : &DepList;
|
||||
|
||||
LocalVertexList[getId(*j)] = add_vertex(*depSgraph);
|
||||
GlobalVertexList[getId(*j)] = vertex_no++;
|
||||
setPropertyVertexAttributes(*depSgraph, LocalVertexList[getId(*j)], j->getPropertyName() + j->getSubPathStr());
|
||||
LocalVertexList[getId(j->first)] = add_vertex(*depSgraph);
|
||||
GlobalVertexList[getId(j->first)] = vertex_no++;
|
||||
setPropertyVertexAttributes(*depSgraph, LocalVertexList[getId(j->first)], j->first.getPropertyName() + j->first.getSubPathStr());
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
@@ -703,17 +702,18 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
auto i = expressions.begin();
|
||||
|
||||
while (i != expressions.end()) {
|
||||
std::set<ObjectIdentifier> deps;
|
||||
std::map<ObjectIdentifier,bool> deps;
|
||||
i->second->getIdentifiers(deps);
|
||||
|
||||
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
|
||||
std::set<ObjectIdentifier>::const_iterator k = deps.begin();
|
||||
while (k != deps.end()) {
|
||||
DocumentObject * depObjDoc = k->getDocumentObject();
|
||||
for(auto k=deps.begin(); k!=deps.end(); ++k) {
|
||||
if(k->second)
|
||||
continue;
|
||||
DocumentObject * depObjDoc = k->first.getDocumentObject();
|
||||
Edge edge;
|
||||
bool inserted;
|
||||
|
||||
tie(edge, inserted) = add_edge(GlobalVertexList[getId(i->first)], GlobalVertexList[getId(*k)], DepList);
|
||||
tie(edge, inserted) = add_edge(GlobalVertexList[getId(i->first)], GlobalVertexList[getId(k->first)], DepList);
|
||||
|
||||
// Add this edge to the set of all expression generated edges
|
||||
existingEdges.insert(std::make_pair(docObj, depObjDoc));
|
||||
@@ -721,7 +721,6 @@ void Document::exportGraphviz(std::ostream& out) const
|
||||
// Edges between properties should be a bit smaller, and dashed
|
||||
edgeAttrMap[edge]["arrowsize"] = "0.5";
|
||||
edgeAttrMap[edge]["style"] = "dashed";
|
||||
++k;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
@@ -240,17 +240,7 @@ std::string unquote(const std::string & input)
|
||||
//
|
||||
// ExpressionVistor
|
||||
//
|
||||
void ExpressionVisitor::getDeps(Expression &e, ExpressionDeps &deps) {
|
||||
e._getDeps(deps);
|
||||
}
|
||||
|
||||
void ExpressionVisitor::getDepObjects(Expression &e,
|
||||
std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels)
|
||||
{
|
||||
e._getDepObjects(deps,labels);
|
||||
}
|
||||
|
||||
void ExpressionVisitor::getIdentifiers(Expression &e, std::set<App::ObjectIdentifier> &ids) {
|
||||
void ExpressionVisitor::getIdentifiers(Expression &e, std::map<App::ObjectIdentifier,bool> &ids) {
|
||||
e._getIdentifiers(ids);
|
||||
}
|
||||
|
||||
@@ -905,58 +895,52 @@ Expression * Expression::parse(const DocumentObject *owner, const std::string &b
|
||||
return ExpressionParser::parse(owner, buffer.c_str());
|
||||
}
|
||||
|
||||
class GetDepsExpressionVisitor : public ExpressionVisitor {
|
||||
public:
|
||||
GetDepsExpressionVisitor(ExpressionDeps &deps)
|
||||
:deps(deps)
|
||||
{}
|
||||
|
||||
virtual void visit(Expression &e) {
|
||||
this->getDeps(e,deps);
|
||||
void Expression::getDeps(ExpressionDeps &deps, int option) const {
|
||||
for(auto &v : getIdentifiers()) {
|
||||
bool hidden = v.second;
|
||||
const ObjectIdentifier &var = v.first;
|
||||
if((hidden && option==DepNormal)
|
||||
|| (!hidden && option==DepHidden))
|
||||
continue;
|
||||
for(auto &dep : var.getDep(true)) {
|
||||
DocumentObject *obj = dep.first;
|
||||
for(auto &propName : dep.second) {
|
||||
deps[obj][propName].push_back(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionDeps &deps;
|
||||
};
|
||||
|
||||
void Expression::getDeps(ExpressionDeps &deps) const {
|
||||
GetDepsExpressionVisitor v(deps);
|
||||
const_cast<Expression*>(this)->visit(v);
|
||||
}
|
||||
|
||||
ExpressionDeps Expression::getDeps() const {
|
||||
ExpressionDeps Expression::getDeps(int option) const {
|
||||
ExpressionDeps deps;
|
||||
getDeps(deps);
|
||||
getDeps(deps, option);
|
||||
return deps;
|
||||
}
|
||||
|
||||
class GetDepObjsExpressionVisitor : public ExpressionVisitor {
|
||||
public:
|
||||
GetDepObjsExpressionVisitor(std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels)
|
||||
:deps(deps),labels(labels)
|
||||
{}
|
||||
|
||||
virtual void visit(Expression &e) {
|
||||
this->getDepObjects(e,deps,labels);
|
||||
void Expression::getDepObjects(
|
||||
std::map<App::DocumentObject*,bool> &deps, std::vector<std::string> *labels) const
|
||||
{
|
||||
for(auto &v : getIdentifiers()) {
|
||||
bool hidden = v.second;
|
||||
const ObjectIdentifier &var = v.first;
|
||||
for(auto &dep : var.getDep(false,labels)) {
|
||||
DocumentObject *obj = dep.first;
|
||||
auto res = deps.insert(std::make_pair(obj,hidden));
|
||||
if(!hidden || res.second)
|
||||
res.first->second = hidden;
|
||||
}
|
||||
}
|
||||
|
||||
std::set<App::DocumentObject*> &deps;
|
||||
std::vector<std::string> *labels;
|
||||
};
|
||||
|
||||
void Expression::getDepObjects(std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels) const {
|
||||
GetDepObjsExpressionVisitor v(deps,labels);
|
||||
const_cast<Expression *>(this)->visit(v);
|
||||
}
|
||||
|
||||
std::set<App::DocumentObject*> Expression::getDepObjects(std::vector<std::string> *labels) const {
|
||||
std::set<App::DocumentObject*> deps;
|
||||
std::map<App::DocumentObject*,bool> Expression::getDepObjects(std::vector<std::string> *labels) const {
|
||||
std::map<App::DocumentObject*,bool> deps;
|
||||
getDepObjects(deps,labels);
|
||||
return deps;
|
||||
}
|
||||
|
||||
class GetIdentifiersExpressionVisitor : public ExpressionVisitor {
|
||||
public:
|
||||
GetIdentifiersExpressionVisitor(std::set<App::ObjectIdentifier> &deps)
|
||||
GetIdentifiersExpressionVisitor(std::map<App::ObjectIdentifier,bool> &deps)
|
||||
:deps(deps)
|
||||
{}
|
||||
|
||||
@@ -964,16 +948,16 @@ public:
|
||||
this->getIdentifiers(e,deps);
|
||||
}
|
||||
|
||||
std::set<App::ObjectIdentifier> &deps;
|
||||
std::map<App::ObjectIdentifier,bool> &deps;
|
||||
};
|
||||
|
||||
void Expression::getIdentifiers(std::set<App::ObjectIdentifier> &deps) const {
|
||||
void Expression::getIdentifiers(std::map<App::ObjectIdentifier,bool> &deps) const {
|
||||
GetIdentifiersExpressionVisitor v(deps);
|
||||
const_cast<Expression*>(this)->visit(v);
|
||||
}
|
||||
|
||||
std::set<App::ObjectIdentifier> Expression::getIdentifiers() const {
|
||||
std::set<App::ObjectIdentifier> deps;
|
||||
std::map<App::ObjectIdentifier,bool> Expression::getIdentifiers() const {
|
||||
std::map<App::ObjectIdentifier,bool> deps;
|
||||
getIdentifiers(deps);
|
||||
return deps;
|
||||
}
|
||||
@@ -1016,7 +1000,7 @@ ExpressionPtr Expression::importSubNames(const std::map<std::string,std::string>
|
||||
if(!owner || !owner->getDocument())
|
||||
return 0;
|
||||
ObjectIdentifier::SubNameMap subNameMap;
|
||||
for(auto &dep : getDeps()) {
|
||||
for(auto &dep : getDeps(DepAll)) {
|
||||
for(auto &info : dep.second) {
|
||||
for(auto &path : info.second) {
|
||||
auto obj = path.getDocumentObject();
|
||||
@@ -1064,7 +1048,8 @@ ExpressionPtr Expression::updateLabelReference(
|
||||
if(ref.size()<=2)
|
||||
return ExpressionPtr();
|
||||
std::vector<std::string> labels;
|
||||
getDepObjects(&labels);
|
||||
for(auto &v : getIdentifiers())
|
||||
v.first.getDepLabels(labels);
|
||||
for(auto &label : labels) {
|
||||
// ref contains something like $label. and we need to strip '$' and '.'
|
||||
if(ref.compare(1,ref.size()-2,label)==0) {
|
||||
@@ -1709,6 +1694,32 @@ bool OperatorExpression::isRightAssociative() const
|
||||
|
||||
TYPESYSTEM_SOURCE(App::FunctionExpression, App::UnitExpression)
|
||||
|
||||
static int _HiddenReference;
|
||||
|
||||
struct HiddenReference {
|
||||
HiddenReference(bool cond)
|
||||
:cond(cond)
|
||||
{
|
||||
if(cond)
|
||||
++_HiddenReference;
|
||||
}
|
||||
~HiddenReference() {
|
||||
if(cond)
|
||||
--_HiddenReference;
|
||||
}
|
||||
|
||||
static bool check(int option) {
|
||||
return (option==Expression::DepNormal && _HiddenReference)
|
||||
|| (option==Expression::DepHidden && !_HiddenReference);
|
||||
}
|
||||
|
||||
static bool isHidden() {
|
||||
return _HiddenReference!=0;
|
||||
}
|
||||
|
||||
bool cond;
|
||||
};
|
||||
|
||||
FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f, std::string &&name, std::vector<Expression *> _args)
|
||||
: UnitExpression(_owner)
|
||||
, f(_f)
|
||||
@@ -1736,6 +1747,7 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
|
||||
case FLOOR:
|
||||
case MINVERT:
|
||||
case STR:
|
||||
case HIDDENREF:
|
||||
if (args.size() != 1)
|
||||
EXPR_THROW("Invalid number of arguments: exactly one required.");
|
||||
break;
|
||||
@@ -2087,6 +2099,8 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
return res;
|
||||
} else if (f == STR) {
|
||||
return Py::String(args[0]->getPyValue().as_string());
|
||||
} else if (f == HIDDENREF) {
|
||||
return args[0]->getPyValue();
|
||||
}
|
||||
|
||||
Py::Object e1 = args[0]->getPyValue();
|
||||
@@ -2219,7 +2233,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
unit = v1.getUnit();
|
||||
break;
|
||||
default:
|
||||
_EXPR_THROW("Unknown function: " << f,expr);
|
||||
_EXPR_THROW("Unknown function: " << f,0);
|
||||
}
|
||||
|
||||
/* Compute result */
|
||||
@@ -2299,7 +2313,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
output = floor(value);
|
||||
break;
|
||||
default:
|
||||
_EXPR_THROW("Unknown function: " << f,expr);
|
||||
_EXPR_THROW("Unknown function: " << f,0);
|
||||
}
|
||||
|
||||
return Py::asObject(new QuantityPy(new Quantity(scaler * output, unit)));
|
||||
@@ -2421,6 +2435,8 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
|
||||
ss << "create("; break;;
|
||||
case STR:
|
||||
ss << "str("; break;;
|
||||
case HIDDENREF:
|
||||
ss << "hiddenref("; break;;
|
||||
default:
|
||||
ss << fname << "("; break;;
|
||||
}
|
||||
@@ -2454,6 +2470,7 @@ void FunctionExpression::_visit(ExpressionVisitor &v)
|
||||
{
|
||||
std::vector<Expression*>::const_iterator i = args.begin();
|
||||
|
||||
HiddenReference ref(f == HIDDENREF);
|
||||
while (i != args.end()) {
|
||||
(*i)->visit(v);
|
||||
++i;
|
||||
@@ -2592,24 +2609,12 @@ Expression *VariableExpression::_copy() const
|
||||
return new VariableExpression(owner, var);
|
||||
}
|
||||
|
||||
void VariableExpression::_getDeps(ExpressionDeps &deps) const
|
||||
void VariableExpression::_getIdentifiers(std::map<App::ObjectIdentifier,bool> &deps) const
|
||||
{
|
||||
auto dep = var.getDep();
|
||||
if(dep.first)
|
||||
deps[dep.first][dep.second].push_back(var);
|
||||
}
|
||||
|
||||
void VariableExpression::_getDepObjects(
|
||||
std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels) const
|
||||
{
|
||||
auto dep = var.getDep(labels);
|
||||
if(dep.first)
|
||||
deps.insert(dep.first);
|
||||
}
|
||||
|
||||
void VariableExpression::_getIdentifiers(std::set<App::ObjectIdentifier> &deps) const
|
||||
{
|
||||
deps.insert(var);
|
||||
bool hidden = HiddenReference::isHidden();
|
||||
auto res = deps.insert(std::make_pair(var,hidden));
|
||||
if(!hidden || res.second)
|
||||
res.first->second = hidden;
|
||||
}
|
||||
|
||||
bool VariableExpression::_relabeledDocument(const std::string &oldName,
|
||||
@@ -2977,16 +2982,19 @@ Expression *RangeExpression::simplify() const
|
||||
return copy();
|
||||
}
|
||||
|
||||
void RangeExpression::_getDeps(ExpressionDeps &deps) const
|
||||
void RangeExpression::_getIdentifiers(std::map<App::ObjectIdentifier,bool> &deps) const
|
||||
{
|
||||
bool hidden = HiddenReference::isHidden();
|
||||
|
||||
assert(owner);
|
||||
|
||||
Range i(getRange());
|
||||
|
||||
auto &dep = deps[owner];
|
||||
do {
|
||||
std::string address = i.address();
|
||||
dep[address].push_back(ObjectIdentifier(owner,address));
|
||||
ObjectIdentifier var(owner,i.address());
|
||||
auto res = deps.insert(std::make_pair(var,hidden));
|
||||
if(!hidden || res.second)
|
||||
res.first->second = hidden;
|
||||
} while (i.next());
|
||||
}
|
||||
|
||||
@@ -3243,6 +3251,7 @@ static void initParser(const App::DocumentObject *owner)
|
||||
registered_functions["minvert"] = FunctionExpression::MINVERT;
|
||||
registered_functions["create"] = FunctionExpression::CREATE;
|
||||
registered_functions["str"] = FunctionExpression::STR;
|
||||
registered_functions["hiddenref"] = FunctionExpression::HIDDENREF;
|
||||
|
||||
// Aggregates
|
||||
registered_functions["sum"] = FunctionExpression::SUM;
|
||||
|
||||
@@ -51,7 +51,9 @@ typedef std::unique_ptr<Expression> ExpressionPtr;
|
||||
AppExport bool isAnyEqual(const App::any &v1, const App::any &v2);
|
||||
AppExport Base::Quantity anyToQuantity(const App::any &value, const char *errmsg = 0);
|
||||
|
||||
// Map of depending objects to a map of depending property name to the full referencing object identifier
|
||||
typedef std::map<App::DocumentObject*, std::map<std::string, std::vector<ObjectIdentifier> > > ExpressionDeps;
|
||||
|
||||
class AppExport ExpressionVisitor {
|
||||
public:
|
||||
virtual ~ExpressionVisitor() {}
|
||||
@@ -62,9 +64,7 @@ public:
|
||||
virtual App::PropertyLinkBase* getPropertyLink() {return 0;}
|
||||
|
||||
protected:
|
||||
void getIdentifiers(Expression &e, std::set<App::ObjectIdentifier> &);
|
||||
void getDeps(Expression &e, ExpressionDeps &);
|
||||
void getDepObjects(Expression &e, std::set<App::DocumentObject*> &, std::vector<std::string> *);
|
||||
void getIdentifiers(Expression &e, std::map<App::ObjectIdentifier, bool> &);
|
||||
bool adjustLinks(Expression &e, const std::set<App::DocumentObject*> &inList);
|
||||
bool relabeledDocument(Expression &e, const std::string &oldName, const std::string &newName);
|
||||
bool renameObjectIdentifier(Expression &e,
|
||||
@@ -135,14 +135,19 @@ public:
|
||||
|
||||
virtual int priority() const;
|
||||
|
||||
void getIdentifiers(std::set<App::ObjectIdentifier> &) const;
|
||||
std::set<App::ObjectIdentifier> getIdentifiers() const;
|
||||
void getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const;
|
||||
std::map<App::ObjectIdentifier,bool> getIdentifiers() const;
|
||||
|
||||
void getDeps(ExpressionDeps &deps) const;
|
||||
ExpressionDeps getDeps() const;
|
||||
enum DepOption {
|
||||
DepNormal,
|
||||
DepHidden,
|
||||
DepAll,
|
||||
};
|
||||
void getDeps(ExpressionDeps &deps, int option=DepNormal) const;
|
||||
ExpressionDeps getDeps(int option=DepNormal) const;
|
||||
|
||||
std::set<App::DocumentObject*> getDepObjects(std::vector<std::string> *labels=0) const;
|
||||
void getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *labels=0) const;
|
||||
std::map<App::DocumentObject*,bool> getDepObjects(std::vector<std::string> *labels=0) const;
|
||||
void getDepObjects(std::map<App::DocumentObject*,bool> &, std::vector<std::string> *labels=0) const;
|
||||
|
||||
ExpressionPtr importSubNames(const std::map<std::string,std::string> &nameMap) const;
|
||||
|
||||
@@ -188,9 +193,7 @@ protected:
|
||||
virtual bool _isIndexable() const {return false;}
|
||||
virtual Expression *_copy() const = 0;
|
||||
virtual void _toString(std::ostream &ss, bool persistent, int indent=0) const = 0;
|
||||
virtual void _getDeps(ExpressionDeps &) const {}
|
||||
virtual void _getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *) const {}
|
||||
virtual void _getIdentifiers(std::set<App::ObjectIdentifier> &) const {}
|
||||
virtual void _getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const {}
|
||||
virtual bool _adjustLinks(const std::set<App::DocumentObject*> &, ExpressionVisitor &) {return false;}
|
||||
virtual bool _updateElementReference(App::DocumentObject *,bool,ExpressionVisitor &) {return false;}
|
||||
virtual bool _relabeledDocument(const std::string &, const std::string &, ExpressionVisitor &) {return false;}
|
||||
|
||||
@@ -273,6 +273,7 @@ public:
|
||||
MINVERT, // invert matrix/placement/rotation
|
||||
CREATE, // create new object of a given type
|
||||
STR, // stringify
|
||||
HIDDENREF, // hidden reference that has no dependency check
|
||||
|
||||
// Aggregates
|
||||
AGGREGATES,
|
||||
@@ -345,9 +346,7 @@ protected:
|
||||
virtual Py::Object _getPyValue() const override;
|
||||
virtual void _toString(std::ostream &ss, bool persistent, int indent) const override;
|
||||
virtual bool _isIndexable() const override;
|
||||
virtual void _getDeps(ExpressionDeps &) const override;
|
||||
virtual void _getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *) const override;
|
||||
virtual void _getIdentifiers(std::set<App::ObjectIdentifier> &) const override;
|
||||
virtual void _getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const override;
|
||||
virtual bool _adjustLinks(const std::set<App::DocumentObject*> &, ExpressionVisitor &) override;
|
||||
virtual void _importSubNames(const ObjectIdentifier::SubNameMap &) override;
|
||||
virtual void _updateLabelReference(App::DocumentObject *, const std::string &, const char *) override;
|
||||
@@ -435,7 +434,7 @@ protected:
|
||||
virtual Expression * _copy() const override;
|
||||
virtual void _toString(std::ostream &ss, bool persistent, int indent) const override;
|
||||
virtual Py::Object _getPyValue() const override;
|
||||
virtual void _getDeps(ExpressionDeps &) const override;
|
||||
virtual void _getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const override;
|
||||
virtual bool _renameObjectIdentifier(const std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const ObjectIdentifier &, ExpressionVisitor &) override;
|
||||
virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &) override;
|
||||
|
||||
@@ -33,18 +33,19 @@
|
||||
|
||||
/// Here the FreeCAD includes sorted by Base,App,Gui......
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include <App/ComplexGeoData.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/QuantityPy.h>
|
||||
#include <Base/Console.h>
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include "ComplexGeoData.h"
|
||||
#include "Property.h"
|
||||
#include "Application.h"
|
||||
#include "Document.h"
|
||||
#include "DocumentObject.h"
|
||||
#include "ObjectIdentifier.h"
|
||||
#include "ExpressionParser.h"
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/QuantityPy.h>
|
||||
#include <App/Link.h>
|
||||
#include <Base/Console.h>
|
||||
#include "Link.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("Expression",true,true)
|
||||
|
||||
@@ -1086,32 +1087,58 @@ enum PseudoPropertyType {
|
||||
PseudoCadquery,
|
||||
};
|
||||
|
||||
std::pair<DocumentObject*,std::string> ObjectIdentifier::getDep(std::vector<std::string> *labels) const {
|
||||
void ObjectIdentifier::getDepLabels(std::vector<std::string> &labels) const {
|
||||
getDepLabels(ResolveResults(*this),labels);
|
||||
}
|
||||
|
||||
void ObjectIdentifier::getDepLabels(
|
||||
const ResolveResults &result, std::vector<std::string> &labels) const
|
||||
{
|
||||
if(documentObjectName.getString().size()) {
|
||||
if(documentObjectName.isRealString())
|
||||
labels.push_back(documentObjectName.getString());
|
||||
} else if(result.propertyIndex == 1)
|
||||
labels.push_back(components[0].name.getString());
|
||||
if(subObjectName.getString().size())
|
||||
PropertyLinkBase::getLabelReferences(labels,subObjectName.getString().c_str());
|
||||
}
|
||||
|
||||
ObjectIdentifier::Dependencies
|
||||
ObjectIdentifier::getDep(bool needProps, std::vector<std::string> *labels) const
|
||||
{
|
||||
Dependencies deps;
|
||||
getDep(deps,needProps,labels);
|
||||
return deps;
|
||||
}
|
||||
|
||||
void ObjectIdentifier::getDep(
|
||||
Dependencies &deps, bool needProps, std::vector<std::string> *labels) const
|
||||
{
|
||||
ResolveResults result(*this);
|
||||
if(labels) {
|
||||
if(documentObjectName.getString().size()) {
|
||||
if(documentObjectName.isRealString())
|
||||
labels->push_back(documentObjectName.getString());
|
||||
} else if(result.propertyIndex == 1)
|
||||
labels->push_back(components[0].name.getString());
|
||||
if(subObjectName.getString().size())
|
||||
PropertyLinkBase::getLabelReferences(*labels,subObjectName.getString().c_str());
|
||||
if(labels)
|
||||
getDepLabels(result,*labels);
|
||||
|
||||
if(!result.resolvedDocumentObject)
|
||||
return;
|
||||
|
||||
if(!needProps) {
|
||||
deps[result.resolvedDocumentObject];
|
||||
return;
|
||||
}
|
||||
if(subObjectName.getString().empty()) {
|
||||
if(result.propertyType==PseudoNone) {
|
||||
CellAddress addr;
|
||||
if(addr.parseAbsoluteAddress(result.propertyName.c_str()))
|
||||
return std::make_pair(result.resolvedDocumentObject,addr.toString(true));
|
||||
return std::make_pair(result.resolvedDocumentObject,result.propertyName);
|
||||
}else if(result.propertyType == PseudoSelf
|
||||
&& result.resolvedDocumentObject
|
||||
&& result.propertyIndex+1 < (int)components.size())
|
||||
{
|
||||
return std::make_pair(result.resolvedDocumentObject,
|
||||
components[result.propertyIndex+1].getName());
|
||||
}
|
||||
|
||||
if(!result.resolvedProperty) {
|
||||
if(result.propertyName.size())
|
||||
deps[result.resolvedDocumentObject].insert(result.propertyName);
|
||||
return;
|
||||
}
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
access(result,0,&deps);
|
||||
}catch(Py::Exception &) {
|
||||
Base::PyException e;
|
||||
}catch(...){
|
||||
}
|
||||
return std::make_pair(result.resolvedDocumentObject,std::string());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1241,7 +1268,7 @@ Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj,
|
||||
if(!obj)
|
||||
return 0;
|
||||
|
||||
static std::map<std::string,int> _props = {
|
||||
static std::unordered_map<const char*,int,CStringHasher,CStringHasher> _props = {
|
||||
{"_shape",PseudoShape},
|
||||
{"_pla",PseudoPlacement},
|
||||
{"_matrix",PseudoMatrix},
|
||||
@@ -1270,23 +1297,8 @@ Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj,
|
||||
}
|
||||
return &const_cast<App::DocumentObject*>(obj)->Label; //fake the property
|
||||
}
|
||||
|
||||
auto prop = obj->getPropertyByName(propertyName);
|
||||
if(prop && !prop->testStatus(Property::Hidden) && !(prop->getType() & PropertyType::Prop_Hidden))
|
||||
return prop;
|
||||
|
||||
auto linked = obj->getLinkedObject(true);
|
||||
if(!linked || linked==obj) {
|
||||
auto ext = obj->getExtensionByType<App::LinkBaseExtension>(true);
|
||||
if(!ext)
|
||||
return prop;
|
||||
linked = ext->getTrueLinkedObject(true);
|
||||
if(!linked || linked==obj)
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto linkedProp = linked->getPropertyByName(propertyName);
|
||||
return linkedProp?linkedProp:prop;
|
||||
|
||||
return obj->getPropertyByName(propertyName);
|
||||
}
|
||||
|
||||
|
||||
@@ -1503,7 +1515,8 @@ void ObjectIdentifier::String::checkImport(const App::DocumentObject *owner,
|
||||
}
|
||||
}
|
||||
|
||||
Py::Object ObjectIdentifier::access(const ResolveResults &result, Py::Object *value) const
|
||||
Py::Object ObjectIdentifier::access(const ResolveResults &result,
|
||||
Py::Object *value, Dependencies *deps) const
|
||||
{
|
||||
if(!result.resolvedDocumentObject || !result.resolvedProperty ||
|
||||
(subObjectName.getString().size() && !result.resolvedSubObject))
|
||||
@@ -1647,13 +1660,65 @@ Py::Object ObjectIdentifier::access(const ResolveResults &result, Py::Object *va
|
||||
break;
|
||||
}}}
|
||||
}
|
||||
|
||||
auto setPropDep = [deps](DocumentObject *obj, Property *prop, const char *propName) {
|
||||
if(!deps || !obj)
|
||||
return;
|
||||
if(prop && prop->getContainer()!=obj) {
|
||||
auto linkTouched = Base::freecad_dynamic_cast<PropertyBool>(
|
||||
obj->getPropertyByName("_LinkTouched"));
|
||||
if(linkTouched)
|
||||
propName = linkTouched->getName();
|
||||
else {
|
||||
auto propOwner = Base::freecad_dynamic_cast<DocumentObject>(prop->getContainer());
|
||||
if(propOwner)
|
||||
obj = propOwner;
|
||||
else
|
||||
propName = 0;
|
||||
}
|
||||
}
|
||||
auto &propset = (*deps)[obj];
|
||||
// inserting a null name in the propset indicates the dependency is
|
||||
// on all properties of the corresponding object.
|
||||
if(propset.size()!=1 || propset.begin()->size()) {
|
||||
if(!propName)
|
||||
propset.clear();
|
||||
propset.insert(propName);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
App::DocumentObject *lastObj = result.resolvedDocumentObject;
|
||||
if(result.resolvedSubObject) {
|
||||
setPropDep(lastObj,0,0);
|
||||
lastObj = result.resolvedSubObject;
|
||||
}
|
||||
if(ptype == PseudoNone)
|
||||
setPropDep(lastObj, result.resolvedProperty, result.resolvedProperty->getName());
|
||||
else
|
||||
setPropDep(lastObj,0,0);
|
||||
lastObj = 0;
|
||||
|
||||
if(components.empty())
|
||||
return pyobj;
|
||||
|
||||
size_t count = components.size();
|
||||
if(value) --count;
|
||||
assert(idx<=count);
|
||||
for(;idx<count;++idx)
|
||||
|
||||
for(;idx<count;++idx) {
|
||||
if(PyObject_TypeCheck(*pyobj, &DocumentObjectPy::Type))
|
||||
lastObj = static_cast<DocumentObjectPy*>(*pyobj)->getDocumentObjectPtr();
|
||||
else if(lastObj) {
|
||||
const char *attr = components[idx].getName().c_str();
|
||||
auto prop = lastObj->getPropertyByName(attr);
|
||||
if(!prop && pyobj.hasAttr(attr))
|
||||
attr = 0;
|
||||
setPropDep(lastObj,prop,attr);
|
||||
lastObj = 0;
|
||||
}
|
||||
pyobj = components[idx].get(pyobj);
|
||||
}
|
||||
if(value) {
|
||||
components[idx].set(pyobj,*value);
|
||||
return Py::Object();
|
||||
|
||||
@@ -342,7 +342,45 @@ public:
|
||||
|
||||
bool relabeledDocument(ExpressionVisitor &v, const std::string &oldLabel, const std::string &newLabel);
|
||||
|
||||
std::pair<App::DocumentObject*,std::string> getDep(std::vector<std::string> *labels=0) const;
|
||||
/** Type for storing dependency of an ObjectIdentifier
|
||||
*
|
||||
* The dependency is a map from document object to a set of property names.
|
||||
* An object identifier may references multiple objects using syntax like
|
||||
* 'Part.Group[0].Width'.
|
||||
*
|
||||
* Also, we use set of string instead of set of Property pointer, because
|
||||
* the property may not exist at the time this ObjectIdentifier is
|
||||
* constructed.
|
||||
*/
|
||||
typedef std::map<App::DocumentObject *, std::set<std::string> > Dependencies;
|
||||
|
||||
/** Get dependencies of this object identifier
|
||||
*
|
||||
* @param needProps: whether need property dependencies.
|
||||
* @param labels: optional return of any label references.
|
||||
*
|
||||
* In case of multi-object references, like 'Part.Group[0].Width', if no
|
||||
* property dependency is required, then this function will only return the
|
||||
* first referred object dependency. Or else, all object and property
|
||||
* dependencies will be returned.
|
||||
*/
|
||||
Dependencies getDep(bool needProps, std::vector<std::string> *labels=0) const;
|
||||
|
||||
/** Get dependencies of this object identifier
|
||||
*
|
||||
* @param deps: returns the depdenencies.
|
||||
* @param needProps: whether need property dependencies.
|
||||
* @param labels: optional return of any label references.
|
||||
*
|
||||
* In case of multi-object references, like 'Part.Group[0].Width', if no
|
||||
* property dependency is required, then this function will only return the
|
||||
* first referred object dependency. Or else, all object and property
|
||||
* dependencies will be returned.
|
||||
*/
|
||||
void getDep(Dependencies &deps, bool needProps, std::vector<std::string> *labels=0) const;
|
||||
|
||||
/// Returns all label references
|
||||
void getDepLabels(std::vector<std::string> &labels) const;
|
||||
|
||||
App::Document *getDocument(String name = String(), bool *ambiguous=0) const;
|
||||
|
||||
@@ -422,7 +460,8 @@ protected:
|
||||
|
||||
void getSubPathStr(std::ostream &ss, const ResolveResults &result, bool toPython=false) const;
|
||||
|
||||
Py::Object access(const ResolveResults &rs, Py::Object *value=0) const;
|
||||
Py::Object access(const ResolveResults &rs,
|
||||
Py::Object *value=0, Dependencies *deps=0) const;
|
||||
|
||||
void resolve(ResolveResults & results) const;
|
||||
void resolveAmbiguity(ResolveResults &results);
|
||||
@@ -430,6 +469,8 @@ protected:
|
||||
static App::DocumentObject *getDocumentObject(
|
||||
const App::Document *doc, const String &name, std::bitset<32> &flags);
|
||||
|
||||
void getDepLabels(const ResolveResults &result, std::vector<std::string> &labels) const;
|
||||
|
||||
App::DocumentObject * owner;
|
||||
String documentName;
|
||||
String documentObjectName;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
|
||||
FC_LOG_LEVEL_INIT("App",true);
|
||||
|
||||
using namespace App;
|
||||
using namespace Base;
|
||||
@@ -72,6 +73,15 @@ void PropertyExpressionContainer::slotRelabelDocument(const App::Document &doc)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct PropertyExpressionEngine::Private {
|
||||
// For some reason, MSVC has trouble with vector of scoped_connection if
|
||||
// defined in header, hence the private structure here.
|
||||
std::vector<boost::signals2::scoped_connection> conns;
|
||||
std::unordered_map<std::string, std::vector<ObjectIdentifier> > propMap;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TYPESYSTEM_SOURCE(App::PropertyExpressionEngine , App::PropertyExpressionContainer)
|
||||
|
||||
/**
|
||||
@@ -125,7 +135,7 @@ void PropertyExpressionEngine::hasSetValue()
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<App::DocumentObject*> deps;
|
||||
std::map<App::DocumentObject*,bool> deps;
|
||||
std::vector<std::string> labels;
|
||||
unregisterElementReference();
|
||||
UpdateElementReferenceExpressionVisitor<PropertyExpressionEngine> v(*this);
|
||||
@@ -141,9 +151,95 @@ void PropertyExpressionEngine::hasSetValue()
|
||||
|
||||
updateDeps(std::move(deps));
|
||||
|
||||
if(pimpl) {
|
||||
pimpl->conns.clear();
|
||||
pimpl->propMap.clear();
|
||||
}
|
||||
// check if there is any hidden references
|
||||
bool hasHidden = false;
|
||||
for(auto &v : _Deps) {
|
||||
if(v.second) {
|
||||
hasHidden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hasHidden) {
|
||||
if(!pimpl)
|
||||
pimpl.reset(new Private);
|
||||
for(auto &e : expressions) {
|
||||
auto expr = e.second.expression;
|
||||
if(!expr) continue;
|
||||
for(auto &dep : expr->getIdentifiers()) {
|
||||
if(!dep.second)
|
||||
continue;
|
||||
const ObjectIdentifier &var = dep.first;
|
||||
for(auto &vdep : var.getDep(true)) {
|
||||
auto obj = vdep.first;
|
||||
auto objName = obj->getFullName() + ".";
|
||||
for(auto &propName : vdep.second) {
|
||||
std::string key = objName + propName;
|
||||
auto &propDeps = pimpl->propMap[key];
|
||||
if(propDeps.empty()) {
|
||||
if(propName.size())
|
||||
pimpl->conns.push_back(obj->signalChanged.connect(boost::bind(
|
||||
&PropertyExpressionEngine::slotChangedProperty,this,_1,_2)));
|
||||
else
|
||||
pimpl->conns.push_back(obj->signalChanged.connect(boost::bind(
|
||||
&PropertyExpressionEngine::slotChangedObject,this,_1,_2)));
|
||||
}
|
||||
propDeps.push_back(e.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyExpressionContainer::hasSetValue();
|
||||
}
|
||||
|
||||
void PropertyExpressionEngine::updateHiddenReference(const std::string &key) {
|
||||
if(!pimpl)
|
||||
return;
|
||||
auto it = pimpl->propMap.find(key);
|
||||
if(it == pimpl->propMap.end())
|
||||
return;
|
||||
for(auto &var : it->second) {
|
||||
auto it = expressions.find(var);
|
||||
if(it == expressions.end() || it->second.busy)
|
||||
continue;
|
||||
Property *myProp = var.getProperty();
|
||||
if(!myProp)
|
||||
continue;
|
||||
Base::StateLocker guard(it->second.busy);
|
||||
App::any value;
|
||||
try {
|
||||
value = it->second.expression->getValueAsAny();
|
||||
if(!isAnyEqual(value, myProp->getPathValue(var)))
|
||||
myProp->setPathValue(var, value);
|
||||
}catch(Base::Exception &e) {
|
||||
e.ReportException();
|
||||
FC_ERR("Failed to evaluate property binding "
|
||||
<< myProp->getFullName() << " on change of " << key);
|
||||
}catch(std::bad_cast &) {
|
||||
FC_ERR("Invalid type '" << value.type().name()
|
||||
<< "' in property binding " << myProp->getFullName()
|
||||
<< " on change of " << key);
|
||||
}catch(std::exception &e) {
|
||||
FC_ERR(e.what());
|
||||
FC_ERR("Failed to evaluate property binding "
|
||||
<< myProp->getFullName() << " on change of " << key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyExpressionEngine::slotChangedObject(const App::DocumentObject &obj, const App::Property &) {
|
||||
updateHiddenReference(obj.getFullName());
|
||||
}
|
||||
|
||||
void PropertyExpressionEngine::slotChangedProperty(const App::DocumentObject &, const App::Property &prop) {
|
||||
updateHiddenReference(prop.getFullName());
|
||||
}
|
||||
|
||||
void PropertyExpressionEngine::Paste(const Property &from)
|
||||
{
|
||||
const PropertyExpressionEngine &fromee = dynamic_cast<const PropertyExpressionEngine&>(from);
|
||||
@@ -562,17 +658,17 @@ DocumentObjectExecReturn *App::PropertyExpressionEngine::execute(ExecuteOption o
|
||||
prop->setPathValue(*it, value);
|
||||
}catch(Base::Exception &e) {
|
||||
std::ostringstream ss;
|
||||
ss << e.what() << std::endl << "in property binding '" << prop->getName() << "'";
|
||||
ss << e.what() << std::endl << "in property binding '" << prop->getFullName() << "'";
|
||||
e.setMessage(ss.str());
|
||||
throw;
|
||||
}catch(std::bad_cast &) {
|
||||
std::ostringstream ss;
|
||||
ss << "Invalid type '" << value.type().name() << "'";
|
||||
ss << "\nin property binding '" << prop->getName() << "'";
|
||||
ss << "\nin property binding '" << prop->getFullName() << "'";
|
||||
throw Base::TypeError(ss.str().c_str());
|
||||
}catch(std::exception &e) {
|
||||
std::ostringstream ss;
|
||||
ss << e.what() << "\nin property binding '" << prop->getName() << "'";
|
||||
ss << e.what() << "\nin property binding '" << prop->getFullName() << "'";
|
||||
throw Base::RuntimeError(ss.str().c_str());
|
||||
}
|
||||
}
|
||||
@@ -610,9 +706,11 @@ void PropertyExpressionEngine::getPathsToDocumentObject(DocumentObject* obj,
|
||||
|
||||
bool PropertyExpressionEngine::depsAreTouched() const
|
||||
{
|
||||
for(auto obj : _Deps)
|
||||
if(obj->isTouched())
|
||||
for(auto &v : _Deps) {
|
||||
// v.second inidcates if it is a hidden reference
|
||||
if(!v.second && v.first->isTouched())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -639,8 +737,9 @@ std::string PropertyExpressionEngine::validateExpression(const ObjectIdentifier
|
||||
assert(pathDocObj);
|
||||
|
||||
auto inList = pathDocObj->getInListEx(true);
|
||||
for(auto docObj : expr->getDepObjects()) {
|
||||
if(inList.count(docObj)) {
|
||||
for(auto &v : expr->getDepObjects()) {
|
||||
auto docObj = v.first;
|
||||
if(!v.second && inList.count(docObj)) {
|
||||
std::stringstream ss;
|
||||
ss << "cyclic reference to " << docObj->getFullName();
|
||||
return ss.str();
|
||||
@@ -765,8 +864,8 @@ bool PropertyExpressionEngine::adjustLink(const std::set<DocumentObject*> &inLis
|
||||
if(!owner)
|
||||
return false;
|
||||
bool found = false;
|
||||
for(auto obj : _Deps) {
|
||||
if(inList.count(obj)) {
|
||||
for(auto &v : _Deps) {
|
||||
if(inList.count(v.first)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -85,9 +85,11 @@ public:
|
||||
|
||||
struct ExpressionInfo {
|
||||
std::shared_ptr<App::Expression> expression; /**< The actual expression tree */
|
||||
bool busy;
|
||||
|
||||
ExpressionInfo(std::shared_ptr<App::Expression> expression = std::shared_ptr<App::Expression>()) {
|
||||
this->expression = expression;
|
||||
this->busy = false;
|
||||
}
|
||||
|
||||
ExpressionInfo(const ExpressionInfo & other) {
|
||||
@@ -186,6 +188,10 @@ private:
|
||||
boost::unordered_map<int, App::ObjectIdentifier> &revNodes,
|
||||
DiGraph &g, ExecuteOption option=ExecuteAll) const;
|
||||
|
||||
void slotChangedObject(const App::DocumentObject &obj, const App::Property &prop);
|
||||
void slotChangedProperty(const App::DocumentObject &obj, const App::Property &prop);
|
||||
void updateHiddenReference(const std::string &key);
|
||||
|
||||
bool running; /**< Boolean used to avoid loops */
|
||||
bool restoring = false;
|
||||
|
||||
@@ -201,6 +207,9 @@ private:
|
||||
/**< Expressions are read from file to this map first before they are validated and inserted into the actual map */
|
||||
std::unique_ptr<std::vector<RestoredExpression> > restoredExpressions;
|
||||
|
||||
struct Private;
|
||||
std::unique_ptr<Private> pimpl;
|
||||
|
||||
friend class AtomicPropertyChange;
|
||||
|
||||
};
|
||||
|
||||
@@ -4483,7 +4483,7 @@ void PropertyXLinkContainer::afterRestore() {
|
||||
if(info.docLabel != obj->getDocument()->Label.getValue())
|
||||
_DocMap[App::quote(info.docLabel)] = obj->getDocument()->Label.getValue();
|
||||
}
|
||||
if(_Deps.insert(obj).second)
|
||||
if(_Deps.insert(std::make_pair(obj,info.xlink->getScope()==LinkScope::Hidden)).second)
|
||||
_XLinks[obj->getFullName()] = std::move(info.xlink);
|
||||
}
|
||||
_XLinkRestores.reset();
|
||||
@@ -4496,24 +4496,27 @@ void PropertyXLinkContainer::breakLink(App::DocumentObject *obj, bool clear) {
|
||||
if(!owner || !owner->getNameInDocument())
|
||||
return;
|
||||
if(!clear || obj!=owner) {
|
||||
if(!_Deps.erase(obj))
|
||||
auto it = _Deps.find(obj);
|
||||
if(it == _Deps.end())
|
||||
return;
|
||||
aboutToSetValue();
|
||||
onBreakLink(obj);
|
||||
if(obj->getDocument() == owner->getDocument())
|
||||
obj->_removeBackLink(owner);
|
||||
else
|
||||
if (obj->getDocument() != owner->getDocument())
|
||||
_XLinks.erase(obj->getFullName());
|
||||
else if (!it->second)
|
||||
obj->_removeBackLink(owner);
|
||||
_Deps.erase(it);
|
||||
hasSetValue();
|
||||
return;
|
||||
}
|
||||
if(obj!=owner)
|
||||
return;
|
||||
for(auto obj : _Deps) {
|
||||
for(auto &v : _Deps) {
|
||||
auto obj = v.first;
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
continue;
|
||||
onBreakLink(obj);
|
||||
if(obj->getDocument()==owner->getDocument())
|
||||
if(!v.second && obj->getDocument()==owner->getDocument())
|
||||
obj->_removeBackLink(owner);
|
||||
}
|
||||
_XLinks.clear();
|
||||
@@ -4553,6 +4556,19 @@ void PropertyXLinkContainer::Save (Base::Writer &writer) const {
|
||||
writer.Stream() << "\" docs=\"" << docSet.size();
|
||||
}
|
||||
|
||||
std::ostringstream ss;
|
||||
int hidden = 0;
|
||||
int i=-1;
|
||||
for(auto &v : _XLinks) {
|
||||
++i;
|
||||
if(v.second->getScope() == LinkScope::Hidden) {
|
||||
ss << i << ' ';
|
||||
++hidden;
|
||||
}
|
||||
}
|
||||
if(hidden)
|
||||
writer.Stream() << "\" hidden=\"" << ss.str();
|
||||
|
||||
writer.Stream() << "\">" << std::endl;
|
||||
writer.incInd();
|
||||
|
||||
@@ -4575,6 +4591,15 @@ void PropertyXLinkContainer::Restore(Base::XMLReader &reader) {
|
||||
auto count = reader.getAttributeAsUnsigned("count");
|
||||
_XLinkRestores.reset(new std::vector<RestoreInfo>(count));
|
||||
|
||||
if(reader.hasAttribute("hidden")) {
|
||||
std::istringstream iss(reader.getAttribute("hidden"));
|
||||
int index;
|
||||
while(iss >> index) {
|
||||
if(index>=0 && index<static_cast<int>(count))
|
||||
_XLinkRestores->at(index).hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(reader.hasAttribute("docs")) {
|
||||
auto docCount = reader.getAttributeAsUnsigned("docs");
|
||||
_DocMap.clear();
|
||||
@@ -4593,6 +4618,8 @@ void PropertyXLinkContainer::Restore(Base::XMLReader &reader) {
|
||||
|
||||
for(auto &info : *_XLinkRestores) {
|
||||
info.xlink.reset(createXLink());
|
||||
if(info.hidden)
|
||||
info.xlink->setScope(LinkScope::Hidden);
|
||||
info.xlink->Restore(reader);
|
||||
}
|
||||
reader.readEndElement("XLinks");
|
||||
@@ -4624,16 +4651,23 @@ bool PropertyXLinkContainer::isLinkedToDocument(const App::Document &doc) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void PropertyXLinkContainer::updateDeps(std::set<DocumentObject*> &&newDeps) {
|
||||
void PropertyXLinkContainer::updateDeps(std::map<DocumentObject*,bool> &&newDeps) {
|
||||
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
|
||||
if(!owner || !owner->getNameInDocument())
|
||||
return;
|
||||
newDeps.erase(owner);
|
||||
|
||||
for(auto obj : newDeps) {
|
||||
for(auto &v : newDeps) {
|
||||
auto obj = v.first;
|
||||
if(obj && obj->getNameInDocument()) {
|
||||
auto it = _Deps.find(obj);
|
||||
if(it != _Deps.end()) {
|
||||
if(v.second != it->second) {
|
||||
if(v.second)
|
||||
obj->_removeBackLink(owner);
|
||||
else
|
||||
obj->_addBackLink(owner);
|
||||
}
|
||||
_Deps.erase(it);
|
||||
continue;
|
||||
}
|
||||
@@ -4643,21 +4677,21 @@ void PropertyXLinkContainer::updateDeps(std::set<DocumentObject*> &&newDeps) {
|
||||
xlink.reset(createXLink());
|
||||
xlink->setValue(obj);
|
||||
}
|
||||
xlink->setScope(v.second?LinkScope::Hidden:LinkScope::Global);
|
||||
}
|
||||
#ifndef USE_OLD_DAG
|
||||
else
|
||||
else if(!v.second)
|
||||
obj->_addBackLink(owner);
|
||||
#endif
|
||||
|
||||
onAddDep(obj);
|
||||
}
|
||||
}
|
||||
for(auto obj : _Deps) {
|
||||
for(auto &v : _Deps) {
|
||||
auto obj = v.first;
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
continue;
|
||||
if(obj->getDocument()==owner->getDocument()) {
|
||||
#ifndef USE_OLD_DAG
|
||||
obj->_removeBackLink(owner);
|
||||
#endif
|
||||
if(!v.second)
|
||||
obj->_removeBackLink(owner);
|
||||
}else
|
||||
_XLinks.erase(obj->getFullName());
|
||||
onRemoveDep(obj);
|
||||
@@ -4681,8 +4715,9 @@ void PropertyXLinkContainer::clearDeps() {
|
||||
return;
|
||||
#ifndef USE_OLD_DAG
|
||||
if (!owner->testStatus(ObjectStatus::Destroy)) {
|
||||
for(auto obj : _Deps) {
|
||||
if(obj && obj->getNameInDocument() && obj->getDocument()==owner->getDocument())
|
||||
for(auto &v : _Deps) {
|
||||
auto obj = v.first;
|
||||
if(!v.second && obj && obj->getNameInDocument() && obj->getDocument()==owner->getDocument())
|
||||
obj->_removeBackLink(owner);
|
||||
}
|
||||
}
|
||||
@@ -4692,8 +4727,11 @@ void PropertyXLinkContainer::clearDeps() {
|
||||
_LinkRestored = false;
|
||||
}
|
||||
|
||||
void PropertyXLinkContainer::getLinks(std::vector<App::DocumentObject *> &objs,
|
||||
bool, std::vector<std::string> * /*subs*/, bool /*newStyle*/) const
|
||||
void PropertyXLinkContainer::getLinks(std::vector<App::DocumentObject *> &objs,
|
||||
bool all, std::vector<std::string> * /*subs*/, bool /*newStyle*/) const
|
||||
{
|
||||
objs.insert(objs.end(),_Deps.begin(),_Deps.end());
|
||||
for(auto &v : _Deps) {
|
||||
if(all || !v.second)
|
||||
objs.push_back(v.first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1339,11 +1339,11 @@ protected:
|
||||
virtual void onBreakLink(App::DocumentObject *obj);
|
||||
virtual void onAddDep(App::DocumentObject *) {}
|
||||
virtual void onRemoveDep(App::DocumentObject *) {}
|
||||
void updateDeps(std::set<DocumentObject*> &&newDeps);
|
||||
void updateDeps(std::map<DocumentObject*,bool> &&newDeps);
|
||||
void clearDeps();
|
||||
|
||||
protected:
|
||||
std::set<App::DocumentObject*> _Deps;
|
||||
std::map<App::DocumentObject*,bool> _Deps;
|
||||
std::map<std::string, std::unique_ptr<PropertyXLink> > _XLinks;
|
||||
std::map<std::string, std::string> _DocMap;
|
||||
bool _LinkRestored;
|
||||
@@ -1353,6 +1353,7 @@ private:
|
||||
std::unique_ptr<PropertyXLink> xlink;
|
||||
std::string docName;
|
||||
std::string docLabel;
|
||||
bool hidden=false;
|
||||
};
|
||||
std::unique_ptr<std::vector<RestoreInfo> > _XLinkRestores;
|
||||
};
|
||||
|
||||
@@ -1023,38 +1023,39 @@ void PropertySheet::addDependencies(CellAddress key)
|
||||
if (expression == 0)
|
||||
return;
|
||||
|
||||
for(auto &dep : expression->getDeps()) {
|
||||
for(auto &var : expression->getIdentifiers()) {
|
||||
for(auto &dep : var.first.getDep(true)) {
|
||||
App::DocumentObject *docObj = dep.first;
|
||||
App::Document *doc = docObj->getDocument();
|
||||
|
||||
App::DocumentObject *docObj = dep.first;
|
||||
App::Document *doc = docObj->getDocument();
|
||||
std::string docObjName = docObj->getFullName();
|
||||
|
||||
std::string docObjName = docObj->getFullName();
|
||||
owner->observeDocument(doc);
|
||||
|
||||
owner->observeDocument(doc);
|
||||
documentObjectToCellMap[docObjName].insert(key);
|
||||
cellToDocumentObjectMap[key].insert(docObjName);
|
||||
++updateCount;
|
||||
|
||||
documentObjectToCellMap[docObjName].insert(key);
|
||||
cellToDocumentObjectMap[key].insert(docObjName);
|
||||
++updateCount;
|
||||
for(auto &name : dep.second) {
|
||||
std::string propName = docObjName + "." + name;
|
||||
FC_LOG("dep " << key.toString() << " -> " << name);
|
||||
|
||||
for(auto &props : dep.second) {
|
||||
std::string propName = docObjName + "." + props.first;
|
||||
FC_LOG("dep " << key.toString() << " -> " << propName);
|
||||
// Insert into maps
|
||||
propertyNameToCellMap[propName].insert(key);
|
||||
cellToPropertyNameMap[key].insert(propName);
|
||||
|
||||
// Insert into maps
|
||||
propertyNameToCellMap[propName].insert(key);
|
||||
cellToPropertyNameMap[key].insert(propName);
|
||||
// Also an alias?
|
||||
if (docObj==owner && name.size()) {
|
||||
auto j = revAliasProp.find(name);
|
||||
|
||||
// Also an alias?
|
||||
if (docObj==owner && props.first.size()) {
|
||||
std::map<std::string, CellAddress>::const_iterator j = revAliasProp.find(props.first);
|
||||
if (j != revAliasProp.end()) {
|
||||
propName = docObjName + "." + j->second.toString();
|
||||
FC_LOG("dep " << key.toString() << " -> " << propName);
|
||||
|
||||
if (j != revAliasProp.end()) {
|
||||
propName = docObjName + "." + j->second.toString();
|
||||
FC_LOG("dep " << key.toString() << " -> " << propName);
|
||||
|
||||
// Insert into maps
|
||||
propertyNameToCellMap[propName].insert(key);
|
||||
cellToPropertyNameMap[key].insert(propName);
|
||||
// Insert into maps
|
||||
propertyNameToCellMap[propName].insert(key);
|
||||
cellToPropertyNameMap[key].insert(propName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1123,6 +1124,16 @@ void PropertySheet::removeDependencies(CellAddress key)
|
||||
|
||||
void PropertySheet::recomputeDependants(const App::DocumentObject *owner, const char *propName)
|
||||
{
|
||||
auto itD = _Deps.find(const_cast<App::DocumentObject*>(owner));
|
||||
if(itD!=_Deps.end() && itD->second) {
|
||||
// Check for hidden reference. Because a hidden reference is not
|
||||
// protected by cyclic dependency checking, we need to take special
|
||||
// care to prevent it from misbehave.
|
||||
Sheet *sheet = Base::freecad_dynamic_cast<Sheet>(getContainer());
|
||||
if(!sheet || sheet->testStatus(App::ObjectStatus::Recompute2))
|
||||
return;
|
||||
}
|
||||
|
||||
// First, search without actual property name for sub-object/link
|
||||
// references, i.e indirect references. The dependencies of these
|
||||
// references are too complex to track exactly, so we only track the
|
||||
@@ -1135,7 +1146,7 @@ void PropertySheet::recomputeDependants(const App::DocumentObject *owner, const
|
||||
setDirty(cell);
|
||||
}
|
||||
|
||||
if (propName) {
|
||||
if (propName && *propName) {
|
||||
// Now, we check for direct property references
|
||||
it = propertyNameToCellMap.find(fullName + propName);
|
||||
if (it != propertyNameToCellMap.end()) {
|
||||
@@ -1302,7 +1313,7 @@ void PropertySheet::hasSetValue()
|
||||
|
||||
updateCount = 0;
|
||||
|
||||
std::set<App::DocumentObject*> deps;
|
||||
std::map<App::DocumentObject*,bool> deps;
|
||||
std::vector<std::string> labels;
|
||||
unregisterElementReference();
|
||||
UpdateElementReferenceExpressionVisitor<PropertySheet> v(*this);
|
||||
@@ -1384,8 +1395,9 @@ bool PropertySheet::adjustLink(const std::set<DocumentObject*> &inList) {
|
||||
continue;
|
||||
try {
|
||||
bool need_adjust = false;
|
||||
for(auto docObj : expr->getDepObjects()) {
|
||||
if (docObj && docObj != owner && inList.count(docObj)) {
|
||||
for(auto &v : expr->getDepObjects()) {
|
||||
auto docObj = v.first;
|
||||
if (v.second && docObj && docObj!=owner && inList.count(docObj)) {
|
||||
need_adjust = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user