Gui: code-refactoring of document recovery handling to reduce code duplication

This commit is contained in:
wmayer
2021-10-21 21:45:52 +02:00
parent 167e11596a
commit ebb9f4723f
3 changed files with 132 additions and 106 deletions

View File

@@ -2416,80 +2416,8 @@ void Application::setStyleSheet(const QString& qssFile, bool tiledBackground)
void Application::checkForPreviousCrashes()
{
QDir tmp = QString::fromUtf8(App::Application::getTempPath().c_str());
tmp.setNameFilters(QStringList() << QString::fromLatin1("*.lock"));
tmp.setFilter(QDir::Files);
QList<QFileInfo> restoreDocFiles;
QString exeName = QString::fromLatin1(App::GetApplication().getExecutableName());
QList<QFileInfo> locks = tmp.entryInfoList();
for (QList<QFileInfo>::iterator it = locks.begin(); it != locks.end(); ++it) {
QString bn = it->baseName();
// ignore the lock file for this instance
QString pid = QString::number(QCoreApplication::applicationPid());
if (bn.startsWith(exeName) && bn.indexOf(pid) < 0) {
QString fn = it->absoluteFilePath();
boost::interprocess::file_lock flock((const char*)fn.toLocal8Bit());
if (flock.try_lock()) {
// OK, this file is a leftover from a previous crash
QString crashed_pid = bn.mid(exeName.length()+1);
// search for transient directories with this PID
QString filter;
QTextStream str(&filter);
str << exeName << "_Doc_*_" << crashed_pid;
tmp.setNameFilters(QStringList() << filter);
tmp.setFilter(QDir::Dirs);
QList<QFileInfo> dirs = tmp.entryInfoList();
if (dirs.isEmpty()) {
// delete the lock file immediately if no transient directories are related
tmp.remove(fn);
}
else {
int countDeletedDocs = 0;
QString recovery_files = QString::fromLatin1("fc_recovery_files");
for (QList<QFileInfo>::iterator it = dirs.begin(); it != dirs.end(); ++it) {
QDir doc_dir(it->absoluteFilePath());
doc_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries);
uint entries = doc_dir.entryList().count();
if (entries == 0) {
// in this case we can delete the transient directory because
// we cannot do anything
if (tmp.rmdir(it->filePath()))
countDeletedDocs++;
}
// search for the existence of a recovery file
else if (doc_dir.exists(QLatin1String("fc_recovery_file.xml"))) {
// store the transient directory in case it's not empty
restoreDocFiles << *it;
}
// search for the 'fc_recovery_files' sub-directory and check that it's the only entry
else if (entries == 1 && doc_dir.exists(recovery_files)) {
// if the sub-directory is empty delete the transient directory
QDir rec_dir(doc_dir.absoluteFilePath(recovery_files));
rec_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries);
if (rec_dir.entryList().isEmpty()) {
doc_dir.rmdir(recovery_files);
if (tmp.rmdir(it->filePath()))
countDeletedDocs++;
}
}
}
// all directories corresponding to the lock file have been deleted
// so delete the lock file, too
if (countDeletedDocs == dirs.size()) {
tmp.remove(fn);
}
}
}
}
}
if (!restoreDocFiles.isEmpty()) {
Gui::Dialog::DocumentRecovery dlg(restoreDocFiles, Gui::getMainWindow());
if (dlg.foundDocuments())
dlg.exec();
}
Gui::Dialog::DocumentRecoveryFinder finder;
finder.checkForPreviousCrashes();
}
App::Document *Application::reopen(App::Document *doc) {

View File

@@ -60,6 +60,7 @@
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/MainWindow.h>
#include <QDomDocument>
#include <boost/interprocess/sync/file_lock.hpp>
@@ -68,6 +69,7 @@ FC_LOG_LEVEL_INIT("Gui",true,true)
using namespace Gui;
using namespace Gui::Dialog;
namespace sp = std::placeholders;
// taken from the script doctools.py
std::string DocumentRecovery::doctools =
@@ -553,41 +555,20 @@ void DocumentRecovery::on_buttonCleanup_clicked()
d_ptr->ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
d_ptr->ui.buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true);
QDir tmp = QString::fromUtf8(App::Application::getTempPath().c_str());
tmp.setNameFilters(QStringList() << QString::fromLatin1("*.lock"));
tmp.setFilter(QDir::Files);
DocumentRecoveryHandler handler;
handler.checkForPreviousCrashes(std::bind(&DocumentRecovery::cleanup, this, sp::_1, sp::_2, sp::_3));
QMessageBox::information(this, tr("Finished"), tr("Transient directories deleted."));
}
QString exeName = QString::fromLatin1(App::GetApplication().getExecutableName());
QList<QFileInfo> locks = tmp.entryInfoList();
for (QList<QFileInfo>::iterator it = locks.begin(); it != locks.end(); ++it) {
QString bn = it->baseName();
// ignore the lock file for this instance
QString pid = QString::number(QCoreApplication::applicationPid());
if (bn.startsWith(exeName) && bn.indexOf(pid) < 0) {
QString fn = it->absoluteFilePath();
boost::interprocess::file_lock flock((const char*)fn.toLocal8Bit());
if (flock.try_lock()) {
// OK, this file is a leftover from a previous crash
QString crashed_pid = bn.mid(exeName.length()+1);
// search for transient directories with this PID
QString filter;
QTextStream str(&filter);
str << exeName << "_Doc_*_" << crashed_pid;
tmp.setNameFilters(QStringList() << filter);
tmp.setFilter(QDir::Dirs);
QList<QFileInfo> dirs = tmp.entryInfoList();
if (!dirs.isEmpty()) {
for (QList<QFileInfo>::iterator jt = dirs.begin(); jt != dirs.end(); ++jt) {
clearDirectory(*jt);
tmp.rmdir(jt->fileName());
}
}
tmp.remove(it->fileName());
}
void DocumentRecovery::cleanup(QDir& tmp, const QList<QFileInfo>& dirs, const QString& lockFile)
{
if (!dirs.isEmpty()) {
for (QList<QFileInfo>::const_iterator jt = dirs.cbegin(); jt != dirs.cend(); ++jt) {
clearDirectory(*jt);
tmp.rmdir(jt->fileName());
}
}
QMessageBox::information(this, tr("Finished"), tr("Transient directories deleted."));
tmp.remove(lockFile);
}
void DocumentRecovery::clearDirectory(const QFileInfo& dir)
@@ -613,4 +594,102 @@ void DocumentRecovery::clearDirectory(const QFileInfo& dir)
}
}
// ----------------------------------------------------------------------------
void DocumentRecoveryFinder::checkForPreviousCrashes()
{
DocumentRecoveryHandler handler;
handler.checkForPreviousCrashes(std::bind(&DocumentRecoveryFinder::checkDocumentDirs, this, sp::_1, sp::_2, sp::_3));
showRecoveryDialogIfNeeded();
}
void DocumentRecoveryFinder::checkDocumentDirs(QDir& tmp, const QList<QFileInfo>& dirs, const QString& fn)
{
if (dirs.isEmpty()) {
// delete the lock file immediately if no transient directories are related
tmp.remove(fn);
}
else {
int countDeletedDocs = 0;
QString recovery_files = QString::fromLatin1("fc_recovery_files");
for (QList<QFileInfo>::const_iterator it = dirs.cbegin(); it != dirs.cend(); ++it) {
QDir doc_dir(it->absoluteFilePath());
doc_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries);
uint entries = doc_dir.entryList().count();
if (entries == 0) {
// in this case we can delete the transient directory because
// we cannot do anything
if (tmp.rmdir(it->filePath()))
countDeletedDocs++;
}
// search for the existence of a recovery file
else if (doc_dir.exists(QLatin1String("fc_recovery_file.xml"))) {
// store the transient directory in case it's not empty
restoreDocFiles << *it;
}
// search for the 'fc_recovery_files' sub-directory and check that it's the only entry
else if (entries == 1 && doc_dir.exists(recovery_files)) {
// if the sub-directory is empty delete the transient directory
QDir rec_dir(doc_dir.absoluteFilePath(recovery_files));
rec_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries);
if (rec_dir.entryList().isEmpty()) {
doc_dir.rmdir(recovery_files);
if (tmp.rmdir(it->filePath()))
countDeletedDocs++;
}
}
}
// all directories corresponding to the lock file have been deleted
// so delete the lock file, too
if (countDeletedDocs == dirs.size()) {
tmp.remove(fn);
}
}
}
void DocumentRecoveryFinder::showRecoveryDialogIfNeeded()
{
if (!restoreDocFiles.isEmpty()) {
Gui::Dialog::DocumentRecovery dlg(restoreDocFiles, Gui::getMainWindow());
if (dlg.foundDocuments())
dlg.exec();
}
}
// ----------------------------------------------------------------------------
void DocumentRecoveryHandler::checkForPreviousCrashes(const std::function<void(QDir&, const QList<QFileInfo>&, const QString&)> & callableFunc) const
{
QDir tmp = QString::fromUtf8(App::Application::getTempPath().c_str());
tmp.setNameFilters(QStringList() << QString::fromLatin1("*.lock"));
tmp.setFilter(QDir::Files);
QString exeName = QString::fromLatin1(App::GetApplication().getExecutableName());
QList<QFileInfo> locks = tmp.entryInfoList();
for (QList<QFileInfo>::iterator it = locks.begin(); it != locks.end(); ++it) {
QString bn = it->baseName();
// ignore the lock file for this instance
QString pid = QString::number(QCoreApplication::applicationPid());
if (bn.startsWith(exeName) && bn.indexOf(pid) < 0) {
QString fn = it->absoluteFilePath();
boost::interprocess::file_lock flock((const char*)fn.toLocal8Bit());
if (flock.try_lock()) {
// OK, this file is a leftover from a previous crash
QString crashed_pid = bn.mid(exeName.length()+1);
// search for transient directories with this PID
QString filter;
QTextStream str(&filter);
str << exeName << "_Doc_*_" << crashed_pid;
tmp.setNameFilters(QStringList() << filter);
tmp.setFilter(QDir::Dirs);
QList<QFileInfo> dirs = tmp.entryInfoList();
callableFunc(tmp, dirs, it->fileName());
}
}
}
}
#include "moc_DocumentRecovery.cpp"

View File

@@ -29,6 +29,7 @@
#include <QList>
#include <QFileInfo>
#include <string>
#include <functional>
namespace Gui { namespace Dialog {
@@ -53,6 +54,7 @@ protected:
void contextMenuEvent(QContextMenuEvent*);
QString createProjectFile(const QString&);
void clearDirectory(const QFileInfo&);
void cleanup(QDir&, const QList<QFileInfo>&, const QString&);
protected Q_SLOTS:
void on_buttonCleanup_clicked();
@@ -65,6 +67,23 @@ private:
Q_DECLARE_PRIVATE(DocumentRecovery)
};
class DocumentRecoveryFinder {
public:
void checkForPreviousCrashes();
private:
void checkDocumentDirs(QDir&, const QList<QFileInfo>&, const QString&);
void showRecoveryDialogIfNeeded();
private:
QList<QFileInfo> restoreDocFiles;
};
class DocumentRecoveryHandler {
public:
void checkForPreviousCrashes(const std::function<void(QDir&, const QList<QFileInfo>&, const QString&)> & callableFunc) const;
};
} //namespace Dialog
} //namespace Gui