+ issue #0000175: Auto save function

This commit is contained in:
wmayer 2015-09-04 15:08:12 +02:00
parent 9253572afe
commit 9cb63295e9
8 changed files with 537 additions and 7 deletions

View File

@ -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();
}
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net> *
* Copyright (c) 2015 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
@ -25,6 +25,8 @@
#ifndef _PreComp_
# include <QApplication>
# include <QFile>
# include <QTextStream>
# include <boost/bind.hpp>
#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 << "<?xml version='1.0' encoding='utf-8'?>" << endl
<< "<AutoRecovery SchemaVersion=\"1\">" << endl;
str << " <Status>Created</Status>" << endl;
str << " <Label>" << doc->Label.getValue() << "</Label>" << endl; // store the document's current label
str << "</AutoRecovery>" << 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");

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net> *
* Copyright (c) 2015 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *

View File

@ -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

View File

@ -0,0 +1,319 @@
/***************************************************************************
* Copyright (c) 2015 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 <QApplication>
# include <QCloseEvent>
# include <QDir>
# include <QFile>
# include <QHeaderView>
# include <QPushButton>
# include <QTextStream>
# include <QTreeWidgetItem>
# include <QMap>
# include <QList>
# include <QVector>
#endif
#include "DocumentRecovery.h"
#include "ui_DocumentRecovery.h"
#include "WaitCursor.h"
#include <Base/Exception.h>
#include <App/Application.h>
#include <App/Document.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <QDomDocument>
using namespace Gui;
using namespace Gui::Dialog;
namespace Gui { namespace Dialog {
class DocumentRecoveryPrivate
{
public:
typedef QMap<QString, QString> 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<Info> recoveryInfo;
Info getRecoveryInfo(const QFileInfo&) const;
void writeRecoveryInfo(const Info&);
XmlConfig readXmlFile(const QString& fn) const;
};
}
}
DocumentRecovery::DocumentRecovery(const QList<QFileInfo>& 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<QFileInfo>::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<DocumentRecoveryPrivate::Info>::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 << "<?xml version='1.0' encoding='utf-8'?>" << endl
<< "<AutoRecovery SchemaVersion=\"1\">" << endl;
switch (info.status) {
case Created:
str << " <Status>Created</Status>" << endl;
break;
case Success:
str << " <Status>Success</Status>" << endl;
break;
case Failure:
str << " <Status>Failure</Status>" << endl;
break;
default:
str << " <Status>Unknown</Status>" << endl;
break;
}
str << " <Label>" << info.label << "</Label>" << endl;
str << "</AutoRecovery>" << 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<QString> 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"

View File

@ -0,0 +1,64 @@
/***************************************************************************
* Copyright (c) 2015 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 <QDialog>
#include <QScopedPointer>
#include <QList>
#include <QFileInfo>
namespace Gui { namespace Dialog {
class DocumentRecoveryPrivate;
/*!
@author Werner Mayer
*/
class DocumentRecovery : public QDialog
{
Q_OBJECT
public:
DocumentRecovery(const QList<QFileInfo>&, QWidget* parent = 0);
virtual ~DocumentRecovery();
void accept();
bool foundDocuments() const;
protected:
void closeEvent(QCloseEvent*);
private:
QScopedPointer<DocumentRecoveryPrivate> d_ptr;
Q_DISABLE_COPY(DocumentRecovery)
Q_DECLARE_PRIVATE(DocumentRecovery)
};
} //namespace Dialog
} //namespace Gui
#endif //GUI_DIALOG_DOCUMENTRECOVERY_H

120
src/Gui/DocumentRecovery.ui Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::Dialog::DocumentRecovery</class>
<widget class="QDialog" name="Gui::Dialog::DocumentRecovery">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>576</width>
<height>495</height>
</rect>
</property>
<property name="windowTitle">
<string>Document Recovery</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>84</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Press 'Start Recovery' to start the recovery process of the doument listed below.
The 'Status' column shows whether the document could be recovered.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>84</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Status of recovered documents:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QTreeWidget" name="treeWidget">
<column>
<property name="text">
<string>Document Name</string>
</property>
</column>
<column>
<property name="text">
<string>Status</string>
</property>
</column>
</widget>
</item>
<item row="5" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Gui::Dialog::DocumentRecovery</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Gui::Dialog::DocumentRecovery</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -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)