1
/***************************************************************************
2
* Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de> *
3
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
5
* This file is part of the FreeCAD CAx development system. *
7
* This library is free software; you can redistribute it and/or *
8
* modify it under the terms of the GNU Library General Public *
9
* License as published by the Free Software Foundation; either *
10
* version 2 of the License, or (at your option) any later version. *
12
* This library is distributed in the hope that it will be useful, *
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
* GNU Library General Public License for more details. *
17
* You should have received a copy of the GNU Library General Public *
18
* License along with this library; see the file COPYING.LIB. If not, *
19
* write to the Free Software Foundation, Inc., 59 Temple Place, *
20
* Suite 330, Boston, MA 02111-1307, USA *
22
***************************************************************************/
25
#include "PreCompiled.h"
30
#include <App/DocumentObjectPy.h>
31
#include <Base/Console.h>
32
#include <Base/Matrix.h>
33
#include <Base/Tools.h>
34
#include <Base/Writer.h>
36
#include "Application.h"
37
#include "ElementNamingUtils.h"
39
#include "DocumentObject.h"
40
#include "DocumentObjectExtension.h"
41
#include "DocumentObjectGroup.h"
42
#include "GeoFeatureGroupExtension.h"
43
#include "ObjectIdentifier.h"
44
#include "PropertyExpressionEngine.h"
45
#include "PropertyLinks.h"
48
FC_LOG_LEVEL_INIT("App",true,true)
52
/** \defgroup DocObject Document Object
54
\brief Base class of all objects handled in the Document
57
PROPERTY_SOURCE(App::DocumentObject, App::TransactionalObject)
59
DocumentObjectExecReturn *DocumentObject::StdReturn = nullptr;
61
//===========================================================================
63
//===========================================================================
65
DocumentObject::DocumentObject()
68
// define Label of type 'Output' to avoid being marked as touched after relabeling
69
ADD_PROPERTY_TYPE(Label,("Unnamed"),"Base",Prop_Output,"User name of the object (UTF8)");
70
ADD_PROPERTY_TYPE(Label2,(""),"Base",Prop_Hidden,"User description of the object (UTF8)");
71
Label2.setStatus(App::Property::Output,true);
72
ADD_PROPERTY_TYPE(ExpressionEngine,(),"Base",Prop_Hidden,"Property expressions");
74
ADD_PROPERTY(Visibility, (true));
76
// default set Visibility status to hidden and output (no touch) for
77
// compatibitily reason. We use setStatus instead of PropertyType to
78
// allow user to change its status later
79
Visibility.setStatus(Property::Output,true);
80
Visibility.setStatus(Property::Hidden,true);
81
Visibility.setStatus(Property::NoModify,true);
84
DocumentObject::~DocumentObject()
86
if (!PythonObject.is(Py::_None())){
87
Base::PyGILStateLocker lock;
88
// Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed
89
// Python object or not. In the constructor we forced the wrapper to own the object so we need
90
// not to dec'ref the Python object any more.
91
// But we must still invalidate the Python object because it need not to be
92
// destructed right now because the interpreter can own several references to it.
93
Base::PyObjectBase* obj = static_cast<Base::PyObjectBase*>(PythonObject.ptr());
94
// Call before decrementing the reference counter, otherwise a heap error can occur
99
void DocumentObject::printInvalidLinks() const
102
// Get objects that have invalid link scope, and print their names.
103
// Truncate the invalid object list name strings for readability, if they happen to be very long.
104
std::vector<App::DocumentObject*> invalid_linkobjs;
105
std::string objnames, scopenames;
106
GeoFeatureGroupExtension::getInvalidLinkObjects(this, invalid_linkobjs);
107
for (auto& obj : invalid_linkobjs) {
108
objnames += obj->getNameInDocument();
110
for (auto& scope : obj->getParents()) {
111
if (scopenames.length() > 80) {
112
scopenames += "... ";
116
scopenames += scope.first->getNameInDocument();
120
if (objnames.length() > 80) {
126
if (objnames.empty()) {
133
if (scopenames.empty()) {
137
scopenames.pop_back();
140
Base::Console().Warning("%s: Link(s) to object(s) '%s' go out of the allowed scope '%s'. Instead, the linked object(s) reside within '%s'.\n",
141
getTypeId().getName(), objnames.c_str(), getNameInDocument(), scopenames.c_str());
143
catch (const Base::Exception& e) {
148
App::DocumentObjectExecReturn *DocumentObject::recompute()
150
//check if the links are valid before making the recompute
151
if (!GeoFeatureGroupExtension::areLinksValid(this)) {
155
// set/unset the execution bit
156
Base::ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::Recompute, this);
158
// mark the object to recompute its extensions
159
this->setStatus(App::RecomputeExtension, true);
161
auto ret = this->execute();
162
if (ret == StdReturn) {
163
// most feature classes don't call the execute() method of its base class
164
// so execute the extensions now
165
if (this->testStatus(App::RecomputeExtension)) {
166
ret = executeExtensions();
173
DocumentObjectExecReturn *DocumentObject::execute()
175
return executeExtensions();
178
App::DocumentObjectExecReturn* DocumentObject::executeExtensions()
180
//execute extensions but stop on error
181
this->setStatus(App::RecomputeExtension, false); // reset the flag
182
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
183
for(auto ext : vector) {
184
auto ret = ext->extensionExecute();
185
if (ret != StdReturn)
192
bool DocumentObject::recomputeFeature(bool recursive)
194
Document* doc = this->getDocument();
196
return doc->recomputeFeature(this,recursive);
201
* @brief Set this document object touched.
202
* Touching a document object does not mean to recompute it, it only means that
203
* other document objects that link it (i.e. its InList) will be recomputed.
204
* If it should be forced to recompute a document object then use
205
* \ref enforceRecompute() instead.
207
void DocumentObject::touch(bool noRecompute)
210
StatusBits.set(ObjectStatus::Enforce);
211
StatusBits.set(ObjectStatus::Touch);
213
_pDoc->signalTouchedObject(*this);
217
* @brief Set this document object freezed.
218
* A freezed document object does not recompute ever.
220
void DocumentObject::freeze()
222
StatusBits.set(ObjectStatus::Freeze);
223
// use the signalTouchedObject to refresh the Gui
225
_pDoc->signalTouchedObject(*this);
229
* @brief Set this document object unfreezed.
230
* A freezed document object does not recompute ever.
232
void DocumentObject::unfreeze(bool noRecompute)
234
StatusBits.set(ObjectStatus::Freeze, false);
239
* @brief Check whether the document object is touched or not.
240
* @return true if document object is touched, false if not.
242
bool DocumentObject::isTouched() const
244
return ExpressionEngine.isTouched() || StatusBits.test(ObjectStatus::Touch);
248
* @brief Enforces this document object to be recomputed.
249
* This can be useful to recompute the feature without
250
* having to change one of its input properties.
252
void DocumentObject::enforceRecompute()
258
* @brief Check whether the document object must be recomputed or not.
259
* This means that the 'Enforce' flag is set or that \ref mustExecute()
260
* returns a value > 0.
261
* @return true if document object must be recomputed, false if not.
263
bool DocumentObject::mustRecompute() const
265
if (StatusBits.test(ObjectStatus::Freeze))
268
if (StatusBits.test(ObjectStatus::Enforce))
271
return mustExecute() > 0;
274
short DocumentObject::mustExecute() const
276
if (ExpressionEngine.isTouched())
280
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
281
for(auto ext : vector) {
282
if (ext->extensionMustExecute())
289
const char* DocumentObject::getStatusString() const
292
const char* text = getDocument()->getErrorDescription(this);
293
return text ? text : "Error";
295
else if (isTouched())
301
std::string DocumentObject::getFullName() const {
302
if(!getDocument() || !isAttachedToDocument())
304
std::string name(getDocument()->getName());
306
name += *pcNameInDocument;
310
std::string DocumentObject::getFullLabel() const {
314
auto name = getDocument()->Label.getStrValue();
316
name += Label.getStrValue();
320
const char* DocumentObject::getDagKey() const
322
if(!pcNameInDocument)
326
return pcNameInDocument->c_str();
329
const char *DocumentObject::getNameInDocument() const
331
// Note: It can happen that we query the internal name of an object even if it is not
332
// part of a document (anymore). This is the case e.g. if we have a reference in Python
333
// to an object that has been removed from the document. In this case we should rather
335
//assert(pcNameInDocument);
336
if (!pcNameInDocument)
338
return pcNameInDocument->c_str();
341
int DocumentObject::isExporting() const {
342
if(!getDocument() || !isAttachedToDocument())
344
return getDocument()->isExporting(this);
347
std::string DocumentObject::getExportName(bool forced) const {
348
if(!isAttachedToDocument())
351
if(!forced && !isExporting())
352
return *pcNameInDocument;
354
// '@' is an invalid character for an internal name, which ensures the
355
// following returned name will be unique in any document. Saving external
356
// object like that shall only happens in Document::exportObjects(). We
357
// shall strip out this '@' and the following document name during restoring.
358
return *pcNameInDocument + '@' + getDocument()->getName();
361
bool DocumentObject::isAttachedToDocument() const
363
return (pcNameInDocument != nullptr);
366
const char* DocumentObject::detachFromDocument()
368
const std::string* name = pcNameInDocument;
369
pcNameInDocument = nullptr;
370
return name ? name->c_str() : nullptr;
373
const std::vector<DocumentObject*> &DocumentObject::getOutList() const {
374
if(!_outListCached) {
376
getOutList(0,_outList);
377
_outListCached = true;
382
std::vector<DocumentObject*> DocumentObject::getOutList(int options) const
384
std::vector<DocumentObject*> res;
385
getOutList(options,res);
389
void DocumentObject::getOutList(int options, std::vector<DocumentObject*> &res) const {
390
if(_outListCached && !options) {
391
res.insert(res.end(),_outList.begin(),_outList.end());
394
std::vector<Property*> props;
395
getPropertyList(props);
396
bool noHidden = !!(options & OutListNoHidden);
397
std::size_t size = res.size();
398
for(auto prop : props) {
399
auto link = dynamic_cast<PropertyLinkBase*>(prop);
401
link->getLinks(res,noHidden);
403
if(!(options & OutListNoExpression))
404
ExpressionEngine.getLinks(res);
406
if(options & OutListNoXLinked) {
407
for(auto it=res.begin()+size;it!=res.end();) {
409
if(obj && obj->getDocument()!=getDocument())
417
std::vector<App::DocumentObject*> DocumentObject::getOutListOfProperty(App::Property* prop) const
419
std::vector<DocumentObject*> ret;
420
if (!prop || prop->getContainer() != this)
423
auto link = dynamic_cast<PropertyLinkBase*>(prop);
430
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
433
return _pDoc->getInList(this);
435
return std::vector<App::DocumentObject*>();
438
#else // ifndef USE_OLD_DAG
440
const std::vector<App::DocumentObject*> &DocumentObject::getInList() const
445
#endif // if USE_OLD_DAG
448
// The original algorithm is highly inefficient in some special case.
449
// Considering an object is linked by every other objects. After excluding this
450
// object, there is another object linked by every other of the remaining
451
// objects, and so on. The vector 'result' above will be of magnitude n^2.
452
// Even if we replace the vector with a set, we still need to visit that amount
453
// of objects. And this may not be the worst case. getInListEx() has no such
456
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive() const {
457
std::set<App::DocumentObject*> inSet;
458
std::vector<App::DocumentObject*> res;
459
getInListEx(inSet,true,&res);
464
// More efficient algorithm to find the recursive inList of an object,
465
// including possible external parents. One shortcoming of this algorithm is
466
// it does not detect cyclic reference, althgouth it won't crash either.
467
void DocumentObject::getInListEx(std::set<App::DocumentObject*> &inSet,
468
bool recursive, std::vector<App::DocumentObject*> *inList) const
471
std::map<DocumentObject*,std::set<App::DocumentObject*> > outLists;
473
// Old DAG does not have pre-built InList, and must calculate The InList by
474
// going through all objects' OutLists. So we collect all objects and their
475
// outLists first here.
476
for(auto doc : GetApplication().getDocuments()) {
477
for(auto obj : doc->getObjects()) {
478
if(!obj || !obj->isAttachedToDocument() || obj==this)
480
const auto &outList = obj->getOutList();
481
outLists[obj].insert(outList.begin(),outList.end());
485
std::stack<DocumentObject*> pendings;
486
pendings.push(const_cast<DocumentObject*>(this));
487
while(pendings.size()) {
488
auto obj = pendings.top();
490
for(auto &v : outLists) {
491
if(v.first == obj) continue;
492
auto &outList = v.second;
493
// Check the outList to see if the object is there, and pend the
494
// object for recursive check if it's not already in the inList
495
if(outList.find(obj)!=outList.end() &&
496
inSet.insert(v.first).second &&
499
pendings.push(v.first);
506
inSet.insert(_inList.begin(),_inList.end());
512
std::stack<DocumentObject*> pendings;
513
pendings.push(const_cast<DocumentObject*>(this));
514
while(!pendings.empty()) {
515
auto obj = pendings.top();
517
for(auto o : obj->getInList()) {
518
if(o && o->isAttachedToDocument() && inSet.insert(o).second) {
521
inList->push_back(o);
529
std::set<App::DocumentObject*> DocumentObject::getInListEx(bool recursive) const {
530
std::set<App::DocumentObject*> ret;
531
getInListEx(ret,recursive);
535
void _getOutListRecursive(std::set<DocumentObject*>& objSet,
536
const DocumentObject* obj,
537
const DocumentObject* checkObj, int depth)
539
for (const auto objIt : obj->getOutList()) {
540
// if the check object is in the recursive inList we have a cycle!
541
if (objIt == checkObj || depth <= 0) {
542
throw Base::BadGraphError("DocumentObject::getOutListRecursive(): cyclic dependency detected!");
545
// if the element was already in the set then there is no need to process it again
546
auto pair = objSet.insert(objIt);
548
_getOutListRecursive(objSet, objIt, checkObj, depth-1);
552
std::vector<App::DocumentObject*> DocumentObject::getOutListRecursive() const
554
// number of objects in document is a good estimate in result size
555
int maxDepth = GetApplication().checkLinkDepth(0);
556
std::set<App::DocumentObject*> result;
558
// using a recursive helper to collect all OutLists
559
_getOutListRecursive(result, this, this, maxDepth);
561
std::vector<App::DocumentObject*> array;
562
array.insert(array.begin(), result.begin(), result.end());
566
// helper for isInInListRecursive()
567
bool _isInInListRecursive(const DocumentObject* act,
568
const DocumentObject* checkObj, int depth)
571
for (auto obj : act->getInList()) {
574
// if we reach the depth limit we have a cycle!
576
throw Base::BadGraphError("DocumentObject::isInInListRecursive(): cyclic dependency detected!");
579
if (_isInInListRecursive(obj, checkObj, depth - 1))
591
bool DocumentObject::isInInListRecursive(DocumentObject *linkTo) const
593
return this==linkTo || getInListEx(true).count(linkTo);
596
bool DocumentObject::isInInList(DocumentObject *linkTo) const
599
if (std::find(_inList.begin(), _inList.end(), linkTo) != _inList.end())
609
// helper for isInOutListRecursive()
610
bool _isInOutListRecursive(const DocumentObject* act,
611
const DocumentObject* checkObj, int depth)
614
for (auto obj : act->getOutList()) {
617
// if we reach the depth limit we have a cycle!
619
throw Base::BadGraphError("DocumentObject::isInOutListRecursive(): cyclic dependency detected!");
622
if (_isInOutListRecursive(obj, checkObj, depth - 1))
634
bool DocumentObject::isInOutListRecursive(DocumentObject *linkTo) const
636
int maxDepth = getDocument()->countObjects() + 2;
637
return _isInOutListRecursive(this, linkTo, maxDepth);
640
std::vector<std::list<App::DocumentObject*> >
641
DocumentObject::getPathsByOutList(App::DocumentObject* to) const
643
return _pDoc->getPathsByOutList(this, to);
646
DocumentObjectGroup* DocumentObject::getGroup() const
648
return dynamic_cast<DocumentObjectGroup*>(GroupExtension::getGroupOfObject(this));
651
bool DocumentObject::testIfLinkDAGCompatible(DocumentObject *linkTo) const
653
std::vector<App::DocumentObject*> linkTo_in_vector;
654
linkTo_in_vector.push_back(linkTo);
655
return this->testIfLinkDAGCompatible(linkTo_in_vector);
658
bool DocumentObject::testIfLinkDAGCompatible(const std::vector<DocumentObject *> &linksTo) const
660
auto inLists = getInListEx(true);
661
inLists.emplace(const_cast<DocumentObject*>(this));
662
for(auto obj : linksTo)
663
if(inLists.count(obj))
668
bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSubList &linksTo) const
670
const std::vector<App::DocumentObject*> &linksTo_in_vector = linksTo.getValues();
671
return this->testIfLinkDAGCompatible(linksTo_in_vector);
674
bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSub &linkTo) const
676
std::vector<App::DocumentObject*> linkTo_in_vector;
677
linkTo_in_vector.reserve(1);
678
linkTo_in_vector.push_back(linkTo.getValue());
679
return this->testIfLinkDAGCompatible(linkTo_in_vector);
682
void DocumentObject::onLostLinkToObject(DocumentObject*)
687
App::Document *DocumentObject::getDocument() const
692
void DocumentObject::setDocument(App::Document* doc)
698
bool DocumentObject::removeDynamicProperty(const char* name)
700
if (!_pDoc || testStatus(ObjectStatus::Destroy))
703
Property* prop = getDynamicPropertyByName(name);
704
if(!prop || prop->testStatus(App::Property::LockDynamic))
707
if(prop->isDerivedFrom(PropertyLinkBase::getClassTypeId()))
710
_pDoc->addOrRemovePropertyOfObject(this, prop, false);
712
auto expressions = ExpressionEngine.getExpressions();
713
std::vector<App::ObjectIdentifier> removeExpr;
715
for (const auto& it : expressions) {
716
if (it.first.getProperty() == prop) {
717
removeExpr.push_back(it.first);
721
for (const auto& it : removeExpr) {
722
ExpressionEngine.setValue(it, std::shared_ptr<Expression>());
725
return TransactionalObject::removeDynamicProperty(name);
728
App::Property* DocumentObject::addDynamicProperty(
729
const char* type, const char* name, const char* group, const char* doc,
730
short attr, bool ro, bool hidden)
732
auto prop = TransactionalObject::addDynamicProperty(type,name,group,doc,attr,ro,hidden);
734
_pDoc->addOrRemovePropertyOfObject(this, prop, true);
738
void DocumentObject::onBeforeChange(const Property* prop)
740
// Store current name in oldLabel, to be able to easily retrieve old name of document object later
741
// when renaming expressions.
743
oldLabel = Label.getStrValue();
746
onBeforeChangeProperty(_pDoc, prop);
748
signalBeforeChange(*this,*prop);
751
void DocumentObject::onEarlyChange(const Property *prop)
753
if(GetApplication().isClosingAll())
756
if(!GetApplication().isRestoring() &&
757
!prop->testStatus(Property::PartialTrigger) &&
759
getDocument()->testStatus(Document::PartialDoc))
761
static App::Document *warnedDoc;
762
if(warnedDoc != getDocument()) {
763
warnedDoc = getDocument();
764
FC_WARN("Changes to partial loaded document will not be saved: "
765
<< getFullName() << '.' << prop->getName());
769
signalEarlyChanged(*this, *prop);
772
/// get called by the container when a Property was changed
773
void DocumentObject::onChanged(const Property* prop)
778
if(GetApplication().isClosingAll())
781
if(!GetApplication().isRestoring() &&
782
!prop->testStatus(Property::PartialTrigger) &&
784
getDocument()->testStatus(Document::PartialDoc))
786
static App::Document *warnedDoc;
787
if(warnedDoc != getDocument()) {
788
warnedDoc = getDocument();
789
FC_WARN("Changes to partial loaded document will not be saved: "
790
<< getFullName() << '.' << prop->getName());
794
// Delay signaling view provider until the document object has handled the
797
// _pDoc->onChangedProperty(this,prop);
799
if (prop == &Label && _pDoc && oldLabel != Label.getStrValue())
800
_pDoc->signalRelabelObject(*this);
802
// set object touched if it is an input property
803
if (!testStatus(ObjectStatus::NoTouch)
804
&& !(prop->getType() & Prop_Output)
805
&& !prop->testStatus(Property::Output))
807
if(!StatusBits.test(ObjectStatus::Touch)) {
808
FC_TRACE("touch '" << getFullName() << "' on change of '" << prop->getName() << "'");
809
StatusBits.set(ObjectStatus::Touch);
811
// must execute on document recompute
812
if (!(prop->getType() & Prop_NoRecompute))
813
StatusBits.set(ObjectStatus::Enforce);
816
//call the parent for appropriate handling
817
TransactionalObject::onChanged(prop);
819
// Now signal the view provider
821
_pDoc->onChangedProperty(this,prop);
823
signalChanged(*this,*prop);
826
void DocumentObject::clearOutListCache() const {
829
_outListCached = false;
832
PyObject *DocumentObject::getPyObject()
834
if (PythonObject.is(Py::_None())) {
835
// ref counter is set to 1
836
PythonObject = Py::Object(new DocumentObjectPy(this),true);
838
return Py::new_reference_to(PythonObject);
841
DocumentObject *DocumentObject::getSubObject(const char *subname,
842
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
844
DocumentObject *ret = nullptr;
845
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
846
for(auto ext : exts) {
847
if(ext->extensionGetSubObject(ret,subname,pyObj,mat,transform, depth))
852
const char *dot=nullptr;
853
if(!subname || !(dot=strchr(subname,'.'))) {
854
ret = const_cast<DocumentObject*>(this);
855
}else if(subname[0]=='$') {
856
name = std::string(subname+1,dot);
857
for(auto obj : getOutList()) {
858
if(name == obj->Label.getValue()) {
864
name = std::string(subname,dot);
865
const auto &outList = getOutList();
866
if(outList.size()!=_outListMap.size()) {
868
for(auto obj : outList)
869
_outListMap[obj->getDagKey()] = obj;
871
auto it = _outListMap.find(name.c_str());
872
if(it != _outListMap.end())
876
// TODO: By right, normal object's placement does not transform its sub
877
// objects (think of the claimed children of a Fusion). But I do think we
878
// should change that.
879
if(transform && mat) {
880
auto pla = Base::freecad_dynamic_cast<PropertyPlacement>(getPropertyByName("Placement"));
882
*mat *= pla->getValue().toMatrix();
886
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
890
std::vector<DocumentObject*> DocumentObject::getSubObjectList(const char *subname) const {
891
std::vector<DocumentObject*> res;
892
res.push_back(const_cast<DocumentObject*>(this));
893
if(!subname || !subname[0])
895
std::string sub(subname);
896
for(auto pos=sub.find('.');pos!=std::string::npos;pos=sub.find('.',pos+1)) {
899
auto sobj = getSubObject(sub.c_str());
900
if(!sobj || !sobj->isAttachedToDocument())
908
std::vector<std::string> DocumentObject::getSubObjects(int reason) const {
909
std::vector<std::string> ret;
910
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
911
for(auto ext : exts) {
912
if(ext->extensionGetSubObjects(ret,reason))
918
std::vector<std::pair<App::DocumentObject *,std::string>> DocumentObject::getParents(int depth) const {
919
std::vector<std::pair<App::DocumentObject *, std::string>> ret;
920
if (!isAttachedToDocument() || !GetApplication().checkLinkDepth(depth, MessageOption::Throw)) {
924
std::string name(getNameInDocument());
926
for (auto parent : getInList()) {
927
if (!parent || !parent->isAttachedToDocument()) {
931
if (!parent->hasChildElement() &&
932
!parent->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId())) {
936
if (!parent->getSubObject(name.c_str())) {
940
auto links = GetApplication().getLinksTo(parent, App::GetLinkRecursive);
941
links.insert(parent);
943
for (auto parent : links) {
944
auto parents = parent->getParents(depth + 1);
945
if (parents.empty()) {
946
parents.emplace_back(parent, std::string());
949
for (auto &v : parents) {
950
ret.emplace_back(v.first, v.second + name);
958
App::DocumentObject* DocumentObject::getFirstParent() const
960
for (auto obj : getInList()) {
961
if (obj->hasExtension(App::GroupExtension::getExtensionClassTypeId(), true)) {
969
DocumentObject *DocumentObject::getLinkedObject(
970
bool recursive, Base::Matrix4D *mat, bool transform, int depth) const
972
DocumentObject *ret = nullptr;
973
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
974
for(auto ext : exts) {
975
if(ext->extensionGetLinkedObject(ret,recursive,mat,transform,depth))
978
if(transform && mat) {
979
auto pla = dynamic_cast<PropertyPlacement*>(getPropertyByName("Placement"));
981
*mat *= pla->getValue().toMatrix();
983
return const_cast<DocumentObject*>(this);
986
void DocumentObject::Save (Base::Writer &writer) const
988
if (this->isAttachedToDocument())
989
writer.ObjectName = this->getNameInDocument();
990
App::ExtensionContainer::Save(writer);
994
* @brief Associate the expression \expr with the object identifier \a path in this document object.
995
* @param path Target object identifier for the result of the expression
996
* @param expr Expression tree
999
void DocumentObject::setExpression(const ObjectIdentifier &path, std::shared_ptr<Expression> expr)
1001
ExpressionEngine.setValue(path, expr);
1005
* @brief Clear the expression of the object identifier \a path in this document object.
1006
* @param path Target object identifier
1009
void DocumentObject::clearExpression(const ObjectIdentifier & path)
1011
setExpression(path, std::shared_ptr<Expression>());
1015
* @brief Get expression information associated with \a path.
1016
* @param path Object identifier
1017
* @return Expression info, containing expression and optional comment.
1020
const PropertyExpressionEngine::ExpressionInfo DocumentObject::getExpression(const ObjectIdentifier &path) const
1022
boost::any value = ExpressionEngine.getPathValue(path);
1024
if (value.type() == typeid(PropertyExpressionEngine::ExpressionInfo))
1025
return boost::any_cast<PropertyExpressionEngine::ExpressionInfo>(value);
1027
return PropertyExpressionEngine::ExpressionInfo();
1031
* @brief Invoke ExpressionEngine's renameObjectIdentifier, to possibly rewrite expressions using
1032
* the \a paths map with current and new identifiers.
1037
void DocumentObject::renameObjectIdentifiers(const std::map<ObjectIdentifier, ObjectIdentifier> &paths)
1039
ExpressionEngine.renameObjectIdentifiers(paths);
1042
void DocumentObject::onDocumentRestored()
1044
//call all extensions
1045
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1046
for(auto ext : vector)
1047
ext->onExtendedDocumentRestored();
1048
if(Visibility.testStatus(Property::Output))
1049
Visibility.setStatus(Property::NoModify,true);
1052
void DocumentObject::onUndoRedoFinished()
1057
void DocumentObject::onSettingDocument()
1059
//call all extensions
1060
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1061
for(auto ext : vector)
1062
ext->onExtendedSettingDocument();
1065
void DocumentObject::setupObject()
1067
//call all extensions
1068
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1069
for(auto ext : vector)
1070
ext->onExtendedSetupObject();
1073
void DocumentObject::unsetupObject()
1075
//call all extensions
1076
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1077
for(auto ext : vector)
1078
ext->onExtendedUnsetupObject();
1081
void App::DocumentObject::_removeBackLink(DocumentObject* rmvObj)
1084
//do not use erase-remove idom, as this erases ALL entries that match. we only want to remove a
1086
auto it = std::find(_inList.begin(), _inList.end(), rmvObj);
1087
if(it != _inList.end())
1094
void App::DocumentObject::_addBackLink(DocumentObject* newObj)
1097
//we need to add all links, even if they are available multiple times. The reason for this is the
1098
//removal: If a link loses this object it removes the backlink. If we would have added it only once
1099
//this removal would clear the object from the inlist, even though there may be other link properties
1100
//from this object that link to us.
1101
_inList.push_back(newObj);
1107
int DocumentObject::setElementVisible(const char *element, bool visible) {
1108
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
1109
int ret = ext->extensionSetElementVisible(element,visible);
1117
int DocumentObject::isElementVisible(const char *element) const {
1118
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
1119
int ret = ext->extensionIsElementVisible(element);
1127
bool DocumentObject::hasChildElement() const {
1128
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
1129
if(ext->extensionHasChildElement())
1135
DocumentObject *DocumentObject::resolve(const char *subname,
1136
App::DocumentObject **parent, std::string *childName, const char **subElement,
1137
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
1139
auto self = const_cast<DocumentObject*>(this);
1140
if(parent) *parent = nullptr;
1141
if(subElement) *subElement = nullptr;
1143
auto obj = getSubObject(subname,pyObj,pmat,transform,depth);
1144
if(!obj || !subname || *subname==0)
1147
if(!parent && !subElement)
1150
// NOTE, the convention of '.' separated SubName demands a mandatory ending
1151
// '.' for each object name in SubName, even if there is no subelement
1152
// following it. So finding the last dot will give us the end of the last
1154
const char *dot=nullptr;
1155
if(Data::isMappedElement(subname) ||
1156
!(dot=strrchr(subname,'.')) ||
1160
*subElement = dot?dot+1:subname;
1161
return obj; // this means no parent object reference in SubName
1167
bool elementMapChecked = false;
1168
const char *lastDot = dot;
1170
// check for the second last dot, which is the end of the last parent object
1171
if(*dot == '.' || dot == subname) {
1172
// We can't get parent object by its name, because the object may be
1173
// externally linked (i.e. in a different document). So go through
1174
// getSubObject again.
1175
if(!elementMapChecked) {
1176
elementMapChecked = true;
1177
const char *sub = dot==subname?dot:dot+1;
1178
if(Data::isMappedElement(sub)) {
1188
auto sobj = getSubObject(std::string(subname,dot-subname+1).c_str());
1191
// Link/LinkGroup has special visibility handling of plain
1192
// group, so keep ascending
1193
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
1197
for(auto ddot=dot-1;ddot!=subname;--ddot) {
1198
if(*ddot != '.') continue;
1199
auto sobj = getSubObject(std::string(subname,ddot-subname+1).c_str());
1200
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
1210
if(childName && lastDot!=dot) {
1213
const char *nextDot = strchr(dot,'.');
1215
*childName = std::string(dot,nextDot-dot);
1218
*subElement = *lastDot=='.'?lastDot+1:lastDot;
1222
DocumentObject *DocumentObject::resolveRelativeLink(std::string &subname,
1223
DocumentObject *&link, std::string &linkSub) const
1225
if(!link || !link->isAttachedToDocument() || !isAttachedToDocument())
1227
auto ret = const_cast<DocumentObject*>(this);
1229
auto sub = subname.c_str();
1231
for(auto dot=strchr(nextsub,'.');dot;nextsub=dot+1,dot=strchr(nextsub,'.')) {
1232
std::string subcheck(sub,nextsub-sub);
1233
subcheck += link->getNameInDocument();
1235
if(getSubObject(subcheck.c_str())==link) {
1236
ret = getSubObject(std::string(sub,dot+1-sub).c_str());
1239
subname = std::string(dot+1);
1246
size_t pos=0,linkPos=0;
1247
std::string linkssub,ssub;
1249
linkPos = linkSub.find('.',linkPos);
1250
if(linkPos == std::string::npos) {
1255
pos = subname.find('.',pos);
1256
if(pos == std::string::npos) {
1262
}while(subname.compare(0,pos,linkSub,0,linkPos)==0);
1264
if(pos != std::string::npos) {
1265
ret = getSubObject(subname.substr(0,pos).c_str());
1270
subname = subname.substr(pos);
1273
link = link->getSubObject(linkSub.substr(0,linkPos).c_str());
1276
linkSub = linkSub.substr(linkPos);
1281
bool DocumentObject::adjustRelativeLinks(
1282
const std::set<App::DocumentObject *> &inList,
1283
std::set<App::DocumentObject *> *visited)
1286
visited->insert(this);
1288
bool touched = false;
1289
std::vector<Property*> props;
1290
getPropertyList(props);
1291
for(auto prop : props) {
1292
auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
1293
if(linkProp && linkProp->adjustLink(inList))
1297
for(auto obj : getOutList()) {
1298
if(!visited->count(obj)) {
1299
if(obj->adjustRelativeLinks(inList,visited))
1307
const std::string &DocumentObject::hiddenMarker() {
1308
static std::string marker("!hide");
1312
const char *DocumentObject::hasHiddenMarker(const char *subname) {
1315
const char *marker = strrchr(subname,'.');
1320
return hiddenMarker()==marker?marker:nullptr;
1323
bool DocumentObject::redirectSubName(std::ostringstream &, DocumentObject *, DocumentObject *) const {
1327
void DocumentObject::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus) {
1329
if(!Document::isAnyRestoring() && isAttachedToDocument() && getDocument())
1330
getDocument()->signalChangePropertyEditor(*getDocument(),prop);