App: fix external document loading

The problem happens when partial loading is enabled. If document A
contains a link to some object in document B, it will load B as partial
document with only that object and its necessary dependencies. But if
document A contains another link to some object in document C which also
has a link to some object in document B, the link in document C may not
be restored, because document B is partially loaded without the linked
object. This patch will check for this case and reload document B for
more objects.

See an example reported in
https://forum.freecadweb.org/viewtopic.php?p=495078#p495078
This commit is contained in:
Zheng, Lei
2021-08-02 11:38:01 +08:00
parent 39e2381f29
commit ad9ad30c00
7 changed files with 311 additions and 166 deletions

View File

@@ -2458,6 +2458,7 @@ class App::DocInfo :
public:
typedef boost::signals2::scoped_connection Connection;
Connection connFinishRestoreDocument;
Connection connPendingReloadDocument;
Connection connDeleteDocument;
Connection connSaveDocument;
Connection connDeletedObject;
@@ -2589,6 +2590,7 @@ public:
FC_LOG("deinit " << (pcDoc?pcDoc->getName():filePath()));
assert(links.empty());
connFinishRestoreDocument.disconnect();
connPendingReloadDocument.disconnect();
connDeleteDocument.disconnect();
connSaveDocument.disconnect();
connDeletedObject.disconnect();
@@ -2606,6 +2608,8 @@ public:
App::Application &app = App::GetApplication();
connFinishRestoreDocument = app.signalFinishRestoreDocument.connect(
boost::bind(&DocInfo::slotFinishRestoreDocument,this,bp::_1));
connPendingReloadDocument = app.signalPendingReloadDocument.connect(
boost::bind(&DocInfo::slotFinishRestoreDocument,this,bp::_1));
connDeleteDocument = app.signalDeleteDocument.connect(
boost::bind(&DocInfo::slotDeleteDocument,this,bp::_1));
connSaveDocument = app.signalSaveDocument.connect(
@@ -2617,6 +2621,8 @@ public:
else{
for(App::Document *doc : App::GetApplication().getDocuments()) {
if(getFullPath(doc->getFileName()) == fullpath) {
if(doc->testStatus(App::Document::PartialDoc) && !doc->getObject(objName))
break;
attach(doc);
return;
}
@@ -2642,22 +2648,36 @@ public:
continue;
}
auto obj = doc->getObject(link->objectName.c_str());
if(!obj)
if(obj)
link->restoreLink(obj);
else if (doc->testStatus(App::Document::PartialDoc)) {
App::GetApplication().addPendingDocument(
doc->FileName.getValue(),
link->objectName.c_str(),
false);
FC_WARN("reloading partial document '" << doc->FileName.getValue()
<< "' due to object " << link->objectName);
} else
FC_WARN("object '" << link->objectName << "' not found in document '"
<< doc->getName() << "'");
else
link->restoreLink(obj);
}
for(auto &v : parentLinks) {
v.first->setFlag(PropertyLinkBase::LinkRestoring);
v.first->aboutToSetValue();
for(auto link : v.second) {
auto obj = doc->getObject(link->objectName.c_str());
if(!obj)
if(obj)
link->restoreLink(obj);
else if (doc->testStatus(App::Document::PartialDoc)) {
App::GetApplication().addPendingDocument(
doc->FileName.getValue(),
link->objectName.c_str(),
false);
FC_WARN("reloading partial document '" << doc->FileName.getValue()
<< "' due to object " << link->objectName);
} else
FC_WARN("object '" << link->objectName << "' not found in document '"
<< doc->getName() << "'");
else
link->restoreLink(obj);
}
v.first->hasSetValue();
v.first->setFlag(PropertyLinkBase::LinkRestoring,false);
@@ -2723,16 +2743,17 @@ public:
}
}
// time stamp changed, touch the linking document. Unfortunately, there
// is no way to setModfied() for an App::Document. We don't want to touch
// all PropertyXLink for a document, because the linked object is
// potentially unchanged. So we just touch at most one.
// time stamp changed, touch the linking document.
std::set<Document*> docs;
for(auto link : links) {
auto linkdoc = static_cast<DocumentObject*>(link->getContainer())->getDocument();
auto ret = docs.insert(linkdoc);
if(ret.second && !linkdoc->isTouched())
link->touch();
if(ret.second) {
// This will signal the Gui::Document to call setModified();
FC_LOG("touch document " << linkdoc->getName()
<< " on time stamp change of " << link->getFullName());
linkdoc->Comment.touch();
}
}
}
@@ -3473,7 +3494,12 @@ PropertyXLink::getDocumentOutList(App::Document *doc) {
std::map<App::Document*,std::set<App::Document*> > ret;
for(auto &v : _DocInfoMap) {
for(auto link : v.second->links) {
if(!v.second->pcDoc) continue;
if(!v.second->pcDoc
|| link->getScope() == LinkScope::Hidden
|| link->testStatus(Property::PropTransient)
|| link->testStatus(Property::Transient)
|| link->testStatus(Property::PropNoPersist))
continue;
auto obj = dynamic_cast<App::DocumentObject*>(link->getContainer());
if(!obj || !obj->getNameInDocument() || !obj->getDocument())
continue;
@@ -3493,6 +3519,11 @@ PropertyXLink::getDocumentInList(App::Document *doc) {
continue;
auto &docs = ret[v.second->pcDoc];
for(auto link : v.second->links) {
if(link->getScope() == LinkScope::Hidden
|| link->testStatus(Property::PropTransient)
|| link->testStatus(Property::Transient)
|| link->testStatus(Property::PropNoPersist))
continue;
auto obj = dynamic_cast<App::DocumentObject*>(link->getContainer());
if(obj && obj->getNameInDocument() && obj->getDocument())
docs.insert(obj->getDocument());
@@ -4460,12 +4491,12 @@ void PropertyXLinkContainer::breakLink(App::DocumentObject *obj, bool clear) {
}
int PropertyXLinkContainer::checkRestore(std::string *msg) const {
if(_LinkRestored)
return 1;
for(auto &v : _XLinks) {
int res = v.second->checkRestore(msg);
if(res)
return res;
if(_LinkRestored) {
for(auto &v : _XLinks) {
int res = v.second->checkRestore(msg);
if(res)
return res;
}
}
return 0;
}