diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index a6652e4ef..70843f039 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1702,6 +1702,7 @@ void Application::runApplication(void) if (!hDocGrp->GetBool("AutoSaveEnabled", true)) timeout = 0; AutoSaver::instance()->setTimeout(timeout * 60000); + AutoSaver::instance()->setCompressed(hDocGrp->GetBool("AutoSaveCompressed", false)); // set toolbar icon size ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General"); @@ -1915,7 +1916,7 @@ void Application::checkForPreviousCrashes() countDeletedDocs++; } // search for the existance of a recovery file - else if (doc_dir.exists(QLatin1String("fc_recovery_file.fcstd"))) { + else if (doc_dir.exists(QLatin1String("fc_recovery_file.xml"))) { // store the transient directory in case it's not empty restoreDocFiles << *it; } diff --git a/src/Gui/AutoSaver.cpp b/src/Gui/AutoSaver.cpp index 16ad020ec..8434325a9 100644 --- a/src/Gui/AutoSaver.cpp +++ b/src/Gui/AutoSaver.cpp @@ -52,7 +52,7 @@ using namespace Gui; AutoSaver* AutoSaver::self = 0; AutoSaver::AutoSaver(QObject* parent) - : QObject(parent), timeout(900000) + : QObject(parent), timeout(900000), compressed(false) { App::GetApplication().signalNewDocument.connect(boost::bind(&AutoSaver::slotCreateDocument, this, _1)); App::GetApplication().signalDeleteDocument.connect(boost::bind(&AutoSaver::slotDeleteDocument, this, _1)); @@ -82,17 +82,25 @@ void AutoSaver::setTimeout(int ms) } } +void AutoSaver::setCompressed(bool on) +{ + this->compressed = on; +} + void AutoSaver::slotCreateDocument(const App::Document& Doc) { std::string name = Doc.getName(); int id = timeout > 0 ? startTimer(timeout) : 0; AutoSaveProperty* as = new AutoSaveProperty(&Doc); as->timerId = id; - std::string dirName = Doc.TransientDir.getValue(); - dirName += "/fc_recovery_files"; - Base::FileInfo fi(dirName); - fi.createDirectory(); - as->dirName = dirName; + + if (!this->compressed) { + std::string dirName = Doc.TransientDir.getValue(); + dirName += "/fc_recovery_files"; + Base::FileInfo fi(dirName); + fi.createDirectory(); + as->dirName = dirName; + } saverMap.insert(std::make_pair(name, as)); } @@ -113,6 +121,11 @@ void AutoSaver::saveDocument(const std::string& name, AutoSaveProperty& saver) Gui::WaitCursor wc; App::Document* doc = App::GetApplication().getDocument(name.c_str()); if (doc) { + // Set the document's current transient directory + std::string dirName = doc->TransientDir.getValue(); + dirName += "/fc_recovery_files"; + saver.dirName = dirName; + // Write recovery meta file QFile file(QString::fromLatin1("%1/fc_recovery_file.xml") .arg(QString::fromUtf8(doc->TransientDir.getValue()))); @@ -128,10 +141,6 @@ void AutoSaver::saveDocument(const std::string& name, AutoSaveProperty& saver) file.close(); } - std::string fn = doc->TransientDir.getValue(); - fn += "/fc_recovery_file.fcstd"; - Base::FileInfo tmp(fn); - // make sure to tmp. disable saving thumbnails because this causes trouble if the // associated 3d view is not active Base::Reference hGrp = App::GetApplication().GetParameterGroupByPath @@ -139,25 +148,18 @@ void AutoSaver::saveDocument(const std::string& name, AutoSaveProperty& saver) bool save = hGrp->GetBool("SaveThumbnail",false); hGrp->SetBool("SaveThumbnail",false); - //Gui::StatusWidget* sw = new Gui::StatusWidget(qApp->activeWindow()); - //sw->setStatusText(tr("Please wait until the AutoRecovery file has been saved...")); - //sw->show(); getMainWindow()->showMessage(tr("Please wait until the AutoRecovery file has been saved..."), 5000); - qApp->processEvents(); + //qApp->processEvents(); // open extra scope to close ZipWriter properly Base::StopWatch watch; watch.start(); { - Base::ofstream file(tmp, std::ios::out | std::ios::binary); - if (file.is_open()) { - Base::ZipWriter writer(file); - //RecoveryWriter writer(saver); + if (!this->compressed) { + RecoveryWriter writer(saver); if (hGrp->GetBool("SaveBinaryBrep", true)) writer.setMode("BinaryBrep"); - writer.setComment("AutoRecovery file"); - writer.setLevel(1); // apparently the fastest compression writer.putNextEntry("Document.xml"); doc->Save(writer); @@ -168,10 +170,31 @@ void AutoSaver::saveDocument(const std::string& name, AutoSaveProperty& saver) // write additional files writer.writeFiles(); } - } + else { + std::string fn = doc->TransientDir.getValue(); + fn += "/fc_recovery_file.fcstd"; + Base::FileInfo tmp(fn); + Base::ofstream file(tmp, std::ios::out | std::ios::binary); + if (file.is_open()) + { + Base::ZipWriter writer(file); + if (hGrp->GetBool("SaveBinaryBrep", true)) + writer.setMode("BinaryBrep"); - //sw->hide(); - //sw->deleteLater(); + writer.setComment("AutoRecovery file"); + writer.setLevel(1); // apparently the fastest compression + writer.putNextEntry("Document.xml"); + + doc->Save(writer); + + // Special handling for Gui document. + doc->signalSaveDocument(writer); + + // write additional files + writer.writeFiles(); + } + } + } std::string str = watch.toString(watch.elapsed()); Base::Console().Log("Save AutoRecovery file: %s\n", str.c_str()); @@ -243,16 +266,6 @@ RecoveryWriter::~RecoveryWriter() { } -void RecoveryWriter::setLevel(int) -{ - // not implemented -} - -void RecoveryWriter::setComment(const char*) -{ - // not implemented -} - bool RecoveryWriter::shouldWrite(const std::string& name, const Base::Persistence *object) const { // Property files of a view provider can always be written because diff --git a/src/Gui/AutoSaver.h b/src/Gui/AutoSaver.h index 3cd5c445a..164398b0c 100644 --- a/src/Gui/AutoSaver.h +++ b/src/Gui/AutoSaver.h @@ -77,6 +77,10 @@ public: Sets the timeout in milliseconds. A value of 0 means that no timer is used. */ void setTimeout(int ms); + /*! + Enables or disables to create compreesed recovery files. + */ + void setCompressed(bool on); protected: void slotCreateDocument(const App::Document& Doc); @@ -86,6 +90,7 @@ protected: private: int timeout; /*!< Timeout in milliseconds */ + bool compressed; std::map saverMap; }; @@ -95,9 +100,6 @@ public: RecoveryWriter(AutoSaveProperty&); virtual ~RecoveryWriter(); - void setLevel(int); - void setComment(const char*); - /*! This method can be re-implemented in sub-classes to avoid to write out certain objects. The default implementation diff --git a/src/Gui/DlgProjectUtility.cpp b/src/Gui/DlgProjectUtility.cpp index baeccacad..7558d8b5c 100644 --- a/src/Gui/DlgProjectUtility.cpp +++ b/src/Gui/DlgProjectUtility.cpp @@ -37,7 +37,7 @@ using namespace Gui::Dialog; // taken from the script doctools.py -const char* doctools = +std::string DlgProjectUtility::doctools = "import os,sys,string\n" "import xml.sax\n" "import xml.sax.handler\n" diff --git a/src/Gui/DlgProjectUtility.h b/src/Gui/DlgProjectUtility.h index c68abb632..40373f81a 100644 --- a/src/Gui/DlgProjectUtility.h +++ b/src/Gui/DlgProjectUtility.h @@ -25,6 +25,7 @@ #define GUI_DIALOG_DLGPROJECTUTILITY_H #include +#include namespace Gui { namespace Dialog { @@ -42,6 +43,7 @@ private Q_SLOTS: void on_createButton_clicked(); protected: + static std::string doctools; Ui_DlgProjectUtility* ui; }; diff --git a/src/Gui/DocumentRecovery.cpp b/src/Gui/DocumentRecovery.cpp index 1f9e6db18..416151042 100644 --- a/src/Gui/DocumentRecovery.cpp +++ b/src/Gui/DocumentRecovery.cpp @@ -34,6 +34,7 @@ # include # include # include +# include # include # include # include @@ -41,6 +42,7 @@ # include # include # include +# include #endif #include "DocumentRecovery.h" @@ -60,6 +62,74 @@ using namespace Gui; using namespace Gui::Dialog; +// taken from the script doctools.py +std::string DocumentRecovery::doctools = +"import os,sys,string\n" +"import xml.sax\n" +"import xml.sax.handler\n" +"import xml.sax.xmlreader\n" +"import zipfile\n" +"\n" +"# SAX handler to parse the Document.xml\n" +"class DocumentHandler(xml.sax.handler.ContentHandler):\n" +" def __init__(self, dirname):\n" +" self.files = []\n" +" self.dirname = dirname\n" +"\n" +" def startElement(self, name, attributes):\n" +" item=attributes.get(\"file\")\n" +" if item != None:\n" +" self.files.append(os.path.join(self.dirname,str(item)))\n" +"\n" +" def characters(self, data):\n" +" return\n" +"\n" +" def endElement(self, name):\n" +" return\n" +"\n" +"def extractDocument(filename, outpath):\n" +" zfile=zipfile.ZipFile(filename)\n" +" files=zfile.namelist()\n" +"\n" +" for i in files:\n" +" data=zfile.read(i)\n" +" dirs=i.split(\"/\")\n" +" if len(dirs) > 1:\n" +" dirs.pop()\n" +" curpath=outpath\n" +" for j in dirs:\n" +" curpath=curpath+\"/\"+j\n" +" os.mkdir(curpath)\n" +" output=open(outpath+\"/\"+i,\'wb\')\n" +" output.write(data)\n" +" output.close()\n" +"\n" +"def createDocument(filename, outpath):\n" +" files=getFilesList(filename)\n" +" dirname=os.path.dirname(filename)\n" +" guixml=os.path.join(dirname,\"GuiDocument.xml\")\n" +" if os.path.exists(guixml):\n" +" files.extend(getFilesList(guixml))\n" +" compress=zipfile.ZipFile(outpath,\'w\',zipfile.ZIP_DEFLATED)\n" +" for i in files:\n" +" dirs=os.path.split(i)\n" +" #print i, dirs[-1]\n" +" compress.write(i,dirs[-1],zipfile.ZIP_DEFLATED)\n" +" compress.close()\n" +"\n" +"def getFilesList(filename):\n" +" dirname=os.path.dirname(filename)\n" +" handler=DocumentHandler(dirname)\n" +" parser=xml.sax.make_parser()\n" +" parser.setContentHandler(handler)\n" +" parser.parse(filename)\n" +"\n" +" files=[]\n" +" files.append(filename)\n" +" files.extend(iter(handler.files))\n" +" return files\n" +; + namespace Gui { namespace Dialog { class DocumentRecoveryPrivate @@ -113,6 +183,7 @@ DocumentRecovery::DocumentRecovery(const QList& dirs, QWidget* parent item->setText(0, info.label); item->setToolTip(0, info.tooltip); item->setText(1, tr("Not yet recovered")); + item->setToolTip(1, info.projectFile); d_ptr->ui.treeWidget->addTopLevelItem(item); } } @@ -128,6 +199,21 @@ bool DocumentRecovery::foundDocuments() const return (!d->recoveryInfo.isEmpty()); } +QString DocumentRecovery::createProjectFile(const QString& documentXml) +{ + QString source = documentXml; + QFileInfo fi(source); + QString dest = fi.dir().absoluteFilePath(QString::fromLatin1("fc_recovery_file.fcstd")); + + std::stringstream str; + str << doctools << "\n"; + str << "createDocument(\"" << (const char*)source.toUtf8() + << "\", \"" << (const char*)dest.toUtf8() << "\")"; + Application::Instance->runPythonCode(str.str().c_str()); + + return dest; +} + void DocumentRecovery::closeEvent(QCloseEvent* e) { Q_D(DocumentRecovery); @@ -151,6 +237,9 @@ void DocumentRecovery::accept() try { QString file = it->projectFile; + QFileInfo fi(file); + if (fi.fileName() == QLatin1String("Document.xml")) + file = createProjectFile(it->projectFile); App::Document* document = App::GetApplication().newDocument(); documentName = document->getName(); document->FileName.setValue(file.toUtf8().constData()); @@ -253,48 +342,57 @@ DocumentRecoveryPrivate::Info DocumentRecoveryPrivate::getRecoveryInfo(const QFi info.status = DocumentRecoveryPrivate::Unknown; info.label = qApp->translate("StdCmdNew","Unnamed"); + QString file; QDir doc_dir(fi.absoluteFilePath()); + QDir rec_dir(doc_dir.absoluteFilePath(QLatin1String("fc_recovery_files"))); + + // compressed recovery file if (doc_dir.exists(QLatin1String("fc_recovery_file.fcstd"))) { - info.status = DocumentRecoveryPrivate::Created; - QString file = doc_dir.absoluteFilePath(QLatin1String("fc_recovery_file.fcstd")); - info.projectFile = file; - info.tooltip = fi.fileName(); + file = doc_dir.absoluteFilePath(QLatin1String("fc_recovery_file.fcstd")); + } + // separate files for recovery + else if (rec_dir.exists(QLatin1String("Document.xml"))) { + file = rec_dir.absoluteFilePath(QLatin1String("Document.xml")); + } - // when the Xml meta exists get some relevant information - info.xmlFile = doc_dir.absoluteFilePath(QLatin1String("fc_recovery_file.xml")); - if (doc_dir.exists(QLatin1String("fc_recovery_file.xml"))) { - XmlConfig cfg = readXmlFile(info.xmlFile); + info.status = DocumentRecoveryPrivate::Created; + info.projectFile = file; + info.tooltip = fi.fileName(); - if (cfg.contains(QString::fromLatin1("Label"))) { - info.label = cfg[QString::fromLatin1("Label")]; - } + // when the Xml meta exists get some relevant information + info.xmlFile = doc_dir.absoluteFilePath(QLatin1String("fc_recovery_file.xml")); + if (doc_dir.exists(QLatin1String("fc_recovery_file.xml"))) { + XmlConfig cfg = readXmlFile(info.xmlFile); - if (cfg.contains(QString::fromLatin1("FileName"))) { - info.fileName = cfg[QString::fromLatin1("FileName")]; - } + if (cfg.contains(QString::fromLatin1("Label"))) { + info.label = cfg[QString::fromLatin1("Label")]; + } - if (cfg.contains(QString::fromLatin1("Status"))) { - QString status = cfg[QString::fromLatin1("Status")]; - if (status == QLatin1String("Deprecated")) + if (cfg.contains(QString::fromLatin1("FileName"))) { + info.fileName = cfg[QString::fromLatin1("FileName")]; + } + + if (cfg.contains(QString::fromLatin1("Status"))) { + QString status = cfg[QString::fromLatin1("Status")]; + if (status == QLatin1String("Deprecated")) + info.status = DocumentRecoveryPrivate::Overage; + else if (status == QLatin1String("Success")) + info.status = DocumentRecoveryPrivate::Success; + else if (status == QLatin1String("Failure")) + info.status = DocumentRecoveryPrivate::Failure; + } + + if (info.status == DocumentRecoveryPrivate::Created) { + // compare the modification dates + QFileInfo fileInfo(info.fileName); + if (!info.fileName.isEmpty() && fileInfo.exists()) { + QDateTime dateRecv = QFileInfo(file).lastModified(); + QDateTime dateProj = fileInfo.lastModified(); + if (dateRecv < dateProj) { info.status = DocumentRecoveryPrivate::Overage; - else if (status == QLatin1String("Success")) - info.status = DocumentRecoveryPrivate::Success; - else if (status == QLatin1String("Failure")) - info.status = DocumentRecoveryPrivate::Failure; - } - - if (info.status == DocumentRecoveryPrivate::Created) { - // compare the modification dates - QFileInfo fileInfo(info.fileName); - if (!info.fileName.isEmpty() && fileInfo.exists()) { - QDateTime dateRecv = QFileInfo(file).lastModified(); - QDateTime dateProj = fileInfo.lastModified(); - if (dateRecv < dateProj) { - info.status = DocumentRecoveryPrivate::Overage; - writeRecoveryInfo(info); - qWarning() << "Ignore recovery file " << file.toUtf8() - << " because it is older than the project file" << info.fileName.toUtf8() << "\n"; - } + writeRecoveryInfo(info); + qWarning() << "Ignore recovery file " << file.toUtf8() + << " because it is older than the project file" << info.fileName.toUtf8() << "\n"; } } } diff --git a/src/Gui/DocumentRecovery.h b/src/Gui/DocumentRecovery.h index baa81abfd..3b8e9aa04 100644 --- a/src/Gui/DocumentRecovery.h +++ b/src/Gui/DocumentRecovery.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace Gui { namespace Dialog { @@ -49,8 +50,10 @@ public: protected: void closeEvent(QCloseEvent*); + QString createProjectFile(const QString&); private: + static std::string doctools; QScopedPointer d_ptr; Q_DISABLE_COPY(DocumentRecovery) Q_DECLARE_PRIVATE(DocumentRecovery)