diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index da4e7772c..a6652e4ef 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -87,6 +87,7 @@ #include "DlgOnlineHelpImp.h" #include "SpaceballEvent.h" #include "Control.h" +#include "DocumentRecovery.h" #include "TaskView/TaskView.h" #include "SplitView3DInventor.h" @@ -1913,7 +1914,8 @@ void Application::checkForPreviousCrashes() if (tmp.rmdir(it->filePath())) countDeletedDocs++; } - else { + // search for the existance of a recovery file + else if (doc_dir.exists(QLatin1String("fc_recovery_file.fcstd"))) { // store the transient directory in case it's not empty restoreDocFiles << *it; } @@ -1930,6 +1932,8 @@ void Application::checkForPreviousCrashes() } if (!restoreDocFiles.isEmpty()) { - //TODO: + Gui::Dialog::DocumentRecovery dlg(restoreDocFiles, Gui::getMainWindow()); + if (dlg.foundDocuments()) + dlg.exec(); } } diff --git a/src/Gui/AutoSaver.cpp b/src/Gui/AutoSaver.cpp index 266dc3fa7..a50a754c7 100644 --- a/src/Gui/AutoSaver.cpp +++ b/src/Gui/AutoSaver.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2010 Werner Mayer * + * Copyright (c) 2015 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * @@ -25,6 +25,8 @@ #ifndef _PreComp_ # include +# include +# include # include #endif @@ -97,8 +99,22 @@ void AutoSaver::saveDocument(const std::string& name) Gui::WaitCursor wc; App::Document* doc = App::GetApplication().getDocument(name.c_str()); if (doc) { + // Write recovery meta file + QFile file(QString::fromLatin1("%1/fc_recovery_file.xml") + .arg(QString::fromUtf8(doc->TransientDir.getValue()))); + if (file.open(QFile::WriteOnly)) { + QTextStream str(&file); + str.setCodec("UTF-8"); + str << "" << endl + << "" << endl; + str << " Created" << endl; + str << " " << endl; // store the document's current label + str << "" << endl; + file.close(); + } + std::string fn = doc->TransientDir.getValue(); - fn += "/fc_autosave_file.fcstd"; + fn += "/fc_recovery_file.fcstd"; Base::FileInfo tmp(fn); // make sure to tmp. disable saving thumbnails because this causes trouble if the @@ -121,7 +137,7 @@ void AutoSaver::saveDocument(const std::string& name) if (file.is_open()) { Base::ZipWriter writer(file); - writer.setComment(doc->Label.getValue()); // store the document's current label + writer.setComment("AutoRecovery file"); writer.setLevel(1); // apparently the fastest compression writer.putNextEntry("Document.xml"); diff --git a/src/Gui/AutoSaver.h b/src/Gui/AutoSaver.h index 6aa41c3cb..4f723354f 100644 --- a/src/Gui/AutoSaver.h +++ b/src/Gui/AutoSaver.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2010 Werner Mayer * + * Copyright (c) 2015 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 8655b851a..f559e5c36 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -202,6 +202,7 @@ set(Gui_MOC_HDRS DlgUndoRedo.h DockWindow.h DockWindowManager.h + DocumentRecovery.h EditorView.h FileDialog.h Flag.h @@ -303,6 +304,7 @@ SET(Gui_UIC_SRCS DlgTreeWidget.ui DlgLocationAngle.ui DlgLocationPos.ui + DocumentRecovery.ui DownloadManager.ui DownloadItem.ui MouseButtons.ui @@ -375,6 +377,7 @@ SET(Dialog_CPP_SRCS DownloadDialog.cpp DownloadItem.cpp DownloadManager.cpp + DocumentRecovery.cpp ) SET(Dialog_HPP_SRCS @@ -405,6 +408,7 @@ SET(Dialog_HPP_SRCS DownloadDialog.h DownloadItem.h DownloadManager.h + DocumentRecovery.h ) SET(Dialog_SRCS @@ -432,6 +436,7 @@ SET(Dialog_SRCS DlgTreeWidget.ui DownloadManager.ui DownloadItem.ui + DocumentRecovery.ui MouseButtons.ui InputVector.ui Placement.ui diff --git a/src/Gui/DocumentRecovery.cpp b/src/Gui/DocumentRecovery.cpp new file mode 100644 index 000000000..48e298ee2 --- /dev/null +++ b/src/Gui/DocumentRecovery.cpp @@ -0,0 +1,319 @@ +/*************************************************************************** + * Copyright (c) 2015 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +// Implement FileWriter which puts files into a directory +// write a property to file only when it has been modified +// implement xml meta file + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "DocumentRecovery.h" +#include "ui_DocumentRecovery.h" +#include "WaitCursor.h" + +#include + +#include +#include + +#include +#include + +#include + +using namespace Gui; +using namespace Gui::Dialog; + + +namespace Gui { namespace Dialog { +class DocumentRecoveryPrivate +{ +public: + typedef QMap XmlConfig; + + enum Status { + Unknown = 0, /*!< The file is not available */ + Created = 1, /*!< The file was created but not processed so far*/ + Success = 2, /*!< The file could be recovered */ + Failure = 3, /*!< The file could not be recovered */ + }; + struct Info { + QString projectFile; + QString xmlFile; + QString label; + QString tooltip; + Status status; + }; + Ui_DocumentRecovery ui; + bool recovered; + QList recoveryInfo; + + Info getRecoveryInfo(const QFileInfo&) const; + void writeRecoveryInfo(const Info&); + XmlConfig readXmlFile(const QString& fn) const; +}; + +} +} + +DocumentRecovery::DocumentRecovery(const QList& dirs, QWidget* parent) + : QDialog(parent), d_ptr(new DocumentRecoveryPrivate()) +{ + d_ptr->ui.setupUi(this); + d_ptr->ui.buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start Recovery")); + d_ptr->ui.treeWidget->header()->setResizeMode(QHeaderView::Stretch); + + d_ptr->recovered = false; + + for (QList::const_iterator it = dirs.begin(); it != dirs.end(); ++it) { + DocumentRecoveryPrivate::Info info = d_ptr->getRecoveryInfo(*it); + + if (info.status == DocumentRecoveryPrivate::Created) { + d_ptr->recoveryInfo << info; + + QTreeWidgetItem* item = new QTreeWidgetItem(d_ptr->ui.treeWidget); + item->setText(0, info.label); + item->setToolTip(0, info.tooltip); + item->setText(1, tr("Not yet recovered")); + d_ptr->ui.treeWidget->addTopLevelItem(item); + } + } +} + +DocumentRecovery::~DocumentRecovery() +{ +} + +bool DocumentRecovery::foundDocuments() const +{ + Q_D(const DocumentRecovery); + return (!d->recoveryInfo.isEmpty()); +} + +void DocumentRecovery::closeEvent(QCloseEvent* e) +{ + Q_D(DocumentRecovery); + + if (!d->recoveryInfo.isEmpty()) + e->ignore(); +} + +void DocumentRecovery::accept() +{ + Q_D(DocumentRecovery); + + if (!d->recovered) { + + WaitCursor wc; + int index = 0; + for (QList::iterator it = d->recoveryInfo.begin(); it != d->recoveryInfo.end(); ++it, index++) { + std::string documentName; + QString errorInfo; + QTreeWidgetItem* item = d_ptr->ui.treeWidget->topLevelItem(index); + + try { + QString file = it->projectFile; + App::Document* document = App::GetApplication().newDocument(); + documentName = document->getName(); + document->FileName.setValue(file.toUtf8().constData()); + + // If something goes wrong an exception will be thrown here + document->restore(); + + document->FileName.setValue(std::string()); + document->Label.setValue(it->label.toUtf8().constData()); + + // Set the modified flag so that the user cannot close by accident + Gui::Document* guidoc = Gui::Application::Instance->getDocument(document); + if (guidoc) { + guidoc->setModified(true); + } + } + catch (const std::exception& e) { + errorInfo = QString::fromLatin1(e.what()); + } + catch (const Base::Exception& e) { + errorInfo = QString::fromLatin1(e.what()); + } + catch (...) { + errorInfo = tr("Unknown problem occurred"); + } + + // an error occurred so close the document again + if (!errorInfo.isEmpty()) { + if (!documentName.empty()) + App::GetApplication().closeDocument(documentName.c_str()); + + it->status = DocumentRecoveryPrivate::Failure; + + if (item) { + item->setText(1, tr("Failed to recover")); + item->setToolTip(1, errorInfo); + item->setForeground(1, QColor(170,0,0)); + } + } + // everything OK + else { + it->status = DocumentRecoveryPrivate::Success; + + if (item) { + item->setText(1, tr("Successfully recovered")); + item->setForeground(1, QColor(0,170,0)); + } + } + + // write back current status + d->writeRecoveryInfo(*it); + } + + d->ui.buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Finish")); + d->ui.buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); + d->recovered = true; + } + else { + QDialog::accept(); + } +} + +void DocumentRecoveryPrivate::writeRecoveryInfo(const DocumentRecoveryPrivate::Info& info) +{ + // Write recovery meta file + QFile file(info.xmlFile); + if (file.open(QFile::WriteOnly)) { + QTextStream str(&file); + str.setCodec("UTF-8"); + str << "" << endl + << "" << endl; + switch (info.status) { + case Created: + str << " Created" << endl; + break; + case Success: + str << " Success" << endl; + break; + case Failure: + str << " Failure" << endl; + break; + default: + str << " Unknown" << endl; + break; + } + str << " " << endl; + str << "" << endl; + file.close(); + } +} + +DocumentRecoveryPrivate::Info DocumentRecoveryPrivate::getRecoveryInfo(const QFileInfo& fi) const +{ + DocumentRecoveryPrivate::Info info; + info.status = DocumentRecoveryPrivate::Unknown; + info.label = qApp->translate("StdCmdNew","Unnamed"); + + QDir doc_dir(fi.absoluteFilePath()); + 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(); + + // 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("Label"))) { + info.label = cfg[QString::fromLatin1("Label")]; + } + + if (cfg.contains(QString::fromLatin1("Status"))) { + QString status = cfg[QString::fromLatin1("Status")]; + if (status == QLatin1String("Success")) + info.status = DocumentRecoveryPrivate::Success; + else if (status == QLatin1String("Failure")) + info.status = DocumentRecoveryPrivate::Failure; + } + } + } + + return info; +} + +DocumentRecoveryPrivate::XmlConfig DocumentRecoveryPrivate::readXmlFile(const QString& fn) const +{ + DocumentRecoveryPrivate::XmlConfig cfg; + QDomDocument domDocument; + QFile file(fn); + if (!file.open(QFile::ReadOnly)) + return cfg; + + QString errorStr; + int errorLine; + int errorColumn; + + if (!domDocument.setContent(&file, true, &errorStr, &errorLine, + &errorColumn)) { + return cfg; + } + + QDomElement root = domDocument.documentElement(); + if (root.tagName() != QLatin1String("AutoRecovery")) { + return cfg; + } + + file.close(); + + QVector filter; + filter << QString::fromLatin1("Label"); + filter << QString::fromLatin1("Status"); + + QDomElement child; + if (!root.isNull()) { + child = root.firstChildElement(); + while (!child.isNull()) { + QString name = child.localName(); + QString value = child.text(); + if (std::find(filter.begin(), filter.end(), name) != filter.end()) + cfg[name] = value; + child = child.nextSiblingElement(); + } + } + + return cfg; +} + +#include "moc_DocumentRecovery.cpp" diff --git a/src/Gui/DocumentRecovery.h b/src/Gui/DocumentRecovery.h new file mode 100644 index 000000000..baa81abfd --- /dev/null +++ b/src/Gui/DocumentRecovery.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (c) 2015 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef GUI_DIALOG_DOCUMENTRECOVERY_H +#define GUI_DIALOG_DOCUMENTRECOVERY_H + +#include +#include +#include +#include + +namespace Gui { namespace Dialog { + +class DocumentRecoveryPrivate; + +/*! + @author Werner Mayer + */ +class DocumentRecovery : public QDialog +{ + Q_OBJECT + +public: + DocumentRecovery(const QList&, QWidget* parent = 0); + virtual ~DocumentRecovery(); + + void accept(); + bool foundDocuments() const; + +protected: + void closeEvent(QCloseEvent*); + +private: + QScopedPointer d_ptr; + Q_DISABLE_COPY(DocumentRecovery) + Q_DECLARE_PRIVATE(DocumentRecovery) +}; + +} //namespace Dialog + +} //namespace Gui + + +#endif //GUI_DIALOG_DOCUMENTRECOVERY_H diff --git a/src/Gui/DocumentRecovery.ui b/src/Gui/DocumentRecovery.ui new file mode 100644 index 000000000..5b85ab312 --- /dev/null +++ b/src/Gui/DocumentRecovery.ui @@ -0,0 +1,120 @@ + + + Gui::Dialog::DocumentRecovery + + + + 0 + 0 + 576 + 495 + + + + Document Recovery + + + + + + Qt::Vertical + + + + 20 + 84 + + + + + + + + Press 'Start Recovery' to start the recovery process of the doument listed below. + +The 'Status' column shows whether the document could be recovered. + + + + + + + Qt::Vertical + + + + 20 + 84 + + + + + + + + Status of recovered documents: + + + + + + + + Document Name + + + + + Status + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Gui::Dialog::DocumentRecovery + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Gui::Dialog::DocumentRecovery + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index a78bab8f8..d51856c06 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -1014,7 +1014,9 @@ void MainWindow::delayedStartup() App::GetApplication().newDocument(); } - Application::Instance->checkForPreviousCrashes(); + if (hGrp->GetBool("RecoveryEnabled", true)) { + Application::Instance->checkForPreviousCrashes(); + } } void MainWindow::appendRecentFile(const QString& filename)