| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <stack>
|
| | #include <memory>
|
| | #include <map>
|
| | #include <set>
|
| | #include <vector>
|
| | #include <string>
|
| |
|
| | #include <Base/Console.h>
|
| | #include <Base/Matrix.h>
|
| | #include <Base/Placement.h>
|
| | #include <Base/Tools.h>
|
| | #include <Base/Writer.h>
|
| |
|
| | #include "Expression.h"
|
| | #include "Application.h"
|
| | #include "ElementNamingUtils.h"
|
| | #include "Document.h"
|
| | #include "DocumentObject.h"
|
| | #include "DocumentObjectPy.h"
|
| | #include "DocumentObjectExtension.h"
|
| | #include "DocumentObjectGroup.h"
|
| | #include "GeoFeatureGroupExtension.h"
|
| | #include "Link.h"
|
| | #include "ObjectIdentifier.h"
|
| | #include "PropertyExpressionEngine.h"
|
| | #include "PropertyLinks.h"
|
| |
|
| |
|
| | FC_LOG_LEVEL_INIT("App", true, true)
|
| |
|
| | using namespace App;
|
| |
|
| |
|
| | PROPERTY_SOURCE(App::DocumentObject, App::TransactionalObject)
|
| |
|
| | DocumentObjectExecReturn* DocumentObject::StdReturn = nullptr;
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | DocumentObject::DocumentObject()
|
| | : ExpressionEngine()
|
| | {
|
| |
|
| | ADD_PROPERTY_TYPE(Label, ("Unnamed"), "Base", Prop_Output, "User name of the object (UTF8)");
|
| | ADD_PROPERTY_TYPE(Label2, (""), "Base", Prop_Hidden, "User description of the object (UTF8)");
|
| | Label2.setStatus(App::Property::Output, true);
|
| | ADD_PROPERTY_TYPE(ExpressionEngine, (), "Base", Prop_Hidden, "Property expressions");
|
| |
|
| | ADD_PROPERTY(Visibility, (true));
|
| |
|
| |
|
| |
|
| |
|
| | Visibility.setStatus(Property::Output, true);
|
| | Visibility.setStatus(Property::Hidden, true);
|
| | Visibility.setStatus(Property::NoModify, true);
|
| | }
|
| |
|
| | DocumentObject::~DocumentObject()
|
| | {
|
| | if (!PythonObject.is(Py::_None())) {
|
| | Base::PyGILStateLocker lock;
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | Base::PyObjectBase* obj = static_cast<Base::PyObjectBase*>(PythonObject.ptr());
|
| |
|
| | obj->setInvalid();
|
| | }
|
| | }
|
| |
|
| | void DocumentObject::printInvalidLinks() const
|
| | {
|
| | try {
|
| |
|
| |
|
| |
|
| | std::vector<App::DocumentObject*> invalid_linkobjs;
|
| | std::string objnames, scopenames;
|
| | GeoFeatureGroupExtension::getInvalidLinkObjects(this, invalid_linkobjs);
|
| | for (auto& obj : invalid_linkobjs) {
|
| | objnames += obj->getNameInDocument();
|
| | objnames += " ";
|
| | for (auto& scope : obj->getParents()) {
|
| | if (scopenames.length() > 80) {
|
| | scopenames += "... ";
|
| | break;
|
| | }
|
| |
|
| | scopenames += scope.first->getNameInDocument();
|
| | scopenames += " ";
|
| | }
|
| |
|
| | if (objnames.length() > 80) {
|
| | objnames += "... ";
|
| | break;
|
| | }
|
| | }
|
| |
|
| | if (objnames.empty()) {
|
| | objnames = "N/A";
|
| | }
|
| | else {
|
| | objnames.pop_back();
|
| | }
|
| |
|
| | if (scopenames.empty()) {
|
| | scopenames = "N/A";
|
| | }
|
| | else {
|
| | scopenames.pop_back();
|
| | }
|
| |
|
| | 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",
|
| | getTypeId().getName(),
|
| | objnames.c_str(),
|
| | getNameInDocument(),
|
| | scopenames.c_str());
|
| | }
|
| | catch (const Base::Exception& e) {
|
| | e.reportException();
|
| | }
|
| | }
|
| |
|
| | App::DocumentObjectExecReturn* DocumentObject::recompute()
|
| | {
|
| |
|
| | if (!GeoFeatureGroupExtension::areLinksValid(this)) {
|
| | printInvalidLinks();
|
| | }
|
| |
|
| |
|
| | Base::ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::Recompute, this);
|
| |
|
| |
|
| | this->setStatus(App::RecomputeExtension, true);
|
| |
|
| | auto ret = this->execute();
|
| | if (ret == StdReturn) {
|
| |
|
| |
|
| | if (this->testStatus(App::RecomputeExtension)) {
|
| | ret = executeExtensions();
|
| | }
|
| | }
|
| |
|
| | return ret;
|
| | }
|
| |
|
| | DocumentObjectExecReturn* DocumentObject::execute()
|
| | {
|
| | return executeExtensions();
|
| | }
|
| |
|
| | App::DocumentObjectExecReturn* DocumentObject::executeExtensions()
|
| | {
|
| |
|
| | this->setStatus(App::RecomputeExtension, false);
|
| | auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : vector) {
|
| | auto ret = ext->extensionExecute();
|
| | if (ret != StdReturn) {
|
| | return ret;
|
| | }
|
| | }
|
| |
|
| | return StdReturn;
|
| | }
|
| |
|
| | bool DocumentObject::recomputeFeature(bool recursive)
|
| | {
|
| | Document* doc = this->getDocument();
|
| | if (doc) {
|
| | return doc->recomputeFeature(this, recursive);
|
| | }
|
| | return isValid();
|
| | }
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | void DocumentObject::touch(bool noRecompute)
|
| | {
|
| | if (!noRecompute) {
|
| | StatusBits.set(ObjectStatus::Enforce);
|
| | }
|
| | StatusBits.set(ObjectStatus::Touch);
|
| | if (_pDoc) {
|
| | _pDoc->signalTouchedObject(*this);
|
| | }
|
| | }
|
| |
|
| | |
| | |
| | |
| |
|
| | void DocumentObject::freeze()
|
| | {
|
| | StatusBits.set(ObjectStatus::Freeze);
|
| |
|
| |
|
| | this->readOnlyProperties.clear();
|
| | std::vector<std::pair<const char*, Property*>> list;
|
| | static_cast<App::PropertyContainer*>(this)->getPropertyNamedList(list);
|
| | for (auto pair: list){
|
| | if (pair.second->isReadOnly()){
|
| | this->readOnlyProperties.push_back(pair.first);
|
| | } else {
|
| | pair.second->setReadOnly(true);
|
| | }
|
| | }
|
| |
|
| |
|
| | if (_pDoc) {
|
| | _pDoc->signalTouchedObject(*this);
|
| | }
|
| | }
|
| |
|
| | |
| | |
| | |
| |
|
| | void DocumentObject::unfreeze(bool noRecompute)
|
| | {
|
| | StatusBits.reset(ObjectStatus::Freeze);
|
| |
|
| |
|
| | std::vector<std::pair<const char*, Property*>> list;
|
| | static_cast<App::PropertyContainer*>(this)->getPropertyNamedList(list);
|
| |
|
| | for (auto pair: list){
|
| | if (! std::count(readOnlyProperties.begin(), readOnlyProperties.end(), pair.first)){
|
| | pair.second->setReadOnly(false);
|
| | }
|
| | }
|
| |
|
| | touch(noRecompute);
|
| | }
|
| |
|
| | |
| | |
| | |
| |
|
| | bool DocumentObject::isTouched() const
|
| | {
|
| | return ExpressionEngine.isTouched() || StatusBits.test(ObjectStatus::Touch);
|
| | }
|
| |
|
| | |
| | |
| | |
| | |
| |
|
| | void DocumentObject::enforceRecompute()
|
| | {
|
| | touch(false);
|
| | }
|
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | bool DocumentObject::mustRecompute() const
|
| | {
|
| | if (StatusBits.test(ObjectStatus::Freeze)) {
|
| | return false;
|
| | }
|
| |
|
| | if (StatusBits.test(ObjectStatus::Enforce)) {
|
| | return true;
|
| | }
|
| |
|
| | return mustExecute() > 0;
|
| | }
|
| |
|
| | short DocumentObject::mustExecute() const
|
| | {
|
| | if (ExpressionEngine.isTouched()) {
|
| | return 1;
|
| | }
|
| |
|
| |
|
| | auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : vector) {
|
| | if (ext->extensionMustExecute()) {
|
| | return 1;
|
| | }
|
| | }
|
| |
|
| | return 0;
|
| | }
|
| |
|
| | const char* DocumentObject::getStatusString() const
|
| | {
|
| | if (isError()) {
|
| | const char* text = getDocument()->getErrorDescription(this);
|
| | return text ? text : "Error";
|
| | }
|
| | else if (isFreezed()){
|
| | return "Freezed";
|
| | }
|
| | else if (isTouched()) {
|
| | return "Touched";
|
| | }
|
| | else {
|
| | return "Valid";
|
| | }
|
| | }
|
| |
|
| | std::string DocumentObject::getFullName() const
|
| | {
|
| | if (!getDocument() || !isAttachedToDocument()) {
|
| | return "?";
|
| | }
|
| | std::string name(getDocument()->getName());
|
| | name += '#';
|
| | name += *pcNameInDocument;
|
| | return name;
|
| | }
|
| |
|
| | std::string DocumentObject::getFullLabel() const
|
| | {
|
| | if (!getDocument()) {
|
| | return "?";
|
| | }
|
| |
|
| | auto name = getDocument()->Label.getStrValue();
|
| | name += "#";
|
| | name += Label.getStrValue();
|
| | return name;
|
| | }
|
| |
|
| | const char* DocumentObject::getDagKey() const
|
| | {
|
| | if (!pcNameInDocument) {
|
| | return nullptr;
|
| | }
|
| | return pcNameInDocument->c_str();
|
| | }
|
| |
|
| | const char* DocumentObject::getNameInDocument() const
|
| | {
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | if (!pcNameInDocument) {
|
| | return nullptr;
|
| | }
|
| | return pcNameInDocument->c_str();
|
| | }
|
| |
|
| | int DocumentObject::isExporting() const
|
| | {
|
| | if (!getDocument() || !isAttachedToDocument()) {
|
| | return 0;
|
| | }
|
| | return getDocument()->isExporting(this);
|
| | }
|
| |
|
| | std::string DocumentObject::getExportName(bool forced) const
|
| | {
|
| | if (!isAttachedToDocument()) {
|
| | return {};
|
| | }
|
| |
|
| | if (!forced && !isExporting()) {
|
| | return *pcNameInDocument;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | return *pcNameInDocument + '@' + getDocument()->getName();
|
| | }
|
| |
|
| | bool DocumentObject::isAttachedToDocument() const
|
| | {
|
| | return (pcNameInDocument != nullptr);
|
| | }
|
| |
|
| | const char* DocumentObject::detachFromDocument()
|
| | {
|
| | const std::string* name = pcNameInDocument;
|
| | pcNameInDocument = nullptr;
|
| | return name ? name->c_str() : nullptr;
|
| | }
|
| |
|
| | const std::vector<DocumentObject*>& DocumentObject::getOutList() const
|
| | {
|
| | if (!_outListCached) {
|
| | _outList.clear();
|
| | getOutList(0, _outList);
|
| | _outListCached = true;
|
| | }
|
| | return _outList;
|
| | }
|
| |
|
| | std::vector<DocumentObject*> DocumentObject::getOutList(int options) const
|
| | {
|
| | std::vector<DocumentObject*> res;
|
| | getOutList(options, res);
|
| | return res;
|
| | }
|
| |
|
| | void DocumentObject::getOutList(int options, std::vector<DocumentObject*>& res) const
|
| | {
|
| | if (_outListCached && !options) {
|
| | res.insert(res.end(), _outList.begin(), _outList.end());
|
| | return;
|
| | }
|
| | std::vector<Property*> props;
|
| | getPropertyList(props);
|
| | bool noHidden = !!(options & OutListNoHidden);
|
| | std::size_t size = res.size();
|
| | for (auto prop : props) {
|
| | auto link = freecad_cast<PropertyLinkBase*>(prop);
|
| | if (link) {
|
| | link->getLinks(res, noHidden);
|
| | }
|
| | }
|
| | if (!(options & OutListNoExpression)) {
|
| | ExpressionEngine.getLinks(res);
|
| | }
|
| |
|
| | if (options & OutListNoXLinked) {
|
| | for (auto it = res.begin() + size; it != res.end();) {
|
| | auto obj = *it;
|
| | if (obj && obj->getDocument() != getDocument()) {
|
| | it = res.erase(it);
|
| | }
|
| | else {
|
| | ++it;
|
| | }
|
| | }
|
| | }
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> DocumentObject::getOutListOfProperty(App::Property* prop) const
|
| | {
|
| | std::vector<DocumentObject*> ret;
|
| | if (!prop || prop->getContainer() != this) {
|
| | return ret;
|
| | }
|
| |
|
| | auto link = freecad_cast<PropertyLinkBase*>(prop);
|
| | if (link) {
|
| | link->getLinks(ret);
|
| | }
|
| | return ret;
|
| | }
|
| |
|
| | const std::vector<App::DocumentObject*>& DocumentObject::getInList() const
|
| | {
|
| | return _inList;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | std::vector<App::DocumentObject*> DocumentObject::getInListRecursive() const
|
| | {
|
| | std::set<App::DocumentObject*> inSet;
|
| | std::vector<App::DocumentObject*> res;
|
| | getInListEx(inSet, true, &res);
|
| | return res;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | void DocumentObject::getInListEx(std::set<App::DocumentObject*>& inSet,
|
| | bool recursive,
|
| | std::vector<App::DocumentObject*>* inList) const
|
| | {
|
| | if (!recursive) {
|
| | inSet.insert(_inList.begin(), _inList.end());
|
| | if (inList) {
|
| | *inList = _inList;
|
| | }
|
| | return;
|
| | }
|
| |
|
| | std::stack<DocumentObject*> pendings;
|
| | pendings.push(const_cast<DocumentObject*>(this));
|
| | while (!pendings.empty()) {
|
| | auto obj = pendings.top();
|
| | pendings.pop();
|
| | for (auto o : obj->getInList()) {
|
| | if (o && o->isAttachedToDocument() && inSet.insert(o).second) {
|
| | pendings.push(o);
|
| | if (inList) {
|
| | inList->push_back(o);
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| |
|
| | std::set<App::DocumentObject*> DocumentObject::getInListEx(bool recursive) const
|
| | {
|
| | std::set<App::DocumentObject*> ret;
|
| | getInListEx(ret, recursive);
|
| | return ret;
|
| | }
|
| |
|
| | void _getOutListRecursive(std::set<DocumentObject*>& objSet,
|
| | const DocumentObject* obj,
|
| | const DocumentObject* checkObj,
|
| | int depth)
|
| | {
|
| | for (const auto objIt : obj->getOutList()) {
|
| |
|
| | if (objIt == checkObj || depth <= 0) {
|
| | throw Base::BadGraphError(
|
| | "DocumentObject::getOutListRecursive(): cyclic dependency detected!");
|
| | }
|
| |
|
| |
|
| | auto pair = objSet.insert(objIt);
|
| | if (pair.second) {
|
| | _getOutListRecursive(objSet, objIt, checkObj, depth - 1);
|
| | }
|
| | }
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> DocumentObject::getOutListRecursive() const
|
| | {
|
| |
|
| | int maxDepth = GetApplication().checkLinkDepth(0);
|
| | std::set<App::DocumentObject*> result;
|
| |
|
| |
|
| | _getOutListRecursive(result, this, this, maxDepth);
|
| |
|
| | std::vector<App::DocumentObject*> array;
|
| | array.insert(array.begin(), result.begin(), result.end());
|
| | return array;
|
| | }
|
| |
|
| |
|
| | bool _isInInListRecursive(const DocumentObject* act, const DocumentObject* checkObj, int depth)
|
| | {
|
| | for (auto obj : act->getInList()) {
|
| | if (obj == checkObj) {
|
| | return true;
|
| | }
|
| |
|
| | if (depth <= 0) {
|
| | throw Base::BadGraphError(
|
| | "DocumentObject::isInInListRecursive(): cyclic dependency detected!");
|
| | }
|
| |
|
| | if (_isInInListRecursive(obj, checkObj, depth - 1)) {
|
| | return true;
|
| | }
|
| | }
|
| |
|
| | return false;
|
| | }
|
| |
|
| | bool DocumentObject::isInInListRecursive(DocumentObject* linkTo) const
|
| | {
|
| | return this == linkTo || getInListEx(true).contains(linkTo);
|
| | }
|
| |
|
| | bool DocumentObject::isInInList(DocumentObject* linkTo) const
|
| | {
|
| | if (std::ranges::find(_inList, linkTo) != _inList.end()) {
|
| | return true;
|
| | }
|
| | else {
|
| | return false;
|
| | }
|
| | }
|
| |
|
| |
|
| | bool _isInOutListRecursive(const DocumentObject* act, const DocumentObject* checkObj, int depth)
|
| | {
|
| | for (auto obj : act->getOutList()) {
|
| | if (obj == checkObj) {
|
| | return true;
|
| | }
|
| |
|
| | if (depth <= 0) {
|
| | throw Base::BadGraphError(
|
| | "DocumentObject::isInOutListRecursive(): cyclic dependency detected!");
|
| | }
|
| |
|
| | if (_isInOutListRecursive(obj, checkObj, depth - 1)) {
|
| | return true;
|
| | }
|
| | }
|
| |
|
| | return false;
|
| | }
|
| |
|
| | bool DocumentObject::isInOutListRecursive(DocumentObject* linkTo) const
|
| | {
|
| | int maxDepth = getDocument()->countObjects() + 2;
|
| | return _isInOutListRecursive(this, linkTo, maxDepth);
|
| | }
|
| |
|
| | std::vector<std::list<App::DocumentObject*>>
|
| | DocumentObject::getPathsByOutList(App::DocumentObject* to) const
|
| | {
|
| | return _pDoc->getPathsByOutList(this, to);
|
| | }
|
| |
|
| | DocumentObjectGroup* DocumentObject::getGroup() const
|
| | {
|
| | return freecad_cast<DocumentObjectGroup*>(GroupExtension::getGroupOfObject(this));
|
| | }
|
| |
|
| | bool DocumentObject::testIfLinkDAGCompatible(DocumentObject* linkTo) const
|
| | {
|
| | std::vector<App::DocumentObject*> linkTo_in_vector;
|
| | linkTo_in_vector.push_back(linkTo);
|
| | return this->testIfLinkDAGCompatible(linkTo_in_vector);
|
| | }
|
| |
|
| | bool DocumentObject::testIfLinkDAGCompatible(const std::vector<DocumentObject*>& linksTo) const
|
| | {
|
| | auto inLists = getInListEx(true);
|
| | inLists.emplace(const_cast<DocumentObject*>(this));
|
| | for (auto obj : linksTo) {
|
| | if (inLists.contains(obj)) {
|
| | return false;
|
| | }
|
| | }
|
| | return true;
|
| | }
|
| |
|
| | bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSubList& linksTo) const
|
| | {
|
| | const std::vector<App::DocumentObject*>& linksTo_in_vector = linksTo.getValues();
|
| | return this->testIfLinkDAGCompatible(linksTo_in_vector);
|
| | }
|
| |
|
| | bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSub& linkTo) const
|
| | {
|
| | std::vector<App::DocumentObject*> linkTo_in_vector;
|
| | linkTo_in_vector.reserve(1);
|
| | linkTo_in_vector.push_back(linkTo.getValue());
|
| | return this->testIfLinkDAGCompatible(linkTo_in_vector);
|
| | }
|
| |
|
| | void DocumentObject::onLostLinkToObject(DocumentObject*)
|
| | {}
|
| |
|
| | App::Document* DocumentObject::getDocument() const
|
| | {
|
| | return _pDoc;
|
| | }
|
| |
|
| | void DocumentObject::setDocument(App::Document* doc)
|
| | {
|
| | _pDoc = doc;
|
| | onSettingDocument();
|
| | }
|
| |
|
| | bool DocumentObject::removeDynamicProperty(const char* name)
|
| | {
|
| | if (!_pDoc || testStatus(ObjectStatus::Destroy)) {
|
| | return false;
|
| | }
|
| |
|
| | Property* prop = getDynamicPropertyByName(name);
|
| | if (!prop || prop->testStatus(App::Property::LockDynamic)) {
|
| | return false;
|
| | }
|
| |
|
| | if (prop->isDerivedFrom<PropertyLinkBase>()) {
|
| | clearOutListCache();
|
| | }
|
| |
|
| | _pDoc->addOrRemovePropertyOfObject(this, prop, false);
|
| |
|
| | auto expressions = ExpressionEngine.getExpressions();
|
| | std::vector<App::ObjectIdentifier> removeExpr;
|
| |
|
| | for (const auto& it : expressions) {
|
| | if (it.first.getProperty() == prop) {
|
| | removeExpr.push_back(it.first);
|
| | }
|
| | }
|
| |
|
| | for (const auto& it : removeExpr) {
|
| | ExpressionEngine.setValue(it, std::shared_ptr<Expression>());
|
| | }
|
| |
|
| | return TransactionalObject::removeDynamicProperty(name);
|
| | }
|
| |
|
| | bool DocumentObject::renameDynamicProperty(Property* prop, const char* name)
|
| | {
|
| | std::string oldName = prop->getName();
|
| |
|
| | auto expressions = ExpressionEngine.getExpressions();
|
| | std::vector<std::shared_ptr<Expression>> expressionsToMove;
|
| | std::vector<App::ObjectIdentifier> idsWithExprsToRemove;
|
| |
|
| | for (const auto& [id, expr] : expressions) {
|
| | if (id.getProperty() == prop) {
|
| | idsWithExprsToRemove.push_back(id);
|
| | expressionsToMove.emplace_back(expr->copy());
|
| | }
|
| | }
|
| |
|
| | for (const auto& it : idsWithExprsToRemove) {
|
| | ExpressionEngine.setValue(it, std::shared_ptr<Expression>());
|
| | }
|
| |
|
| | bool renamed = TransactionalObject::renameDynamicProperty(prop, name);
|
| | if (renamed && _pDoc) {
|
| | _pDoc->renamePropertyOfObject(this, prop, oldName.c_str());
|
| | }
|
| |
|
| |
|
| | App::ObjectIdentifier idNewProp(prop->getContainer(), std::string(name));
|
| | for (auto& exprToMove : expressionsToMove) {
|
| | ExpressionEngine.setValue(idNewProp, exprToMove);
|
| | }
|
| |
|
| | return renamed;
|
| | }
|
| |
|
| | App::Property* DocumentObject::addDynamicProperty(const char* type,
|
| | const char* name,
|
| | const char* group,
|
| | const char* doc,
|
| | short attr,
|
| | bool ro,
|
| | bool hidden)
|
| | {
|
| | auto prop = TransactionalObject::addDynamicProperty(type, name, group, doc, attr, ro, hidden);
|
| | if (prop && _pDoc) {
|
| | _pDoc->addOrRemovePropertyOfObject(this, prop, true);
|
| | }
|
| | return prop;
|
| | }
|
| |
|
| | void DocumentObject::onBeforeChange(const Property* prop)
|
| | {
|
| | if (isFreezed() && prop != &Visibility) {
|
| | return;
|
| | }
|
| |
|
| |
|
| |
|
| | if (prop == &Label)
|
| | oldLabel = Label.getStrValue();
|
| |
|
| | if (_pDoc){
|
| | onBeforeChangeProperty(_pDoc, prop);
|
| | }
|
| |
|
| | signalBeforeChange(*this, *prop);
|
| | }
|
| |
|
| | std::vector<std::pair<Property*, std::unique_ptr<Property>>>
|
| | DocumentObject::onProposedLabelChange(std::string& newLabel)
|
| | {
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | std::string oldLabel = Label.getStrValue();
|
| | assert(newLabel != oldLabel);
|
| | if (!isAttachedToDocument()) {
|
| | return {};
|
| | }
|
| | App::Document* doc = getDocument();
|
| | if (doc->isPerformingTransaction()
|
| | || (doc->testStatus(App::Document::Restoring)
|
| | && !doc->testStatus(App::Document::Importing))) {
|
| | return {};
|
| | }
|
| | static ParameterGrp::handle _hPGrp;
|
| | if (!_hPGrp) {
|
| | _hPGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document");
|
| | }
|
| | if (doc && !newLabel.empty() && !_hPGrp->GetBool("DuplicateLabels") && !allowDuplicateLabel()
|
| | && doc->containsLabel(newLabel)) {
|
| |
|
| | std::string objName = getNameInDocument();
|
| | if (!doc->containsLabel(objName) && doc->haveSameBaseName(objName, newLabel)) {
|
| |
|
| |
|
| | newLabel = objName;
|
| | }
|
| | else {
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | doc->unregisterLabel(oldLabel);
|
| | newLabel = doc->makeUniqueLabel(newLabel);
|
| | doc->registerLabel(oldLabel);
|
| | }
|
| | }
|
| |
|
| |
|
| | onBeforeChangeLabel(newLabel);
|
| |
|
| | if (oldLabel == newLabel || getDocument()->testStatus(App::Document::Restoring)) {
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | return {};
|
| | }
|
| | return PropertyLinkBase::updateLabelReferences(this, newLabel.c_str());
|
| | }
|
| |
|
| | void DocumentObject::onEarlyChange(const Property* prop)
|
| | {
|
| | if (isFreezed() && prop != &Visibility) {
|
| | return;
|
| | }
|
| |
|
| | if (GetApplication().isClosingAll()) {
|
| | return;
|
| | }
|
| |
|
| | if (!GetApplication().isRestoring() && !prop->testStatus(Property::PartialTrigger)
|
| | && getDocument() && getDocument()->testStatus(Document::PartialDoc)) {
|
| | static App::Document* warnedDoc;
|
| | if (warnedDoc != getDocument()) {
|
| | warnedDoc = getDocument();
|
| | FC_WARN("Changes to partial loaded document will not be saved: " << getFullName() << '.'
|
| | << prop->getName());
|
| | }
|
| | }
|
| |
|
| | signalEarlyChanged(*this, *prop);
|
| | }
|
| |
|
| |
|
| | void DocumentObject::onChanged(const Property* prop)
|
| | {
|
| | if (prop == &Label && _pDoc && _pDoc->containsObject(this) && oldLabel != Label.getStrValue()) {
|
| | _pDoc->unregisterLabel(oldLabel);
|
| | _pDoc->registerLabel(Label.getStrValue());
|
| | }
|
| |
|
| | if (isFreezed() && prop != &Visibility) {
|
| | return;
|
| | }
|
| |
|
| | if (GetApplication().isClosingAll()) {
|
| | return;
|
| | }
|
| |
|
| | if (!GetApplication().isRestoring() && !prop->testStatus(Property::PartialTrigger)
|
| | && getDocument() && getDocument()->testStatus(Document::PartialDoc)) {
|
| | static App::Document* warnedDoc;
|
| | if (warnedDoc != getDocument()) {
|
| | warnedDoc = getDocument();
|
| | FC_WARN("Changes to partial loaded document will not be saved: " << getFullName() << '.'
|
| | << prop->getName());
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | if (prop == &Label && _pDoc && oldLabel != Label.getStrValue()) {
|
| | _pDoc->signalRelabelObject(*this);
|
| | }
|
| |
|
| |
|
| | if (!testStatus(ObjectStatus::NoTouch) && !(prop->getType() & Prop_Output)
|
| | && !prop->testStatus(Property::Output)) {
|
| | if (!StatusBits.test(ObjectStatus::Touch)) {
|
| | FC_TRACE("touch '" << getFullName() << "' on change of '" << prop->getName() << "'");
|
| | StatusBits.set(ObjectStatus::Touch);
|
| | }
|
| |
|
| | if (!(prop->getType() & Prop_NoRecompute)) {
|
| | StatusBits.set(ObjectStatus::Enforce);
|
| | }
|
| | }
|
| |
|
| |
|
| | TransactionalObject::onChanged(prop);
|
| |
|
| |
|
| | if (_pDoc) {
|
| | _pDoc->onChangedProperty(this, prop);
|
| | }
|
| |
|
| | signalChanged(*this, *prop);
|
| | }
|
| |
|
| | void DocumentObject::clearOutListCache() const
|
| | {
|
| | _outList.clear();
|
| | _outListMap.clear();
|
| | _outListCached = false;
|
| | }
|
| |
|
| | PyObject* DocumentObject::getPyObject()
|
| | {
|
| | if (PythonObject.is(Py::_None())) {
|
| |
|
| | PythonObject = Py::Object(new DocumentObjectPy(this), true);
|
| | }
|
| | return Py::new_reference_to(PythonObject);
|
| | }
|
| |
|
| | DocumentObject* DocumentObject::getSubObject(const char* subname,
|
| | PyObject** pyObj,
|
| | Base::Matrix4D* mat,
|
| | bool transform,
|
| | int depth) const
|
| | {
|
| | DocumentObject* ret = nullptr;
|
| | auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : exts) {
|
| | if (ext->extensionGetSubObject(ret, subname, pyObj, mat, transform, depth)) {
|
| | return ret;
|
| | }
|
| | }
|
| |
|
| | std::string name;
|
| | const char* dot = nullptr;
|
| | if (!subname || !(dot = strchr(subname, '.'))) {
|
| | ret = const_cast<DocumentObject*>(this);
|
| | }
|
| | else if (subname[0] == '$') {
|
| | name = std::string(subname + 1, dot);
|
| | for (auto obj : getOutList()) {
|
| | if (name == obj->Label.getValue()) {
|
| | ret = obj;
|
| | break;
|
| | }
|
| | }
|
| | }
|
| | else {
|
| | name = std::string(subname, dot);
|
| | const auto& outList = getOutList();
|
| | if (outList.size() != _outListMap.size()) {
|
| | _outListMap.clear();
|
| | for (auto obj : outList) {
|
| | _outListMap[obj->getDagKey()] = obj;
|
| | }
|
| | }
|
| | auto it = _outListMap.find(name.c_str());
|
| | if (it != _outListMap.end()) {
|
| | ret = it->second;
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | if (transform && mat) {
|
| | auto pla = freecad_cast<PropertyPlacement*>(getPropertyByName("Placement"));
|
| | if (pla) {
|
| | *mat *= pla->getValue().toMatrix();
|
| | }
|
| | }
|
| |
|
| | if (ret && dot) {
|
| | return ret->getSubObject(dot + 1, pyObj, mat, true, depth + 1);
|
| | }
|
| | return ret;
|
| | }
|
| |
|
| | namespace
|
| | {
|
| | std::vector<DocumentObject*>
|
| | getSubObjectListFlatten(const std::vector<App::DocumentObject*>& resNotFlatten,
|
| | std::vector<int>* const subsizes,
|
| | const App::DocumentObject* sobj,
|
| | App::DocumentObject** container,
|
| | bool& lastChild)
|
| | {
|
| | auto res {resNotFlatten};
|
| | auto linked = sobj->getLinkedObject();
|
| | if (*container) {
|
| | auto grp = App::GeoFeatureGroupExtension::getGroupOfObject(linked);
|
| | if (grp != *container) {
|
| | *container = nullptr;
|
| | }
|
| | else {
|
| | if (lastChild && !res.empty()) {
|
| | res.pop_back();
|
| | if (subsizes) {
|
| | subsizes->pop_back();
|
| | }
|
| | }
|
| | lastChild = true;
|
| | }
|
| | }
|
| | if (linked->getExtensionByType<App::GeoFeatureGroupExtension>(true)) {
|
| | *container = linked;
|
| | lastChild = false;
|
| | }
|
| | else if (linked != sobj || sobj->hasChildElement()) {
|
| |
|
| | *container = nullptr;
|
| | }
|
| | else if (auto ext = sobj->getExtensionByType<LinkBaseExtension>(true)) {
|
| |
|
| | if (ext->getElementCountValue() != 0) {
|
| | *container = nullptr;
|
| | }
|
| | }
|
| | return res;
|
| | }
|
| | }
|
| |
|
| | std::vector<DocumentObject*> DocumentObject::getSubObjectList(const char* subname,
|
| | std::vector<int>* const subsizes,
|
| | bool flatten) const
|
| | {
|
| | std::vector<DocumentObject*> res;
|
| | res.push_back(const_cast<DocumentObject*>(this));
|
| | if (subsizes) {
|
| | subsizes->push_back(0);
|
| | }
|
| | if (!subname || (subname[0] == '\0')) {
|
| | return res;
|
| | }
|
| | auto element = Data::findElementName(subname);
|
| | std::string sub(subname, element - subname);
|
| | App::DocumentObject* container = nullptr;
|
| |
|
| | bool lastChild = false;
|
| | if (flatten) {
|
| | auto linked = getLinkedObject();
|
| | if (linked->getExtensionByType<App::GeoFeatureGroupExtension>(true)) {
|
| | container = const_cast<DocumentObject*>(this);
|
| | }
|
| | else if (auto grp = App::GeoFeatureGroupExtension::getGroupOfObject(linked)) {
|
| | container = grp;
|
| | lastChild = true;
|
| | }
|
| | }
|
| | for (auto pos = sub.find('.'); pos != std::string::npos; pos = sub.find('.', pos + 1)) {
|
| | char subTail = sub[pos + 1];
|
| | sub[pos + 1] = '\0';
|
| | auto sobj = getSubObject(sub.c_str());
|
| | if (!sobj || !sobj->isAttachedToDocument()) {
|
| | continue;
|
| | }
|
| |
|
| | if (flatten) {
|
| | res = getSubObjectListFlatten(res, subsizes, sobj, &container, lastChild);
|
| | }
|
| | res.push_back(sobj);
|
| | if (subsizes) {
|
| | subsizes->push_back((int)pos + 1);
|
| | }
|
| | sub[pos + 1] = subTail;
|
| | }
|
| | return res;
|
| | }
|
| |
|
| | std::vector<std::string> DocumentObject::getSubObjects(int reason) const
|
| | {
|
| | std::vector<std::string> ret;
|
| | auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : exts) {
|
| | if (ext->extensionGetSubObjects(ret, reason)) {
|
| | return ret;
|
| | }
|
| | }
|
| | return ret;
|
| | }
|
| |
|
| | std::vector<std::pair<App::DocumentObject*, std::string>>
|
| | DocumentObject::getParents(int depth) const
|
| | {
|
| | std::vector<std::pair<App::DocumentObject*, std::string>> ret;
|
| | if (!isAttachedToDocument() || !GetApplication().checkLinkDepth(depth, MessageOption::Throw)) {
|
| | return ret;
|
| | }
|
| |
|
| | std::string name(getNameInDocument());
|
| | name += ".";
|
| | for (auto parent : getInList()) {
|
| | if (!parent || !parent->isAttachedToDocument()) {
|
| | continue;
|
| | }
|
| |
|
| | if (!parent->hasChildElement()
|
| | && !parent->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId())) {
|
| | continue;
|
| | }
|
| |
|
| | if (!parent->getSubObject(name.c_str())) {
|
| | continue;
|
| | }
|
| |
|
| | auto links = GetApplication().getLinksTo(parent, App::GetLinkRecursive);
|
| | links.insert(parent);
|
| |
|
| | for (auto parent : links) {
|
| | auto parents = parent->getParents(depth + 1);
|
| | if (parents.empty()) {
|
| | parents.emplace_back(parent, std::string());
|
| | }
|
| |
|
| | for (auto& v : parents) {
|
| | ret.emplace_back(v.first, v.second + name);
|
| | }
|
| | }
|
| | }
|
| |
|
| | return ret;
|
| | }
|
| |
|
| | App::DocumentObject* DocumentObject::getFirstParent() const
|
| | {
|
| | for (auto obj : getInList()) {
|
| | if (obj->hasExtension(App::GroupExtension::getExtensionClassTypeId(), true)) {
|
| | return obj;
|
| | }
|
| | }
|
| |
|
| | return nullptr;
|
| | }
|
| |
|
| | DocumentObject* DocumentObject::getLinkedObject(bool recursive,
|
| | Base::Matrix4D* mat,
|
| | bool transform,
|
| | int depth) const
|
| | {
|
| | DocumentObject* ret = nullptr;
|
| | auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : exts) {
|
| | if (ext->extensionGetLinkedObject(ret, recursive, mat, transform, depth)) {
|
| | return ret;
|
| | }
|
| | }
|
| | if (transform && mat) {
|
| | auto pla = freecad_cast<PropertyPlacement*>(getPropertyByName("Placement"));
|
| | if (pla) {
|
| | *mat *= pla->getValue().toMatrix();
|
| | }
|
| | }
|
| | return const_cast<DocumentObject*>(this);
|
| | }
|
| |
|
| | void DocumentObject::Save(Base::Writer& writer) const
|
| | {
|
| | if (this->isFreezed()) {
|
| | throw Base::AbortException("At least one object is frozen, unable to save.");
|
| | }
|
| |
|
| | if (this->isAttachedToDocument()){
|
| | writer.ObjectName = this->getNameInDocument();
|
| | }
|
| | App::ExtensionContainer::Save(writer);
|
| | }
|
| |
|
| | |
| | |
| | |
| | |
| |
|
| |
|
| | void DocumentObject::setExpression(const ObjectIdentifier& path, std::shared_ptr<Expression> expr)
|
| | {
|
| | ExpressionEngine.setValue(path, std::move(expr));
|
| | }
|
| |
|
| | |
| | |
| | |
| |
|
| |
|
| | void DocumentObject::clearExpression(const ObjectIdentifier& path)
|
| | {
|
| | setExpression(path, std::shared_ptr<Expression>());
|
| | }
|
| |
|
| | |
| | |
| | |
| | |
| |
|
| |
|
| | const PropertyExpressionEngine::ExpressionInfo
|
| | DocumentObject::getExpression(const ObjectIdentifier& path) const
|
| | {
|
| | boost::any value = ExpressionEngine.getPathValue(path);
|
| |
|
| | if (value.type() == typeid(PropertyExpressionEngine::ExpressionInfo)) {
|
| | return boost::any_cast<PropertyExpressionEngine::ExpressionInfo>(value);
|
| | }
|
| | else {
|
| | return PropertyExpressionEngine::ExpressionInfo();
|
| | }
|
| | }
|
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | void DocumentObject::renameObjectIdentifiers(
|
| | const std::map<ObjectIdentifier, ObjectIdentifier>& paths)
|
| | {
|
| | ExpressionEngine.renameObjectIdentifiers(paths);
|
| | }
|
| |
|
| | void DocumentObject::onDocumentRestored()
|
| | {
|
| |
|
| | auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : vector) {
|
| | ext->onExtendedDocumentRestored();
|
| | }
|
| | if (Visibility.testStatus(Property::Output)) {
|
| | Visibility.setStatus(Property::NoModify, true);
|
| | }
|
| | }
|
| |
|
| | void DocumentObject::restoreFinished()
|
| | {
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | std::vector<App::Property*> props;
|
| | getPropertyList(props);
|
| | for (auto prop : props) {
|
| | prop->afterRestore();
|
| | }
|
| | }
|
| |
|
| | void DocumentObject::onUndoRedoFinished()
|
| | {}
|
| |
|
| | void DocumentObject::onSettingDocument()
|
| | {
|
| |
|
| | auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : vector) {
|
| | ext->onExtendedSettingDocument();
|
| | }
|
| | }
|
| |
|
| | void DocumentObject::setupObject()
|
| | {
|
| |
|
| | auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : vector) {
|
| | ext->onExtendedSetupObject();
|
| | }
|
| | }
|
| |
|
| | void DocumentObject::unsetupObject()
|
| | {
|
| |
|
| | auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
|
| | for (auto ext : vector) {
|
| | ext->onExtendedUnsetupObject();
|
| | }
|
| | }
|
| |
|
| | void App::DocumentObject::_removeBackLink(DocumentObject* rmvObj)
|
| | {
|
| |
|
| |
|
| | auto it = std::ranges::find(_inList, rmvObj);
|
| | if (it != _inList.end()) {
|
| | _inList.erase(it);
|
| | }
|
| | }
|
| |
|
| | void App::DocumentObject::_addBackLink(DocumentObject* newObj)
|
| | {
|
| |
|
| |
|
| |
|
| |
|
| | _inList.push_back(newObj);
|
| | }
|
| |
|
| | int DocumentObject::setElementVisible(const char* element, bool visible)
|
| | {
|
| | for (auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
| | int ret = ext->extensionSetElementVisible(element, visible);
|
| | if (ret >= 0) {
|
| | return ret;
|
| | }
|
| | }
|
| |
|
| | return -1;
|
| | }
|
| |
|
| | int DocumentObject::isElementVisible(const char* element) const
|
| | {
|
| | for (auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
| | int ret = ext->extensionIsElementVisible(element);
|
| | if (ret >= 0) {
|
| | return ret;
|
| | }
|
| | }
|
| |
|
| | return -1;
|
| | }
|
| |
|
| | bool DocumentObject::hasChildElement() const
|
| | {
|
| | for (auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
|
| | if (ext->extensionHasChildElement()) {
|
| | return true;
|
| | }
|
| | }
|
| | return false;
|
| | }
|
| |
|
| | DocumentObject* DocumentObject::resolve(const char* subname,
|
| | App::DocumentObject** parent,
|
| | std::string* childName,
|
| | const char** subElement,
|
| | PyObject** pyObj,
|
| | Base::Matrix4D* pmat,
|
| | bool transform,
|
| | int depth) const
|
| | {
|
| | auto self = const_cast<DocumentObject*>(this);
|
| | if (parent) {
|
| | *parent = nullptr;
|
| | }
|
| | if (subElement) {
|
| | *subElement = nullptr;
|
| | }
|
| |
|
| | auto obj = getSubObject(subname, pyObj, pmat, transform, depth);
|
| | if (!obj || !subname || *subname == 0) {
|
| | return self;
|
| | }
|
| |
|
| | if (!parent && !subElement) {
|
| | return obj;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | const char* dot = nullptr;
|
| | if (Data::isMappedElement(subname) || !(dot = strrchr(subname, '.')) || dot == subname) {
|
| | if (subElement) {
|
| | *subElement = dot ? dot + 1 : subname;
|
| | }
|
| | return obj;
|
| | }
|
| |
|
| | if (parent) {
|
| | *parent = self;
|
| | }
|
| |
|
| | bool elementMapChecked = false;
|
| | const char* lastDot = dot;
|
| | for (--dot;; --dot) {
|
| |
|
| | if (*dot == '.' || dot == subname) {
|
| |
|
| |
|
| |
|
| | if (!elementMapChecked) {
|
| | elementMapChecked = true;
|
| | const char* sub = dot == subname ? dot : dot + 1;
|
| | if (Data::isMappedElement(sub)) {
|
| | lastDot = dot;
|
| | if (dot == subname) {
|
| | break;
|
| | }
|
| | else {
|
| | continue;
|
| | }
|
| | }
|
| | }
|
| | if (dot == subname) {
|
| | break;
|
| | }
|
| | auto sobj = getSubObject(std::string(subname, dot - subname + 1).c_str());
|
| | if (sobj != obj) {
|
| | if (parent) {
|
| |
|
| |
|
| | if (!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(), false)) {
|
| | *parent = sobj;
|
| | break;
|
| | }
|
| | for (auto ddot = dot - 1; ddot != subname; --ddot) {
|
| | if (*ddot != '.') {
|
| | continue;
|
| | }
|
| | auto sobj = getSubObject(std::string(subname, ddot - subname + 1).c_str());
|
| | if (!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(), false)) {
|
| | *parent = sobj;
|
| | break;
|
| | }
|
| | }
|
| | }
|
| | break;
|
| | }
|
| | }
|
| | }
|
| | if (childName && lastDot != dot) {
|
| | if (*dot == '.') {
|
| | ++dot;
|
| | }
|
| | const char* nextDot = strchr(dot, '.');
|
| | assert(nextDot);
|
| | *childName = std::string(dot, nextDot - dot);
|
| | }
|
| | if (subElement) {
|
| | *subElement = *lastDot == '.' ? lastDot + 1 : lastDot;
|
| | }
|
| | return obj;
|
| | }
|
| |
|
| | DocumentObject* DocumentObject::resolveRelativeLink(std::string& subname,
|
| | DocumentObject*& link,
|
| | std::string& linkSub) const
|
| | {
|
| | if (!link || !link->isAttachedToDocument() || !isAttachedToDocument()) {
|
| | return nullptr;
|
| | }
|
| | auto ret = const_cast<DocumentObject*>(this);
|
| | if (link != ret) {
|
| | auto sub = subname.c_str();
|
| | auto nextsub = sub;
|
| | for (auto dot = strchr(nextsub, '.'); dot; nextsub = dot + 1, dot = strchr(nextsub, '.')) {
|
| | std::string subcheck(sub, nextsub - sub);
|
| | subcheck += link->getNameInDocument();
|
| | subcheck += '.';
|
| | if (getSubObject(subcheck.c_str()) == link) {
|
| | ret = getSubObject(std::string(sub, dot + 1 - sub).c_str());
|
| | if (!ret) {
|
| | return nullptr;
|
| | }
|
| | subname = std::string(dot + 1);
|
| | break;
|
| | }
|
| | }
|
| | return ret;
|
| | }
|
| |
|
| | size_t pos = 0, linkPos = 0;
|
| | std::string linkssub, ssub;
|
| | do {
|
| | linkPos = linkSub.find('.', linkPos);
|
| | if (linkPos == std::string::npos) {
|
| | link = nullptr;
|
| | return nullptr;
|
| | }
|
| | ++linkPos;
|
| | pos = subname.find('.', pos);
|
| | if (pos == std::string::npos) {
|
| | subname.clear();
|
| | ret = nullptr;
|
| | break;
|
| | }
|
| | ++pos;
|
| | } while (subname.compare(0, pos, linkSub, 0, linkPos) == 0);
|
| |
|
| | if (pos != std::string::npos) {
|
| | ret = getSubObject(subname.substr(0, pos).c_str());
|
| | if (!ret) {
|
| | link = nullptr;
|
| | return nullptr;
|
| | }
|
| | subname = subname.substr(pos);
|
| | }
|
| | if (linkPos) {
|
| | link = link->getSubObject(linkSub.substr(0, linkPos).c_str());
|
| | if (!link) {
|
| | return nullptr;
|
| | }
|
| | linkSub = linkSub.substr(linkPos);
|
| | }
|
| | return ret;
|
| | }
|
| |
|
| | bool DocumentObject::adjustRelativeLinks(const std::set<App::DocumentObject*>& inList,
|
| | std::set<App::DocumentObject*>* visited)
|
| | {
|
| | if (visited) {
|
| | visited->insert(this);
|
| | }
|
| |
|
| | bool touched = false;
|
| | std::vector<Property*> props;
|
| | getPropertyList(props);
|
| | for (auto prop : props) {
|
| | auto linkProp = freecad_cast<PropertyLinkBase*>(prop);
|
| | if (linkProp && linkProp->adjustLink(inList)) {
|
| | touched = true;
|
| | }
|
| | }
|
| | if (visited) {
|
| | for (auto obj : getOutList()) {
|
| | if (!visited->count(obj)) {
|
| | if (obj->adjustRelativeLinks(inList, visited)) {
|
| | touched = true;
|
| | }
|
| | }
|
| | }
|
| | }
|
| | return touched;
|
| | }
|
| |
|
| | std::string DocumentObject::getElementMapVersion(const App::Property* _prop, bool restored) const
|
| | {
|
| | auto prop = freecad_cast<const PropertyComplexGeoData*>(_prop);
|
| | if (!prop) {
|
| | return std::string();
|
| | }
|
| | return prop->getElementMapVersion(restored);
|
| | }
|
| |
|
| | bool DocumentObject::checkElementMapVersion(const App::Property* _prop, const char* ver) const
|
| | {
|
| | auto prop = freecad_cast<const PropertyComplexGeoData*>(_prop);
|
| | if (!prop) {
|
| | return false;
|
| | }
|
| | return prop->checkElementMapVersion(ver);
|
| | }
|
| |
|
| | const std::string& DocumentObject::hiddenMarker()
|
| | {
|
| | static std::string marker("!hide");
|
| | return marker;
|
| | }
|
| |
|
| | const char* DocumentObject::hasHiddenMarker(const char* subname)
|
| | {
|
| | if (!subname) {
|
| | return nullptr;
|
| | }
|
| | const char* marker = strrchr(subname, '.');
|
| | if (!marker) {
|
| | marker = subname;
|
| | }
|
| | else {
|
| | ++marker;
|
| | }
|
| | return hiddenMarker() == marker ? marker : nullptr;
|
| | }
|
| |
|
| | bool DocumentObject::redirectSubName(std::ostringstream&, DocumentObject*, DocumentObject*) const
|
| | {
|
| | return false;
|
| | }
|
| |
|
| | void DocumentObject::onPropertyStatusChanged(const Property& prop, unsigned long oldStatus)
|
| | {
|
| | (void)oldStatus;
|
| | if (!Document::isAnyRestoring() && isAttachedToDocument() && getDocument()) {
|
| | getDocument()->signalChangePropertyEditor(*getDocument(), prop);
|
| | }
|
| | }
|
| |
|
| | Base::Placement DocumentObject::getPlacementOf(const std::string& sub, DocumentObject* targetObj)
|
| | {
|
| | Base::Placement plc;
|
| | auto* propPlacement = freecad_cast<App::PropertyPlacement*>(getPropertyByName("Placement"));
|
| | if (propPlacement) {
|
| |
|
| | plc = propPlacement->getValue();
|
| | }
|
| |
|
| | std::vector<std::string> names = Base::Tools::splitSubName(sub);
|
| |
|
| | if (names.empty() || this == targetObj) {
|
| | return plc;
|
| | }
|
| |
|
| | DocumentObject* subObj = getDocument()->getObject(names.front().c_str());
|
| |
|
| | if (!subObj) {
|
| | return plc;
|
| | }
|
| |
|
| | std::vector<std::string> newNames(names.begin() + 1, names.end());
|
| | std::string newSub = Base::Tools::joinList(newNames, ".");
|
| |
|
| | return plc * subObj->getPlacementOf(newSub, targetObj);
|
| | }
|
| |
|
| | Base::Placement DocumentObject::getPlacement() const
|
| | {
|
| | Base::Placement plc;
|
| | if (auto* prop = getPlacementProperty()) {
|
| | plc = prop->getValue();
|
| | }
|
| | return plc;
|
| | }
|
| |
|
| | App::PropertyPlacement* DocumentObject::getPlacementProperty() const
|
| | {
|
| | if (auto linkExtension = getExtensionByType<App::LinkBaseExtension>(true)) {
|
| | if (auto linkPlacementProp = linkExtension->getLinkPlacementProperty()) {
|
| | return linkPlacementProp;
|
| | }
|
| |
|
| | return linkExtension->getPlacementProperty();
|
| | }
|
| |
|
| | return getPropertyByName<App::PropertyPlacement>("Placement");
|
| | }
|
| |
|
| |
|
| |
|