From 3b37d22a0b4b7ed1d18db0859e1d4caf36aa64fb Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Tue, 21 Apr 2020 19:02:21 +0300 Subject: [PATCH] Actions Open Puzzle, Create Manual Layout, Update Manual Layout in Valentina. --- src/app/valentina/core/vapplication.cpp | 77 ++---- src/app/valentina/core/vapplication.h | 1 + .../valentina/dialogs/dialogsavelayout.cpp | 12 + src/app/valentina/mainwindow.cpp | 146 +++++++++++ src/app/valentina/mainwindow.h | 4 + src/app/valentina/mainwindow.ui | 57 +++- src/app/valentina/mainwindowsnogui.cpp | 26 +- src/app/valentina/mainwindowsnogui.h | 4 +- src/libs/vlayout/vlayout.pri | 2 + src/libs/vlayout/vlayoutdef.h | 1 + src/libs/vlayout/vrawlayout.cpp | 248 ++++++++++++++++++ src/libs/vlayout/vrawlayout.h | 73 ++++++ 12 files changed, 592 insertions(+), 59 deletions(-) create mode 100644 src/libs/vlayout/vrawlayout.cpp create mode 100644 src/libs/vlayout/vrawlayout.h diff --git a/src/app/valentina/core/vapplication.cpp b/src/app/valentina/core/vapplication.cpp index 493a5392e..91514f439 100644 --- a/src/app/valentina/core/vapplication.cpp +++ b/src/app/valentina/core/vapplication.cpp @@ -62,6 +62,28 @@ QT_WARNING_POP Q_DECL_CONSTEXPR auto DAYS_TO_KEEP_LOGS = 3; +namespace +{ +QString AppFilePath(const QString &appName) +{ + QString appNameExe = appName; +#ifdef Q_OS_WIN + appNameExe += ".exe"; +#endif + QFileInfo canonicalFile(QString("%1/%2").arg(QCoreApplication::applicationDirPath(), appNameExe)); + if (canonicalFile.exists()) + { + return canonicalFile.absoluteFilePath(); + } + else + { + QFileInfo debugFile(QString("%1/../../%2/bin/%3") + .arg(QCoreApplication::applicationDirPath(), appName, appNameExe)); + return debugFile.exists() ? debugFile.absoluteFilePath() : appNameExe; + } +} +} + //--------------------------------------------------------------------------------------------------------------------- inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { @@ -447,54 +469,13 @@ void VApplication::ActivateDarkMode() //--------------------------------------------------------------------------------------------------------------------- QString VApplication::TapeFilePath() const { - const QString tape = QStringLiteral("tape"); -#ifdef Q_OS_WIN - QFileInfo tapeFile(QCoreApplication::applicationDirPath() + "/" + tape + ".exe"); - if (tapeFile.exists()) - { - return tapeFile.absoluteFilePath(); - } - else - { - return QCoreApplication::applicationDirPath() + "/../../tape/bin/" + tape + ".exe"; - } -#elif defined(Q_OS_MAC) - QFileInfo tapeFile(QCoreApplication::applicationDirPath() + "/" + tape); - if (tapeFile.exists()) - { - return tapeFile.absoluteFilePath(); - } - else - { - QFileInfo file(QCoreApplication::applicationDirPath() + "/../../tape/bin/" + tape); - if (file.exists()) - { - return file.absoluteFilePath(); - } - else - { - return tape; - } - } -#else // Unix - QFileInfo file(QCoreApplication::applicationDirPath() + "/../../tape/bin/" + tape); - if (file.exists()) - { - return file.absoluteFilePath(); - } - else - { - QFileInfo tapeFile(QCoreApplication::applicationDirPath() + "/" + tape); - if (tapeFile.exists()) - { - return tapeFile.absoluteFilePath(); - } - else - { - return tape; - } - } -#endif + return AppFilePath(QStringLiteral("tape")); +} + +//--------------------------------------------------------------------------------------------------------------------- +QString VApplication::PuzzleFilePath() const +{ + return AppFilePath(QStringLiteral("puzzle")); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/valentina/core/vapplication.h b/src/app/valentina/core/vapplication.h index d01d0f2ab..94dc56eff 100644 --- a/src/app/valentina/core/vapplication.h +++ b/src/app/valentina/core/vapplication.h @@ -58,6 +58,7 @@ public: void InitOptions(); QString TapeFilePath() const; + QString PuzzleFilePath() const; QTimer *getAutoSaveTimer() const; void setAutoSaveTimer(QTimer *value); diff --git a/src/app/valentina/dialogs/dialogsavelayout.cpp b/src/app/valentina/dialogs/dialogsavelayout.cpp index ecac7451b..58ec2458a 100644 --- a/src/app/valentina/dialogs/dialogsavelayout.cpp +++ b/src/app/valentina/dialogs/dialogsavelayout.cpp @@ -110,6 +110,7 @@ DialogSaveLayout::DialogSaveLayout(int count, Draw mode, const QString &fileName } else { + RemoveFormatFromList(LayoutExportFormats::RLD); ui->checkBoxTextAsPaths->setVisible(false); } @@ -238,6 +239,7 @@ void DialogSaveLayout::SetBinaryDXFFormat(bool binary) case LayoutExportFormats::PS: case LayoutExportFormats::EPS: case LayoutExportFormats::NC: + case LayoutExportFormats::RLD: default: ui->checkBoxBinaryDXF->setChecked(false); break; @@ -285,6 +287,7 @@ bool DialogSaveLayout::IsBinaryDXFFormat() const case LayoutExportFormats::PS: case LayoutExportFormats::EPS: case LayoutExportFormats::NC: + case LayoutExportFormats::RLD: default: return false; } @@ -424,6 +427,8 @@ QString DialogSaveLayout::ExportFormatDescription(LayoutExportFormats format) return QStringLiteral("PDF %1 %2 (*.pdf)").arg(tr("tiled"), filesStr); case LayoutExportFormats::NC: return QStringLiteral("%1 %2 (*.nc)").arg(tr("Numerical control"), filesStr); + case LayoutExportFormats::RLD: + return QStringLiteral("%1 %2 (*.rld)").arg(tr("Raw Layout Data"), filesStr); default: return QString(); } @@ -477,6 +482,8 @@ QString DialogSaveLayout::ExportFormatSuffix(LayoutExportFormats format) return QStringLiteral(".dxf"); case LayoutExportFormats::NC: return QStringLiteral(".nc"); + case LayoutExportFormats::RLD: + return QStringLiteral(".rld"); default: return QString(); } @@ -566,6 +573,7 @@ void DialogSaveLayout::ShowExample() ui->checkBoxBinaryDXF->setEnabled(false); ui->groupBoxPaperFormat->setEnabled(false); ui->groupBoxMargins->setEnabled(false); + ui->checkBoxTextAsPaths->setEnabled(true); switch(currentFormat) { @@ -602,6 +610,9 @@ void DialogSaveLayout::ShowExample() ui->groupBoxPaperFormat->setEnabled(true); ui->groupBoxMargins->setEnabled(true); break; + case LayoutExportFormats::RLD: + ui->checkBoxTextAsPaths->setEnabled(false); + break; case LayoutExportFormats::SVG: case LayoutExportFormats::PDF: case LayoutExportFormats::PNG: @@ -869,6 +880,7 @@ QVector > DialogSaveLayout::InitFormats( InitFormat(LayoutExportFormats::DXF_AC1027_ASTM); InitFormat(LayoutExportFormats::PDFTiled); // InitFormat(LayoutExportFormats::NC); + InitFormat(LayoutExportFormats::RLD); return list; } diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index f5bb15524..205533881 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -3149,6 +3149,148 @@ void MainWindow::on_actionOpen_triggered() LoadPattern(filePath); } +//--------------------------------------------------------------------------------------------------------------------- +void MainWindow::on_actionOpenPuzzle_triggered() +{ + const QString puzzle = qApp->PuzzleFilePath(); + const QString workingDirectory = QFileInfo(puzzle).absoluteDir().absolutePath(); + + QStringList arguments; + if (isNoScaling) + { + arguments.append(QLatin1String("--") + LONG_OPTION_NO_HDPI_SCALING); + } + + QProcess::startDetached(puzzle, arguments, workingDirectory); +} + +//--------------------------------------------------------------------------------------------------------------------- +void MainWindow::on_actionCreateManualLayout_triggered() +{ + QTemporaryFile rldFile(QDir::tempPath()+"/puzzle.rld.XXXXXX"); + if (rldFile.open()) + { + QVector detailsInLayout = SortDetailsForLayout(pattern->DataPieces()); + + if (detailsInLayout.count() == 0) + { + QMessageBox::information(this, tr("Layout mode"), tr("You don't have enough details to export. Please, " + "include at least one detail in layout."), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + QVector listDetails; + try + { + listDetails = PrepareDetailsForLayout(detailsInLayout); + } + catch (VException &e) + { + QMessageBox::warning(this, tr("Export details"), + tr("Can't export details.") + QLatin1String(" \n") + e.ErrorMessage(), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + RLDFile(rldFile.fileName(), listDetails); + + QStringList arguments {"-r", rldFile.fileName()}; + if (isNoScaling) + { + arguments.append(QLatin1String("--") + LONG_OPTION_NO_HDPI_SCALING); + } + + rldFile.setAutoRemove(false); + + const QString puzzle = qApp->PuzzleFilePath(); + const QString workingDirectory = QFileInfo(puzzle).absoluteDir().absolutePath(); + QProcess::startDetached(puzzle, arguments, workingDirectory); + } + else + { + qCCritical(vMainWindow) << tr("Unable to prepare raw layout data."); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void MainWindow::on_actionUpdateManualLayout_triggered() +{ + const QString filter(tr("Manual layout files") + QLatin1String(" (*.vlt)")); + + //Use standard path to manual layouts + const QString path = qApp->ValentinaSettings()->GetPathManualLayouts(); + + bool usedNotExistedDir = false; + QDir directory(path); + if (not directory.exists()) + { + usedNotExistedDir = directory.mkpath(QChar('.')); + } + + auto RemoveUnsuded = qScopeGuard([usedNotExistedDir, path]() + { + if (usedNotExistedDir) + { + QDir directory(path); + directory.rmpath(QChar('.')); + } + }); + + const QString filePath = QFileDialog::getOpenFileName(this, tr("Select manual layout"), path, filter, nullptr); + + if (filePath.isEmpty()) + { + return; + } + + QTemporaryFile rldFile(QDir::tempPath()+"/puzzle.rld.XXXXXX"); + rldFile.setAutoRemove(false); + if (rldFile.open()) + { + QVector detailsInLayout = SortDetailsForLayout(pattern->DataPieces()); + + if (detailsInLayout.count() == 0) + { + QMessageBox::information(this, tr("Layout mode"), tr("You don't have enough details to export. Please, " + "include at least one detail in layout."), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + QVector listDetails; + try + { + listDetails = PrepareDetailsForLayout(detailsInLayout); + } + catch (VException &e) + { + QMessageBox::warning(this, tr("Export details"), + tr("Can't export details.") + QLatin1String(" \n") + e.ErrorMessage(), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + RLDFile(rldFile.fileName(), listDetails); + + QStringList arguments {filePath, "-r", rldFile.fileName()}; + if (isNoScaling) + { + arguments.append(QLatin1String("--") + LONG_OPTION_NO_HDPI_SCALING); + } + + rldFile.setAutoRemove(false); + + const QString puzzle = qApp->PuzzleFilePath(); + const QString workingDirectory = QFileInfo(puzzle).absoluteDir().absolutePath(); + QProcess::startDetached(puzzle, arguments, workingDirectory); + } + else + { + qCCritical(vMainWindow) << tr("Unable to prepare raw layout data."); + } +} + //--------------------------------------------------------------------------------------------------------------------- /** * @brief Clear reset to default window. @@ -3554,7 +3696,9 @@ void MainWindow::SetEnableWidgets(bool enable) { const bool drawStage = (qApp->GetDrawMode() == Draw::Calculation); const bool detailsStage = (qApp->GetDrawMode() == Draw::Modeling); + const bool layoutStage = (qApp->GetDrawMode() == Draw::Layout); const bool designStage = (drawStage || detailsStage); + const bool piecesStage = (detailsStage || layoutStage); comboBoxDraws->setEnabled(enable && drawStage); ui->actionOptionDraw->setEnabled(enable && drawStage); @@ -3588,6 +3732,8 @@ void MainWindow::SetEnableWidgets(bool enable) ui->actionDecreaseLabelFont->setEnabled(enable); ui->actionOriginalLabelFont->setEnabled(enable); ui->actionHideLabels->setEnabled(enable); + ui->actionCreateManualLayout->setEnabled(enable && piecesStage); + ui->actionUpdateManualLayout->setEnabled(enable && piecesStage); ui->actionLoadWatermark->setEnabled(enable); ui->actionRemoveWatermark->setEnabled(enable && not doc->GetWatermarkPath().isEmpty()); diff --git a/src/app/valentina/mainwindow.h b/src/app/valentina/mainwindow.h index 9602839d0..f5b30d68f 100644 --- a/src/app/valentina/mainwindow.h +++ b/src/app/valentina/mainwindow.h @@ -184,6 +184,10 @@ private slots: bool on_actionSave_triggered(); void on_actionOpen_triggered(); + void on_actionOpenPuzzle_triggered(); + void on_actionCreateManualLayout_triggered(); + void on_actionUpdateManualLayout_triggered(); + void ClosedDialogUnionDetails(int result); void ClosedDialogDuplicateDetail(int result); void ClosedDialogGroup(int result); diff --git a/src/app/valentina/mainwindow.ui b/src/app/valentina/mainwindow.ui index 0a78e70ba..c45ac3910 100644 --- a/src/app/valentina/mainwindow.ui +++ b/src/app/valentina/mainwindow.ui @@ -372,7 +372,7 @@ 0 0 - 104 + 140 108 @@ -481,7 +481,7 @@ 0 0 - 104 + 126 243 @@ -746,7 +746,7 @@ 0 0 - 104 + 126 282 @@ -1037,7 +1037,7 @@ 0 0 - 98 + 140 102 @@ -1120,7 +1120,7 @@ 0 0 - 104 + 126 192 @@ -1324,7 +1324,7 @@ 0 0 - 104 + 126 237 @@ -1589,8 +1589,8 @@ 0 0 - 104 - 57 + 140 + 170 @@ -1690,7 +1690,7 @@ 0 0 1100 - 22 + 21 @@ -1804,12 +1804,21 @@ + + + Manual Layout + + + + + + @@ -3026,6 +3035,36 @@ Create or edit a watermark + + + Open Puzzle + + + Open the Puzzle app + + + + + false + + + Create + + + Create manual layout + + + + + false + + + Update + + + Update manual layout + + diff --git a/src/app/valentina/mainwindowsnogui.cpp b/src/app/valentina/mainwindowsnogui.cpp index 559a02dcd..adfb73090 100644 --- a/src/app/valentina/mainwindowsnogui.cpp +++ b/src/app/valentina/mainwindowsnogui.cpp @@ -53,6 +53,7 @@ #include "../ifc/xml/vvstconverter.h" #include "../ifc/xml/vvitconverter.h" #include "../ifc/xml/vwatermarkconverter.h" +#include "../vlayout/vrawlayout.h" #include #include @@ -523,7 +524,8 @@ void MainWindowsNoGUI::ExportData(const QVector &listDetails) format == LayoutExportFormats::DXF_AC1018_ASTM || format == LayoutExportFormats::DXF_AC1021_ASTM || format == LayoutExportFormats::DXF_AC1024_ASTM || - format == LayoutExportFormats::DXF_AC1027_ASTM) + format == LayoutExportFormats::DXF_AC1027_ASTM || + format == LayoutExportFormats::RLD) { if (m_dialogSaveLayout->Mode() == Draw::Layout) { @@ -731,6 +733,9 @@ void MainWindowsNoGUI::ExportApparelLayout(const QVector &details, case LayoutExportFormats::DXF_AC1027_AAMA: AAMADxfFile(name, DRW::AC1027, m_dialogSaveLayout->IsBinaryDXFFormat(), size, details); break; + case LayoutExportFormats::RLD: + RLDFile(name, details, m_dialogSaveLayout->GetXScale(), m_dialogSaveLayout->GetYScale()); + break; default: qDebug() << "Can't recognize file type." << Q_FUNC_INFO; break; @@ -1488,6 +1493,25 @@ void MainWindowsNoGUI::ASTMDxfFile(const QString &name, int version, bool binary generator.ExportToASTM(details); } +//--------------------------------------------------------------------------------------------------------------------- +void MainWindowsNoGUI::RLDFile(const QString &name, QVector details, qreal xScale, qreal yScale) const +{ + for(auto detail : details) + { + detail.Scale(xScale, yScale); + } + + VRawLayoutData layoutDate; + layoutDate.pieces = details; + + VRawLayout generator; + if (not generator.WriteFile(name, layoutDate)) + { + const QString errorMsg = tr("Export raw layout data failed. %1.").arg(generator.ErrorString()); + qApp->IsPedantic() ? throw VException(errorMsg) : qCritical() << errorMsg; + } +} + QT_WARNING_POP //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/valentina/mainwindowsnogui.h b/src/app/valentina/mainwindowsnogui.h index 77a69eb86..52df26f62 100644 --- a/src/app/valentina/mainwindowsnogui.h +++ b/src/app/valentina/mainwindowsnogui.h @@ -150,6 +150,8 @@ protected: QSharedPointer OpenMeasurementFile(const QString &path) const; void CheckRequiredMeasurements(const VMeasurements *m) const; + + void RLDFile(const QString &name, QVector details, qreal xScale=1, qreal yScale=1) const; private slots: void PrintPages (QPrinter *printer); private: @@ -186,7 +188,7 @@ private: void AAMADxfFile(const QString &name, int version, bool binary, const QSize &size, const QVector &details) const; void ASTMDxfFile(const QString &name, int version, bool binary, const QSize &size, - const QVector &details) const; + const QVector &details) const; void PreparePaper(int index) const; void RestorePaper(int index) const; diff --git a/src/libs/vlayout/vlayout.pri b/src/libs/vlayout/vlayout.pri index fe16ecc40..752c7f27a 100644 --- a/src/libs/vlayout/vlayout.pri +++ b/src/libs/vlayout/vlayout.pri @@ -13,6 +13,7 @@ HEADERS += \ $$PWD/vcontour_p.h \ $$PWD/vbestsquare.h \ $$PWD/vposition.h \ + $$PWD/vrawlayout.h \ $$PWD/vsapoint.h \ $$PWD/vtextmanager.h \ $$PWD/vposter.h \ @@ -34,6 +35,7 @@ SOURCES += \ $$PWD/vcontour.cpp \ $$PWD/vbestsquare.cpp \ $$PWD/vposition.cpp \ + $$PWD/vrawlayout.cpp \ $$PWD/vtextmanager.cpp \ $$PWD/vposter.cpp \ $$PWD/vgraphicsfillitem.cpp \ diff --git a/src/libs/vlayout/vlayoutdef.h b/src/libs/vlayout/vlayoutdef.h index c6a3ed070..f110864b7 100644 --- a/src/libs/vlayout/vlayoutdef.h +++ b/src/libs/vlayout/vlayoutdef.h @@ -72,6 +72,7 @@ enum class LayoutExportFormats : qint8 DXF_AC1027_ASTM = 32, /* ACAD 2013. */ PDFTiled = 33, NC = 34, /*G-code. Reserved for future*/ + RLD = 35, /*Raw Layout Data*/ COUNT /*Use only for validation*/ }; diff --git a/src/libs/vlayout/vrawlayout.cpp b/src/libs/vlayout/vrawlayout.cpp new file mode 100644 index 000000000..42d404530 --- /dev/null +++ b/src/libs/vlayout/vrawlayout.cpp @@ -0,0 +1,248 @@ +/************************************************************************ + ** + ** @file vrawlayout.cpp + ** @author Roman Telezhynskyi + ** @date 21 4, 2020 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2020 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina 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 General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ +#include "vrawlayout.h" + +#include +#include +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) +#include "../vmisc/backport/qscopeguard.h" +#else +#include +#endif + +#include "../vmisc/def.h" +#include "../vmisc/vdatastreamenum.h" +#include "../ifc/exception/vexception.h" + +const QByteArray VRawLayout::fileHeaderByteArray = QByteArray("RLD!..."); +const quint16 VRawLayout::fileVersion = 1; + +const quint32 VRawLayoutData::streamHeader = 0x8B0E8A27; // CRC-32Q string "VRawLayoutData" +const quint16 VRawLayoutData::classVersion = 1; + +//--------------------------------------------------------------------------------------------------------------------- +QDataStream &operator<<(QDataStream &dataStream, const VRawLayoutData &data) +{ + dataStream << VRawLayoutData::streamHeader << VRawLayoutData::classVersion; + + // Added in classVersion = 1 + dataStream << data.pieces; + + // Added in classVersion = 2 + + return dataStream; +} + +//--------------------------------------------------------------------------------------------------------------------- +QDataStream &operator>>(QDataStream &dataStream, VRawLayoutData &data) +{ + quint32 actualStreamHeader = 0; + dataStream >> actualStreamHeader; + + if (actualStreamHeader != VRawLayoutData::streamHeader) + { + QString message = QCoreApplication::tr("VRawLayoutData prefix mismatch error: actualStreamHeader = 0x%1 and " + "streamHeader = 0x%2") + .arg(actualStreamHeader, 8, 0x10, QChar('0')) + .arg(VRawLayoutData::streamHeader, 8, 0x10, QChar('0')); + throw VException(message); + } + + quint16 actualClassVersion = 0; + dataStream >> actualClassVersion; + + if (actualClassVersion > VRawLayoutData::classVersion) + { + QString message = QCoreApplication::tr("VRawLayoutData compatibility error: actualClassVersion = %1 and " + "classVersion = %2") + .arg( actualClassVersion ).arg(VRawLayoutData::classVersion); + throw VException(message); + } + + dataStream >> data.pieces; + +// if (actualClassVersion >= 2) +// { +// // read value in version 2 +// } + + return dataStream; +} + +//--------------------------------------------------------------------------------------------------------------------- +VRawLayout::VRawLayout() +{} + +//--------------------------------------------------------------------------------------------------------------------- +bool VRawLayout::WriteFile(QIODevice *ioDevice, const VRawLayoutData &data) +{ + SCASSERT(ioDevice != nullptr) + m_errorString.clear(); + + const bool wasOpen = ioDevice->isOpen(); + + if (wasOpen || ioDevice->open(QIODevice::WriteOnly)) + { + QDataStream dataStream(ioDevice); + dataStream.setVersion(QDataStream::Qt_5_4); + + // Don't use the << operator for QByteArray. See the note in ReadFile() below. + dataStream.writeRawData(fileHeaderByteArray.constData(), fileHeaderByteArray.size()); + dataStream << fileVersion; + dataStream << data; + + if (not wasOpen) + { + ioDevice->close(); // Only close this if it was opened by this function. + } + return true; + } + else + { + m_errorString = ioDevice->errorString(); + return false; + } + + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VRawLayout::ReadFile(QIODevice *ioDevice, VRawLayoutData &data) +{ + SCASSERT(ioDevice != nullptr) + m_errorString.clear(); + + const bool wasOpen = ioDevice->isOpen(); + + if (wasOpen || ioDevice->open(QIODevice::ReadOnly)) + { + auto CloseFile = qScopeGuard([wasOpen, ioDevice]() + { + if (not wasOpen) // Only close this if it was opened by this function. + { + ioDevice->close(); + } + }); + + QDataStream dataStream(ioDevice); + dataStream.setVersion(QDataStream::Qt_5_4); + + // Note: we could have used the QDataStream << and >> operators on QByteArray but since the first + // bytes of the stream will be the size of the array, we might end up attempting to allocate + // a large amount of memory if the wrong file type was read. Instead, we'll just read the + // same number of bytes that are in the array we are comparing it to. No size was written. + const int len = fileHeaderByteArray.size(); + QByteArray actualFileHeaderByteArray( len, '\0' ); + dataStream.readRawData( actualFileHeaderByteArray.data(), len ); + + if (actualFileHeaderByteArray != fileHeaderByteArray) + { + // prefixes don't match + m_errorString = tr("VRawLayout::ReadFile() failed. Raw layout format prefix mismatch error."); + return false; + } + + quint16 actualFileVersion = 0; + dataStream >> actualFileVersion; + + if (actualFileVersion > fileVersion) + { + // file is from a future version that we don't know how to load + m_errorString = tr("VRawLayout::ReadFile() failed.\n" + "Raw layout format compatibility error: actualFileVersion = %1 and fileVersion = %2" ) + .arg(actualFileVersion).arg(fileVersion); + return false; + } + + try + { + // This may throw an exception if one of the VRawLayoutData objects is corrupt or unsupported. + // For example, if this file is from a future version of this code. + dataStream >> data; + } + catch (const VException& e) + { + qCritical() << e.ErrorMessage(); + + m_errorString = e.ErrorMessage(); + return false; + } + + return true; + } + else + { + m_errorString = ioDevice->errorString(); + return false; + } + + return true; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VRawLayout::WriteFile(const QString &filePath, const VRawLayoutData &data) +{ + QFile file(filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + auto CloseFile = qScopeGuard([&file]() + { + file.flush(); + file.close(); + }); + return WriteFile(&file, data); + } + else + { + m_errorString = file.errorString(); + return false; + } +} + +//--------------------------------------------------------------------------------------------------------------------- +bool VRawLayout::ReadFile(const QString &filePath, VRawLayoutData &data) +{ + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) + { + auto CloseFile = qScopeGuard([&file]() + { + file.flush(); + file.close(); + }); + return ReadFile(&file, data); + } + else + { + m_errorString = file.errorString(); + return false; + } +} diff --git a/src/libs/vlayout/vrawlayout.h b/src/libs/vlayout/vrawlayout.h new file mode 100644 index 000000000..6521f62e7 --- /dev/null +++ b/src/libs/vlayout/vrawlayout.h @@ -0,0 +1,73 @@ +/************************************************************************ + ** + ** @file vrawlayout.h + ** @author Roman Telezhynskyi + ** @date 21 4, 2020 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2020 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina 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 General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ +#ifndef VRAWLAYOUT_H +#define VRAWLAYOUT_H + +#include "vlayoutpiece.h" + +struct VRawLayoutData +{ + QVector pieces{}; + + friend QDataStream& operator<< (QDataStream& dataStream, const VRawLayoutData& date); + friend QDataStream& operator>> (QDataStream& dataStream, VRawLayoutData& date); + +private: + static const quint32 streamHeader; + static const quint16 classVersion; +}; + + +class VRawLayout +{ + Q_DECLARE_TR_FUNCTIONS(VRawLayout) +public: + VRawLayout(); + + bool WriteFile(QIODevice* ioDevice, const VRawLayoutData& data); + bool ReadFile(QIODevice* ioDevice, VRawLayoutData& data); + + bool WriteFile(const QString& filePath, const VRawLayoutData& data); + bool ReadFile(const QString& filePath, VRawLayoutData& data); + + QString ErrorString() const; + +private: + QString m_errorString{}; + + static const QByteArray fileHeaderByteArray; + static const quint16 fileVersion; +}; + +//--------------------------------------------------------------------------------------------------------------------- +inline QString VRawLayout::ErrorString() const +{ + return m_errorString; +} + +#endif // VRAWLAYOUT_H