From d10355b400ec069efd636eefb7a73c8de1467faf Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Fri, 21 May 2021 17:17:22 +0300 Subject: [PATCH] File > Save functions. --- src/app/puzzle/vpmainwindow.cpp | 194 ++++++++++++++++++---- src/app/puzzle/vpmainwindow.h | 14 +- src/app/puzzle/xml/vplayoutfilewriter.cpp | 12 -- src/app/puzzle/xml/vplayoutfilewriter.h | 8 +- src/app/tape/tmainwindow.cpp | 43 +---- src/app/valentina/mainwindow.cpp | 47 +----- src/libs/vmisc/vcommonsettings.cpp | 22 +++ src/libs/vmisc/vcommonsettings.h | 6 +- src/libs/vwidgets/vabstractmainwindow.cpp | 48 ++++++ src/libs/vwidgets/vabstractmainwindow.h | 2 + 10 files changed, 260 insertions(+), 136 deletions(-) diff --git a/src/app/puzzle/vpmainwindow.cpp b/src/app/puzzle/vpmainwindow.cpp index c9be83a3c..e11aa5020 100644 --- a/src/app/puzzle/vpmainwindow.cpp +++ b/src/app/puzzle/vpmainwindow.cpp @@ -45,6 +45,12 @@ #include "../vwidgets/vmaingraphicsscene.h" #include "vpsheet.h" +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) +#include "../vmisc/backport/qscopeguard.h" +#else +#include +#endif + #include QT_WARNING_PUSH @@ -157,16 +163,50 @@ bool VPMainWindow::LoadFile(QString path) } //--------------------------------------------------------------------------------------------------------------------- -bool VPMainWindow::SaveFile(const QString &path) +void VPMainWindow::LayoutWasSaved(bool saved) +{ + setWindowModified(!saved); + not lIsReadOnly ? ui->actionSave->setEnabled(!saved): ui->actionSave->setEnabled(false); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::SetCurrentFile(const QString &fileName) +{ + curFile = fileName; + if (not curFile.isEmpty()) + { + auto settings = VPApplication::VApp()->PuzzleSettings(); + QStringList files = settings->GetRecentFileList(); + files.removeAll(fileName); + files.prepend(fileName); + while (files.size() > MaxRecentFiles) + { + files.removeLast(); + } + settings->SetRecentFileList(files); + UpdateRecentFileActions(); + } + + UpdateWindowTitle(); +} + +//--------------------------------------------------------------------------------------------------------------------- +auto VPMainWindow::SaveLayout(const QString &path, QString &error) -> bool { QFile file(path); file.open(QIODevice::WriteOnly); - VPLayoutFileWriter *fileWriter = new VPLayoutFileWriter(); - fileWriter->WriteFile(m_layout, &file); + VPLayoutFileWriter fileWriter; + fileWriter.WriteFile(m_layout, &file); - // TODO / FIXME : better return value and error handling + if (fileWriter.hasError()) + { + error = tr("Fail to create layout."); + return false; + } + SetCurrentFile(path); + LayoutWasSaved(true); return true; } @@ -1058,47 +1098,143 @@ void VPMainWindow::on_actionOpen_triggered() } //--------------------------------------------------------------------------------------------------------------------- -void VPMainWindow::on_actionSave_triggered() +bool VPMainWindow::on_actionSave_triggered() { - // just for test purpuses, to be removed: - QMessageBox msgBox; - msgBox.setText("TODO VPMainWindow::Save"); - int ret = msgBox.exec(); + if (curFile.isEmpty() || lIsReadOnly) + { + return on_actionSaveAs_triggered(); + } - Q_UNUSED(ret); + if (m_curFileFormatVersion < VLayoutConverter::LayoutMaxVer + && not ContinueFormatRewrite(m_curFileFormatVersionStr, VLayoutConverter::LayoutMaxVerStr)) + { + return false; + } - // TODO + if (not CheckFilePermissions(curFile, this)) + { + return false; + } + + QString error; + if (not SaveLayout(curFile, error)) + { + QMessageBox messageBox; + messageBox.setIcon(QMessageBox::Warning); + messageBox.setText(tr("Could not save the file")); + messageBox.setDefaultButton(QMessageBox::Ok); + messageBox.setDetailedText(error); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.exec(); + return false; + } + + m_curFileFormatVersion = VLayoutConverter::LayoutMaxVer; + m_curFileFormatVersionStr = VLayoutConverter::LayoutMaxVerStr; + + return true; } //--------------------------------------------------------------------------------------------------------------------- -void VPMainWindow::on_actionSaveAs_triggered() +bool VPMainWindow::on_actionSaveAs_triggered() { - // TODO / FIXME : See valentina how the save is done over there. we need to add the - // extension .vlt, check for empty file names etc. + QString filters = tr("Layout files") + QStringLiteral(" (*.vlt)"); + QString suffix = QStringLiteral("vlt"); + QString fName = tr("layout") + QChar('.') + suffix; - //Get list last open files - QStringList recentFiles = VPApplication::VApp()->PuzzleSettings()->GetRecentFileList(); QString dir; - if (recentFiles.isEmpty()) + if (curFile.isEmpty()) { - dir = QDir::homePath(); + dir = VPApplication::VApp()->PuzzleSettings()->GetPathLayouts(); } else { - //Absolute path to last open file - dir = QFileInfo(recentFiles.first()).absolutePath(); + dir = QFileInfo(curFile).absolutePath(); } - QString filters(tr("Layout files") + QLatin1String("(*.vlt)")); - QString fileName = QFileDialog::getSaveFileName(this, tr("Save as"), - dir + QLatin1String("/") + tr("Layout") + QLatin1String(".vlt"), - filters, nullptr -#ifdef Q_OS_LINUX - , QFileDialog::DontUseNativeDialog -#endif - ); + bool usedNotExistedDir = false; + QDir directory(dir); + if (not directory.exists()) + { + usedNotExistedDir = directory.mkpath(QChar('.')); + } - SaveFile(fileName); + QString fileName = QFileDialog::getSaveFileName(this, tr("Save as"), dir + QChar('/') + fName, filters, nullptr, + VAbstractApplication::VApp()->NativeFileDialog()); + + auto RemoveTempDir = qScopeGuard([usedNotExistedDir, dir]() + { + if (usedNotExistedDir) + { + QDir(dir).rmpath(QChar('.')); + } + }); + + if (fileName.isEmpty()) + { + return false; + } + + QFileInfo f( fileName ); + if (f.suffix().isEmpty() && f.suffix() != suffix) + { + fileName += QChar('.') + suffix; + } + + if (QFileInfo::exists(fileName) && curFile != fileName) + { + // Temporary try to lock the file before saving + VLockGuard tmp(fileName); + if (not tmp.IsLocked()) + { + qCCritical(pWindow, "%s", + qUtf8Printable(tr("Failed to lock. This file already opened in another window."))); + return false; + } + } + + // Need for restoring previous state in case of failure +// const bool readOnly = m_layout->IsReadOnly(); + +// m_layout->SetReadOnly(false); + lIsReadOnly = false; + + QString error; + bool result = SaveLayout(fileName, error); + if (not result) + { + QMessageBox messageBox; + messageBox.setIcon(QMessageBox::Warning); + messageBox.setInformativeText(tr("Could not save file")); + messageBox.setDefaultButton(QMessageBox::Ok); + messageBox.setDetailedText(error); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.exec(); + + // Restore previous state +// m_layout->SetReadOnly(readOnly); +// lIsReadOnly = readOnly; + return false; + } + + m_curFileFormatVersion = VLayoutConverter::LayoutMaxVer; + m_curFileFormatVersionStr = VLayoutConverter::LayoutMaxVerStr; + +// UpdatePadlock(false); + UpdateWindowTitle(); + + if (curFile == fileName && not lock.isNull()) + { + lock->Unlock(); + } + VlpCreateLock(lock, fileName); + if (not lock->IsLocked()) + { + qCCritical(pWindow, "%s", qUtf8Printable(tr("Failed to lock. This file already opened in another window. " + "Expect collissions when run 2 copies of the program."))); + return false; + } + return true; } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/puzzle/vpmainwindow.h b/src/app/puzzle/vpmainwindow.h index b2f68435e..7644939be 100644 --- a/src/app/puzzle/vpmainwindow.h +++ b/src/app/puzzle/vpmainwindow.h @@ -43,6 +43,7 @@ #include "vpcommandline.h" #include "../vlayout/vlayoutdef.h" #include "../vwidgets/vabstractmainwindow.h" +#include "../vmisc/vlockguard.h" namespace Ui { @@ -65,12 +66,15 @@ public: */ bool LoadFile(QString path); + void LayoutWasSaved(bool saved); + void SetCurrentFile(const QString &fileName); + /** - * @brief SaveFile Saves the current layout to the layout file of given path + * @brief SaveLayout Saves the current layout to the layout file of given path * @param path path to layout file * @return true if success */ - bool SaveFile(const QString &path); + bool SaveLayout(const QString &path, QString &error); /** * @brief ImportRawLayouts The function imports the raw layouts of given paths @@ -110,14 +114,14 @@ private slots: * triggered. * The slot is automatically connected through name convention. */ - void on_actionSave_triggered(); + bool on_actionSave_triggered(); /** * @brief on_actionSaveAs_triggered When the menu action File > Save As * is triggered. * The slot is automatically connected through name convention. */ - void on_actionSaveAs_triggered(); + bool on_actionSaveAs_triggered(); /** * @brief on_actionImportRawLayout_triggered When the menu action @@ -424,6 +428,8 @@ private: bool isInitialized{false}; bool lIsReadOnly{false}; + QSharedPointer> lock{nullptr}; + /** * @brief CreatePiece creates a piece from the given VLayoutPiece data * @param rawPiece the raw piece data diff --git a/src/app/puzzle/xml/vplayoutfilewriter.cpp b/src/app/puzzle/xml/vplayoutfilewriter.cpp index 5f2295f41..3991483d5 100644 --- a/src/app/puzzle/xml/vplayoutfilewriter.cpp +++ b/src/app/puzzle/xml/vplayoutfilewriter.cpp @@ -34,18 +34,6 @@ #include "vplayoutliterals.h" #include "../ifc/xml/vlayoutconverter.h" -//--------------------------------------------------------------------------------------------------------------------- -VPLayoutFileWriter::VPLayoutFileWriter() -{ - -} - -//--------------------------------------------------------------------------------------------------------------------- -VPLayoutFileWriter::~VPLayoutFileWriter() -{ - -} - //--------------------------------------------------------------------------------------------------------------------- void VPLayoutFileWriter::WriteFile(VPLayout *layout, QFile *file) { diff --git a/src/app/puzzle/xml/vplayoutfilewriter.h b/src/app/puzzle/xml/vplayoutfilewriter.h index 4498277c4..2836c5186 100644 --- a/src/app/puzzle/xml/vplayoutfilewriter.h +++ b/src/app/puzzle/xml/vplayoutfilewriter.h @@ -31,6 +31,7 @@ #include #include +#include #include "../vmisc/literals.h" @@ -43,14 +44,14 @@ class QMarginsF; class VPLayoutFileWriter : public QXmlStreamWriter { + Q_DECLARE_TR_FUNCTIONS(VPLayoutFileWriter) public: - VPLayoutFileWriter(); - ~VPLayoutFileWriter(); + VPLayoutFileWriter()= default; + ~VPLayoutFileWriter()= default; void WriteFile(VPLayout *layout, QFile *file); private: - void WriteLayout(VPLayout *layout); void WriteProperties(VPLayout *layout); void WriteUnplacePiecesList(VPLayout *layout); @@ -69,7 +70,6 @@ private: template void SetAttribute(const QString &name, const char (&value)[N]); - }; //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/tape/tmainwindow.cpp b/src/app/tape/tmainwindow.cpp index fc9b63a8f..4670eeeb7 100644 --- a/src/app/tape/tmainwindow.cpp +++ b/src/app/tape/tmainwindow.cpp @@ -775,48 +775,9 @@ bool TMainWindow::FileSave() return false; } -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup++; // turn checking on -#endif /*Q_OS_WIN32*/ - const bool isFileWritable = QFileInfo(curFile).isWritable(); -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup--; // turn it off again -#endif /*Q_OS_WIN32*/ - if (not isFileWritable) + if (not CheckFilePermissions(curFile, this)) { - QMessageBox messageBox(this); - messageBox.setIcon(QMessageBox::Question); - messageBox.setText(tr("The measurements document has no write permissions.")); - messageBox.setInformativeText(tr("Do you want to change the premissions?")); - messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - messageBox.setDefaultButton(QMessageBox::Yes); - - if (messageBox.exec() == QMessageBox::Yes) - { -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup++; // turn checking on -#endif /*Q_OS_WIN32*/ - bool changed = QFile::setPermissions(curFile, - QFileInfo(curFile).permissions() | QFileDevice::WriteUser); -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup--; // turn it off again -#endif /*Q_OS_WIN32*/ - - if (not changed) - { - messageBox.setIcon(QMessageBox::Warning); - messageBox.setText(tr("Cannot set permissions for %1 to writable.").arg(curFile)); - messageBox.setInformativeText(tr("Could not save the file.")); - messageBox.setStandardButtons(QMessageBox::Ok); - messageBox.setDefaultButton(QMessageBox::Ok); - messageBox.exec(); - return false; - } - } - else - { - return false; - } + return false; } QString error; diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index 1cb786c75..409f5c84f 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -3253,52 +3253,9 @@ bool MainWindow::on_actionSave_triggered() return false; } -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup++; // turn checking on -#endif /*Q_OS_WIN32*/ - const bool isFileWritable = QFileInfo(VAbstractValApplication::VApp()->GetPatternPath()).isWritable(); -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup--; // turn it off again -#endif /*Q_OS_WIN32*/ - if (not isFileWritable) + if (not CheckFilePermissions(VAbstractValApplication::VApp()->GetPatternPath(), this)) { - QMessageBox messageBox(this); - messageBox.setIcon(QMessageBox::Question); - messageBox.setText(tr("The document has no write permissions.")); - messageBox.setInformativeText(tr("Do you want to change the premissions?")); - messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - messageBox.setDefaultButton(QMessageBox::Yes); - - if (messageBox.exec() == QMessageBox::Yes) - { -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup++; // turn checking on -#endif /*Q_OS_WIN32*/ - bool changed = - QFile::setPermissions(VAbstractValApplication::VApp()->GetPatternPath(), - QFileInfo(VAbstractValApplication::VApp() - ->GetPatternPath()).permissions() | QFileDevice::WriteUser); -#ifdef Q_OS_WIN32 - qt_ntfs_permission_lookup--; // turn it off again -#endif /*Q_OS_WIN32*/ - - if (not changed) - { - QMessageBox messageBox(this); - messageBox.setIcon(QMessageBox::Warning); - messageBox.setText(tr("Cannot set permissions for %1 to writable.") - .arg(VAbstractValApplication::VApp()->GetPatternPath())); - messageBox.setInformativeText(tr("Could not save the file.")); - messageBox.setDefaultButton(QMessageBox::Ok); - messageBox.setStandardButtons(QMessageBox::Ok); - messageBox.exec(); - return false; - } - } - else - { - return false; - } + return false; } QString error; diff --git a/src/libs/vmisc/vcommonsettings.cpp b/src/libs/vmisc/vcommonsettings.cpp index 1aa73dc8d..b7425823b 100644 --- a/src/libs/vmisc/vcommonsettings.cpp +++ b/src/libs/vmisc/vcommonsettings.cpp @@ -73,6 +73,7 @@ namespace { Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingPathsIndividualMeasurements, (QLatin1String("paths/individual_measurements"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingPathsMultisizeMeasurements, (QLatin1String("paths/standard_measurements"))) +Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingPathsLayouts, (QLatin1String("paths/layouts"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingPathsPattern, (QLatin1String("paths/pattern"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingPathsTemplates, (QLatin1String("paths/templates"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingPathsLabelTemplate, (QLatin1String("paths/labels"))) @@ -406,6 +407,27 @@ void VCommonSettings::SetPathMultisizeMeasurements(const QString &value) settings.sync(); } +//--------------------------------------------------------------------------------------------------------------------- +QString VCommonSettings::GetDefPathLayouts() +{ + return QDir::homePath() + QLatin1String("/valentina/") + tr("layouts"); +} + +//--------------------------------------------------------------------------------------------------------------------- +QString VCommonSettings::GetPathLayouts() const +{ + QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename); + return settings.value(*settingPathsLayouts, GetDefPathLayouts()).toString(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCommonSettings::SetPathLayouts(const QString &value) +{ + QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename); + settings.setValue(*settingPathsLayouts, value); + settings.sync(); +} + //--------------------------------------------------------------------------------------------------------------------- QString VCommonSettings::GetDefPathPattern() { diff --git a/src/libs/vmisc/vcommonsettings.h b/src/libs/vmisc/vcommonsettings.h index 6c4f90897..7ff8d4666 100644 --- a/src/libs/vmisc/vcommonsettings.h +++ b/src/libs/vmisc/vcommonsettings.h @@ -1,4 +1,4 @@ -/************************************************************************ +/************************************************************************ ** ** @file vcommonsettings.h ** @author Roman Telezhynskyi @@ -68,6 +68,10 @@ public: QString GetPathMultisizeMeasurements() const; void SetPathMultisizeMeasurements(const QString &value); + static QString GetDefPathLayouts(); + QString GetPathLayouts() const; + void SetPathLayouts(const QString &value); + static QString GetDefPathPattern(); QString GetPathPattern() const; void SetPathPattern(const QString &value); diff --git a/src/libs/vwidgets/vabstractmainwindow.cpp b/src/libs/vwidgets/vabstractmainwindow.cpp index c4c259f86..230528307 100644 --- a/src/libs/vwidgets/vabstractmainwindow.cpp +++ b/src/libs/vwidgets/vabstractmainwindow.cpp @@ -236,6 +236,54 @@ void VAbstractMainWindow::UpdateRecentFileActions() m_separatorAct->setVisible(numRecentFiles>0); } +//--------------------------------------------------------------------------------------------------------------------- +auto VAbstractMainWindow::CheckFilePermissions(const QString &path, QWidget *messageBoxParent) -> bool +{ +#ifdef Q_OS_WIN32 + qt_ntfs_permission_lookup++; // turn checking on +#endif /*Q_OS_WIN32*/ + const bool isFileWritable = QFileInfo(path).isWritable(); +#ifdef Q_OS_WIN32 + qt_ntfs_permission_lookup--; // turn it off again +#endif /*Q_OS_WIN32*/ + if (not isFileWritable) + { + QMessageBox messageBox(messageBoxParent); + messageBox.setIcon(QMessageBox::Question); + messageBox.setText(tr("The measurements document has no write permissions.")); + messageBox.setInformativeText(tr("Do you want to change the premissions?")); + messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + messageBox.setDefaultButton(QMessageBox::Yes); + + if (messageBox.exec() == QMessageBox::Yes) + { +#ifdef Q_OS_WIN32 + qt_ntfs_permission_lookup++; // turn checking on +#endif /*Q_OS_WIN32*/ + bool changed = QFile::setPermissions(path, QFileInfo(path).permissions() | QFileDevice::WriteUser); +#ifdef Q_OS_WIN32 + qt_ntfs_permission_lookup--; // turn it off again +#endif /*Q_OS_WIN32*/ + + if (not changed) + { + messageBox.setIcon(QMessageBox::Warning); + messageBox.setText(tr("Cannot set permissions for %1 to writable.").arg(path)); + messageBox.setInformativeText(tr("Could not save the file.")); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.setDefaultButton(QMessageBox::Ok); + messageBox.exec(); + return false; + } + } + else + { + return false; + } + } + return true; +} + //--------------------------------------------------------------------------------------------------------------------- void VAbstractMainWindow::WindowsLocale() { diff --git a/src/libs/vwidgets/vabstractmainwindow.h b/src/libs/vwidgets/vabstractmainwindow.h index 070201708..ee803c5a8 100644 --- a/src/libs/vwidgets/vabstractmainwindow.h +++ b/src/libs/vwidgets/vabstractmainwindow.h @@ -71,6 +71,8 @@ protected: virtual QStringList RecentFileList() const =0; void UpdateRecentFileActions(); + static bool CheckFilePermissions(const QString &path, QWidget *messageBoxParent=nullptr) ; + private: Q_DISABLE_COPY(VAbstractMainWindow) };