diff --git a/src/App/VRMLObject.cpp b/src/App/VRMLObject.cpp index b4f1edae0..8e061d989 100644 --- a/src/App/VRMLObject.cpp +++ b/src/App/VRMLObject.cpp @@ -27,7 +27,12 @@ #endif #include "VRMLObject.h" +#include "Document.h" #include "DocumentObjectPy.h" +#include +#include +#include +#include using namespace App; @@ -37,6 +42,8 @@ PROPERTY_SOURCE(App::VRMLObject, App::GeoFeature) VRMLObject::VRMLObject() { ADD_PROPERTY_TYPE(VrmlFile,(0),"",Prop_None,"Included file with the VRML definition"); + ADD_PROPERTY_TYPE(Urls,(""),"",static_cast(Prop_ReadOnly|Prop_Output),"Included file with the VRML definition"); + Urls.setSize(0); } VRMLObject::~VRMLObject() @@ -56,3 +63,92 @@ PyObject *VRMLObject::getPyObject() } return Py::new_reference_to(PythonObject); } + +void VRMLObject::Save (Base::Writer &writer) const +{ + App::GeoFeature::Save(writer); + + // if the VRML file has some inline files then store them inside the project file + if (Urls.getSize() > 0) { + const std::vector& urls = Urls.getValues(); + writer.incInd(); + writer.Stream() << writer.ind() << "" << std::endl; + writer.incInd(); + std::string intname = this->getNameInDocument(); + for (std::vector::const_iterator it = urls.begin(); it != urls.end(); ++it) { + Base::FileInfo fi(*it); + // make sure to put the VRML files into a sub-directory + std::string output = intname + "/" + fi.fileName(); + output = writer.addFile(output.c_str(), this); + writer.Stream() << writer.ind() << "" << std::endl; + } + writer.decInd(); + writer.Stream() << writer.ind() << "" << std::endl; + writer.decInd(); + } + + this->index = 0; +} + +void VRMLObject::Restore(Base::XMLReader &reader) +{ + App::GeoFeature::Restore(reader); + + // are there inline files + if (Urls.getSize() > 0) { + reader.readElement("UrlList"); + int count = reader.getAttributeAsInteger("count"); + for(int i = 0; i < count; i++) { + reader.readElement("Url"); + std::string value = reader.getAttribute("value"); + reader.addFile(value.c_str(), this); + } + + reader.readEndElement("UrlList"); + } + + this->index = 0; +} + +void VRMLObject::SaveDocFile (Base::Writer &writer) const +{ + // store the inline files of the VRML file + if (this->index < Urls.getSize()) { + std::string url = Urls[this->index]; + this->index++; + + Base::FileInfo fi(url); + Base::ifstream file(fi, std::ios::in | std::ios::binary); + if (file) { + writer.Stream() << file.rdbuf(); + } + } +} + +void VRMLObject::RestoreDocFile(Base::Reader &reader) +{ + if (this->index < Urls.getSize()) { + std::string path = getDocument()->TransientDir.getValue(); + std::string url = Urls[this->index]; + std::string intname = this->getNameInDocument(); + + Base::FileInfo fi(url); + std::string subdir = path + "/" + intname; + Base::FileInfo(subdir).createDirectory(); + url = subdir + "/" + fi.fileName(); + fi.setFile(url); + Urls.set1Value(this->index, url); + this->index++; + + Base::ofstream file(fi, std::ios::out | std::ios::binary); + if (file) { + reader >> file.rdbuf(); + file.close(); + } + + // after restoring all inline files reload the VRML file + if (this->index == Urls.getSize()) { + VrmlFile.touch(); + } + } +} diff --git a/src/App/VRMLObject.h b/src/App/VRMLObject.h index 311c36e97..e89707b61 100644 --- a/src/App/VRMLObject.h +++ b/src/App/VRMLObject.h @@ -49,9 +49,16 @@ public: } virtual short mustExecute(void) const; virtual PyObject *getPyObject(void); + virtual void Save (Base::Writer &writer) const; + virtual void Restore(Base::XMLReader &reader); + virtual void SaveDocFile (Base::Writer &writer) const; + virtual void RestoreDocFile(Base::Reader &reader); PropertyFileIncluded VrmlFile; + PropertyStringList Urls; +private: + mutable int index; }; } //namespace App diff --git a/src/Gui/ViewProviderVRMLObject.cpp b/src/Gui/ViewProviderVRMLObject.cpp index 3f5b9f5db..9fe664a58 100644 --- a/src/Gui/ViewProviderVRMLObject.cpp +++ b/src/Gui/ViewProviderVRMLObject.cpp @@ -26,12 +26,24 @@ #ifndef _PreComp_ # include # include +# include +# include # include # include # include # include #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "ViewProviderVRMLObject.h" #include "SoFCSelection.h" #include @@ -81,6 +93,116 @@ std::vector ViewProviderVRMLObject::getDisplayModes(void) const return StrList; } +template +void ViewProviderVRMLObject::getResourceFile(SoNode* node, std::list& resources) +{ + SoSearchAction sa; + sa.setType(T::getClassTypeId()); + sa.setInterest(SoSearchAction::ALL); + sa.setSearchingAll(true); + sa.apply(node); + const SoPathList & pathlist = sa.getPaths(); + for (int i = 0; i < pathlist.getLength(); i++ ) { + SoFullPath * path = static_cast(pathlist[i]); + if (path->getTail()->isOfType(T::getClassTypeId())) { + T * tex = static_cast(path->getTail()); + for (int j = 0; j < tex->url.getNum(); j++) { + this->addResource(tex->url[j], resources); + } + } + } +} + +// Special handling for SoVRMLBackground +template<> +void ViewProviderVRMLObject::getResourceFile(SoNode* node, std::list& resources) +{ + SoSearchAction sa; + sa.setType(SoVRMLBackground::getClassTypeId()); + sa.setInterest(SoSearchAction::ALL); + sa.setSearchingAll(true); + sa.apply(node); + const SoPathList & pathlist = sa.getPaths(); + for (int i = 0; i < pathlist.getLength(); i++ ) { + SoFullPath * path = static_cast(pathlist[i]); + if (path->getTail()->isOfType(SoVRMLBackground::getClassTypeId())) { + SoVRMLBackground * vrml = static_cast(path->getTail()); + // backUrl + for (int j = 0; j < vrml->backUrl.getNum(); j++) { + addResource(vrml->backUrl[j], resources); + } + // bottomUrl + for (int j = 0; j < vrml->bottomUrl.getNum(); j++) { + addResource(vrml->bottomUrl[j], resources); + } + // frontUrl + for (int j = 0; j < vrml->frontUrl.getNum(); j++) { + addResource(vrml->frontUrl[j], resources); + } + // leftUrl + for (int j = 0; j < vrml->leftUrl.getNum(); j++) { + addResource(vrml->leftUrl[j], resources); + } + // rightUrl + for (int j = 0; j < vrml->rightUrl.getNum(); j++) { + addResource(vrml->rightUrl[j], resources); + } + // topUrl + for (int j = 0; j < vrml->topUrl.getNum(); j++) { + addResource(vrml->topUrl[j], resources); + } + } + } +} + +void ViewProviderVRMLObject::addResource(const SbString& url, std::list& resources) +{ + SbString found = SoInput::searchForFile(url, SoInput::getDirectories(), SbStringList()); + Base::FileInfo fi(found.getString()); + if (fi.exists()) { + // add the resource file if not yet listed + if (std::find(resources.begin(), resources.end(), found.getString()) == resources.end()) { + resources.push_back(found.getString()); + } + } +} + +void ViewProviderVRMLObject::getLocalResources(SoNode* node, std::list& resources) +{ + // search for SoVRMLInline files + SoSearchAction sa; + sa.setType(SoVRMLInline::getClassTypeId()); + sa.setInterest(SoSearchAction::ALL); + sa.setSearchingAll(true); + sa.apply(node); + + const SoPathList & pathlist = sa.getPaths(); + for (int i = 0; i < pathlist.getLength(); i++ ) { + SoPath * path = pathlist[i]; + SoVRMLInline * vrml = static_cast(path->getTail()); + const SbString& url = vrml->getFullURLName(); + if (url.getLength() > 0) { + // add the resource file if not yet listed + if (std::find(resources.begin(), resources.end(), url.getString()) == resources.end()) { + resources.push_back(url.getString()); + } + + // if the resource file could be loaded check if it references further resources + if (vrml->getChildData()) { + getLocalResources(vrml->getChildData(), resources); + } + } + } + + // search for SoVRMLImageTexture, ... files + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); +} + void ViewProviderVRMLObject::updateData(const App::Property* prop) { App::VRMLObject* ivObj = static_cast(pcObject); @@ -93,14 +215,31 @@ void ViewProviderVRMLObject::updateData(const App::Property* prop) pcVRML->removeAllChildren(); if (!fn.isEmpty() && file.open(QFile::ReadOnly)) { QFileInfo fi(fn); - QByteArray path = fi.absolutePath().toUtf8(); + QByteArray filepath = fi.absolutePath().toUtf8(); + QByteArray subpath = filepath + "/" + ivObj->getNameInDocument(); + // Add this to the search path in order to read inline files - SoInput::addDirectoryFirst(path.constData()); + SoInput::addDirectoryFirst(filepath.constData()); + SoInput::addDirectoryFirst(subpath.constData()); + + // Read in the file QByteArray buffer = file.readAll(); in.setBuffer((void *)buffer.constData(), buffer.length()); SoSeparator * node = SoDB::readAll(&in); - if (node) pcVRML->addChild(node); - SoInput::removeDirectory(path.constData()); + + if (node) { + pcVRML->addChild(node); + + std::list urls; + getLocalResources(node, urls); + if (!urls.empty() && ivObj->Urls.getSize() == 0) { + std::vector res; + res.insert(res.end(), urls.begin(), urls.end()); + ivObj->Urls.setValues(res); + } + } + SoInput::removeDirectory(filepath.constData()); + SoInput::removeDirectory(subpath.constData()); } } else if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId()) && diff --git a/src/Gui/ViewProviderVRMLObject.h b/src/Gui/ViewProviderVRMLObject.h index 81022ef75..39a024491 100644 --- a/src/Gui/ViewProviderVRMLObject.h +++ b/src/Gui/ViewProviderVRMLObject.h @@ -26,6 +26,7 @@ #include "ViewProviderDocumentObject.h" +class SbString; namespace Gui { @@ -46,6 +47,9 @@ public: void setDisplayMode(const char* ModeName); std::vector getDisplayModes() const; void updateData(const App::Property*); + void getLocalResources(SoNode*, std::list&); + void addResource(const SbString&, std::list&); + template void getResourceFile(SoNode*, std::list&); protected: SoFCSelection * pcVRML;