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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user