From 9dbe3695b79733afaab701117a407f71434bbbde Mon Sep 17 00:00:00 2001 From: Leonardo Zide Date: Wed, 29 Jul 2026 22:48:12 -0700 Subject: [PATCH] Add new lcResult class to handle task failures more cleanly. --- common/lc_application.cpp | 35 +++++++-- common/lc_bricklink.cpp | 12 ++-- common/lc_bricklink.h | 4 +- common/lc_mainwindow.cpp | 28 +++++--- common/lc_mainwindow.h | 2 + common/lc_result.h | 134 ++++++++++++++++++++++++++++++++++ common/project.cpp | 146 +++++++++++++------------------------- common/project.h | 17 ++--- leocad.pro | 3 + qt/lc_renderdialog.cpp | 18 ++--- 10 files changed, 263 insertions(+), 136 deletions(-) create mode 100644 common/lc_result.h diff --git a/common/lc_application.cpp b/common/lc_application.cpp index 27f48c49..8a2614af 100644 --- a/common/lc_application.cpp +++ b/common/lc_application.cpp @@ -1156,9 +1156,13 @@ lcStartupMode lcApplication::Initialize(const QList>& Libra FileName = FileName.left(FileName.length() - Extension.length() - 1); FileName += ".obj"; } + + lcResult ExportResult = mProject->ExportWavefront(FileName); - if (mProject->ExportWavefront(FileName)) + if (ExportResult) StdOut << tr("Saved '%1'.\n").arg(FileName); + else if (!ExportResult.error().isEmpty()) + StdErr << ExportResult.error(); } if (Options.Save3DS) @@ -1181,9 +1185,13 @@ lcStartupMode lcApplication::Initialize(const QList>& Libra FileName = FileName.left(FileName.length() - Extension.length() - 1); FileName += ".3ds"; } - - if (mProject->Export3DStudio(FileName)) + + lcResult ExportResult = mProject->Export3DStudio(FileName); + + if (ExportResult) StdOut << tr("Saved '%1'.\n").arg(FileName); + else if (!ExportResult.error().isEmpty()) + StdErr << ExportResult.error(); } if (Options.SaveCOLLADA) @@ -1206,9 +1214,13 @@ lcStartupMode lcApplication::Initialize(const QList>& Libra FileName = FileName.left(FileName.length() - Extension.length() - 1); FileName += ".dae"; } + + lcResult ExportResult = mProject->ExportCOLLADA(FileName); - if (mProject->ExportCOLLADA(FileName)) + if (ExportResult) StdOut << tr("Saved '%1'.\n").arg(FileName); + else if (!ExportResult.error().isEmpty()) + StdErr << ExportResult.error(); } if (Options.SaveCSV) @@ -1231,9 +1243,13 @@ lcStartupMode lcApplication::Initialize(const QList>& Libra FileName = FileName.left(FileName.length() - Extension.length() - 1); FileName += ".csv"; } - - if (mProject->ExportCSV(FileName)) + + lcResult ExportResult = mProject->ExportCSV(FileName); + + if (ExportResult) StdOut << tr("Saved '%1'.\n").arg(FileName); + else if (!ExportResult.error().isEmpty()) + StdErr << ExportResult.error(); } if (Options.SaveHTML) @@ -1243,7 +1259,12 @@ lcStartupMode lcApplication::Initialize(const QList>& Libra if (!Options.SaveHTMLName.isEmpty()) HTMLOptions.PathName = Options.SaveHTMLName; - mProject->ExportHTML(HTMLOptions); + lcResult ExportResult = mProject->ExportHTML(HTMLOptions); + + if (ExportResult) + StdOut << tr("Saved '%1'.\n").arg(HTMLOptions.PathName); + else if (!ExportResult.error().isEmpty()) + StdErr << ExportResult.error(); } } diff --git a/common/lc_bricklink.cpp b/common/lc_bricklink.cpp index c85fbb21..1814157a 100644 --- a/common/lc_bricklink.cpp +++ b/common/lc_bricklink.cpp @@ -1,7 +1,8 @@ #include "lc_global.h" +#include "lc_bricklink.h" #include "lc_file.h" #include "lc_library.h" -#include "lc_mainwindow.h" +#include "lc_application.h" #include "lc_string.h" #include "pieceinf.h" #include "lc_colors.h" @@ -59,7 +60,7 @@ public: int mCount; }; -void lcExportBrickLink(const QString& SaveFileName, const lcPartsList& PartsList) +lcResult lcExportBrickLink(const QString& SaveFileName, const lcPartsList& PartsList) { QJsonDocument Document = lcLoadBrickLinkMapping(); QJsonObject Root = Document.object(); @@ -70,10 +71,7 @@ void lcExportBrickLink(const QString& SaveFileName, const lcPartsList& PartsList char Line[1024]; if (!BrickLinkFile.Open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, QObject::tr("LeoCAD"), QObject::tr("Could not open file '%1' for writing.").arg(SaveFileName)); - return; - } + return lcUnexpected(QObject::tr("Could not open file '%1' for writing.").arg(SaveFileName)); std::map Inventory; @@ -127,4 +125,6 @@ void lcExportBrickLink(const QString& SaveFileName, const lcPartsList& PartsList } BrickLinkFile.WriteLine("\n"); + + return lcResult(); } diff --git a/common/lc_bricklink.h b/common/lc_bricklink.h index 6967a883..a97fe089 100644 --- a/common/lc_bricklink.h +++ b/common/lc_bricklink.h @@ -1,3 +1,5 @@ #pragma once -void lcExportBrickLink(const QString& FileName, const lcPartsList& PartsList); +#include "lc_result.h" + +lcResult lcExportBrickLink(const QString& FileName, const lcPartsList& PartsList); diff --git a/common/lc_mainwindow.cpp b/common/lc_mainwindow.cpp index cfb1e29f..8e59d03b 100644 --- a/common/lc_mainwindow.cpp +++ b/common/lc_mainwindow.cpp @@ -1321,15 +1321,15 @@ void lcMainWindow::ShowHTMLDialog() return; Options.SaveDefaults(); - lcGetActiveProject()->ExportHTML(Options); + ShowResultMessageBox(lcGetActiveProject()->ExportHTML(Options)); } void lcMainWindow::ShowExportPOVRayDialog() { - auto [Success, ErrorMessage] = lcGetActiveProject()->ExportPOVRay(QString()); + lcResult ExportResult = lcGetActiveProject()->ExportPOVRay(QString()); - if (!Success && !ErrorMessage.isEmpty()) - QMessageBox::warning(this, tr("POV-Ray Export" ), ErrorMessage); + if (!ExportResult && !ExportResult.error().isEmpty()) + QMessageBox::warning(this, tr("POV-Ray Export" ), ExportResult.error()); } void lcMainWindow::ShowRenderDialog(lcRenderDialogMode RenderDialogMode) @@ -1407,6 +1407,12 @@ void lcMainWindow::ShowSelectDialog() ActiveModel->SetSelectionAndFocusAction(Dialog.mObjects, nullptr, 0, lcSelectionMode::Single); } +void lcMainWindow::ShowResultMessageBox(const lcResult& Result) +{ + if (!Result && !Result.error().isEmpty()) + QMessageBox::information(this, tr("LeoCAD"), Result.error()); +} + void lcMainWindow::SetShadingMode(lcShadingMode ShadingMode) { lcGetPreferences().mShadingMode = ShadingMode; @@ -2483,8 +2489,8 @@ void lcMainWindow::ShowMergeDialog() lcSetProfileString(LC_PROFILE_PROJECTS_PATH, QFileInfo(LoadFileNames.first()).absolutePath()); std::vector> ProjectsToMerge; - - for (const QString& LoadFileName : LoadFileNames) + + for (const QString& LoadFileName : std::as_const(LoadFileNames)) { std::unique_ptr& NewProject = ProjectsToMerge.emplace_back(std::make_unique()); @@ -2706,11 +2712,11 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId) break; case LC_FILE_EXPORT_3DS: - lcGetActiveProject()->Export3DStudio(QString()); + ShowResultMessageBox(lcGetActiveProject()->Export3DStudio(QString())); break; case LC_FILE_EXPORT_COLLADA: - lcGetActiveProject()->ExportCOLLADA(QString()); + ShowResultMessageBox(lcGetActiveProject()->ExportCOLLADA(QString())); break; case LC_FILE_EXPORT_HTML: @@ -2718,11 +2724,11 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId) break; case LC_FILE_EXPORT_BRICKLINK: - lcGetActiveProject()->ExportBrickLink(); + ShowResultMessageBox(lcGetActiveProject()->ExportBrickLink()); break; case LC_FILE_EXPORT_CSV: - lcGetActiveProject()->ExportCSV(QString()); + ShowResultMessageBox(lcGetActiveProject()->ExportCSV(QString())); break; case LC_FILE_EXPORT_POVRAY: @@ -2730,7 +2736,7 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId) break; case LC_FILE_EXPORT_WAVEFRONT: - lcGetActiveProject()->ExportWavefront(QString()); + ShowResultMessageBox(lcGetActiveProject()->ExportWavefront(QString())); break; case LC_FILE_RENDER_POVRAY: diff --git a/common/lc_mainwindow.h b/common/lc_mainwindow.h index 3dc94246..c667d064 100644 --- a/common/lc_mainwindow.h +++ b/common/lc_mainwindow.h @@ -2,6 +2,7 @@ #include "lc_application.h" #include "lc_shortcuts.h" +#include "lc_result.h" #include "lc_commands.h" #include "lc_model.h" @@ -310,6 +311,7 @@ protected: void ShowPrintDialog(); void ShowImageDialog(); void ShowSelectDialog(); + void ShowResultMessageBox(const lcResult& Result); void CreatePreviewWidget(); bool OpenProjectFile(const QString& FileName); diff --git a/common/lc_result.h b/common/lc_result.h new file mode 100644 index 00000000..a60260f3 --- /dev/null +++ b/common/lc_result.h @@ -0,0 +1,134 @@ +#pragma once + +// We can't use std::expected because we only require C++17. + +#include + +class lcUnexpected +{ +public: + explicit lcUnexpected(const QString& Error) + : mError(Error) + { + } + + explicit lcUnexpected(QString&& Error) + : mError(std::move(Error)) + { + } + + const QString& error() const + { + return mError; + } + + QString& error() + { + return mError; + } + +protected: + QString mError; +}; + +template +class [[nodiscard]] lcResult +{ + using internal_t = std::conditional_t, char, T>; + +public: + template && std::is_convertible_v>> + lcResult(U&& Value) + : mHasValue(true) + { + ::new (static_cast(&mValue)) internal_t(std::forward(Value)); + } + + template >> + lcResult() + : mHasValue(true) + { + } + + lcResult(lcUnexpected Unexpected) + : mHasValue(false) + { + ::new (static_cast(&mUnexpected)) lcUnexpected(std::move(Unexpected.error())); + } + + ~lcResult() noexcept + { + if (mHasValue) + { + if constexpr (!std::is_void_v) + mValue.~internal_t(); + } + else + { + mUnexpected.~lcUnexpected(); + } + } + + lcResult(const lcResult& Other) : + mHasValue(Other.mHasValue) + { + if (mHasValue) + { + if constexpr (!std::is_void_v) + ::new (static_cast(&mValue)) internal_t(Other.mValue); + } + else + { + ::new (static_cast(&mUnexpected)) lcUnexpected(Other.mUnexpected); + } + } + + lcResult(lcResult&& Other) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : mHasValue(Other.mHasValue) + { + if (mHasValue) + { + if constexpr (!std::is_void_v) + ::new (static_cast(&mValue)) internal_t(std::move(Other.mValue)); + } + else + { + ::new (static_cast(&mUnexpected)) lcUnexpected(std::move(Other.mUnexpected)); + } + } + + bool has_value() const + { + return mHasValue; + } + + explicit operator bool() const + { + return has_value(); + } + + template >> + constexpr const U& value() const + { + if (!mHasValue) + throw std::logic_error("bad lcResult access"); + + return mValue; + } + + constexpr const QString& error() const + { + if (mHasValue) + throw std::logic_error("bad lcResult access"); + + return mUnexpected.error(); + } + +protected: + union + { + internal_t mValue; + lcUnexpected mUnexpected; + }; + bool mHasValue; +}; diff --git a/common/project.cpp b/common/project.cpp index 6001fd48..a0084258 100644 --- a/common/project.cpp +++ b/common/project.cpp @@ -650,15 +650,12 @@ std::vector Project::GetModelParts() return ModelParts; } -bool Project::ExportCurrentStep(const QString& FileName) +lcResult Project::ExportCurrentStep(const QString& FileName) { QFile File(FileName); if (!File.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); - return false; - } + return lcUnexpected(tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); QStringList Models; @@ -749,8 +746,8 @@ bool Project::ExportCurrentStep(const QString& FileName) File.close(); lcSetProfileString(LC_PROFILE_PROJECTS_PATH, QFileInfo(FileName).absolutePath()); - - return true; + + return lcResult(); } bool Project::ExportModel(const QString& FileName, lcModel* Model) const @@ -795,28 +792,22 @@ QString Project::GetExportFileName(const QString& FileName, const QString& Defau return QFileDialog::getSaveFileName(gMainWindow, DialogTitle, SaveFileName, DialogFilter); } -bool Project::Export3DStudio(const QString& FileName) +lcResult Project::Export3DStudio(const QString& FileName) { std::vector ModelParts = GetModelParts(); if (ModelParts.empty()) - { - QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export.")); - return false; - } + return lcUnexpected(tr("Nothing to export.")); QString SaveFileName = GetExportFileName(FileName, "3ds", tr("Export 3D Studio"), tr("3DS Files (*.3ds);;All Files (*.*)")); if (SaveFileName.isEmpty()) - return false; + return lcUnexpected(QString()); lcDiskFile File(SaveFileName); if (!File.Open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName)); - return false; - } + return lcUnexpected(tr("Could not open file '%1' for writing.").arg(SaveFileName)); long M3DStart = File.GetPosition(); File.WriteU16(0x4D4D); // CHK_M3DMAGIC @@ -1232,11 +1223,11 @@ bool Project::Export3DStudio(const QString& FileName) File.Seek(M3DStart + 2, SEEK_SET); File.WriteU32(M3DEnd - M3DStart); File.Seek(M3DEnd, SEEK_SET); - - return true; + + return lcResult(); } -void Project::ExportBrickLink() +lcResult Project::ExportBrickLink() { lcPartsList PartsList; @@ -1244,41 +1235,32 @@ void Project::ExportBrickLink() mModels[0]->GetPartsList(gDefaultColor, true, false, PartsList); if (PartsList.empty()) - { - QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export.")); - return; - } + return lcUnexpected(tr("Nothing to export.")); QString SaveFileName = GetExportFileName(QString(), "xml", tr("Export BrickLink"), tr("XML Files (*.xml);;All Files (*.*)")); if (SaveFileName.isEmpty()) - return; + return lcUnexpected(QString()); - lcExportBrickLink(SaveFileName, PartsList); + return lcExportBrickLink(SaveFileName, PartsList); } -bool Project::ExportCOLLADA(const QString& FileName) +lcResult Project::ExportCOLLADA(const QString& FileName) { std::vector ModelParts = GetModelParts(); if (ModelParts.empty()) - { - QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export.")); - return false; - } + return lcUnexpected(tr("Nothing to export.")); QString SaveFileName = GetExportFileName(FileName, "dae", tr("Export COLLADA"), tr("COLLADA Files (*.dae);;All Files (*.*)")); if (SaveFileName.isEmpty()) - return false; + return lcUnexpected(QString()); QFile File(SaveFileName); if (!File.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName)); - return false; - } + return lcUnexpected(tr("Could not open file '%1' for writing.").arg(SaveFileName)); QTextStream Stream(&File); @@ -1525,11 +1507,11 @@ bool Project::ExportCOLLADA(const QString& FileName) Stream << "\r\n"; Stream << "\r\n"; - - return true; + + return lcResult(); } -bool Project::ExportCSV(const QString& FileName) +lcResult Project::ExportCSV(const QString& FileName) { lcPartsList PartsList; @@ -1537,24 +1519,18 @@ bool Project::ExportCSV(const QString& FileName) mModels[0]->GetPartsList(gDefaultColor, true, false, PartsList); if (PartsList.empty()) - { - QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export.")); - return false; - } + return lcUnexpected(tr("Nothing to export.")); QString SaveFileName = GetExportFileName(FileName, "csv", tr("Export CSV"), tr("CSV Files (*.csv);;All Files (*.*)")); if (SaveFileName.isEmpty()) - return false; + return lcUnexpected(QString()); lcDiskFile CSVFile(SaveFileName); char Line[1024]; if (!CSVFile.Open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName)); - return false; - } + return lcUnexpected(tr("Could not open file '%1' for writing.").arg(SaveFileName)); CSVFile.WriteLine("Part Name,Color,Quantity,Part ID,Color Code\n"); @@ -1571,8 +1547,8 @@ bool Project::ExportCSV(const QString& FileName) CSVFile.WriteLine(Line); } } - - return true; + + return lcResult(); } lcInstructions* Project::GetInstructions() @@ -1583,7 +1559,7 @@ lcInstructions* Project::GetInstructions() return mInstructions.get(); } -void Project::ExportHTML(const lcHTMLExportOptions& Options) +lcResult Project::ExportHTML(const lcHTMLExportOptions& Options) { QDir Dir(Options.PathName); Dir.mkpath(QLatin1String(".")); @@ -1641,10 +1617,7 @@ void Project::ExportHTML(const lcHTMLExportOptions& Options) QFile File(FileName); if (!File.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); - return; - } + return lcUnexpected(tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); QTextStream Stream(&File); @@ -1672,10 +1645,7 @@ void Project::ExportHTML(const lcHTMLExportOptions& Options) QFile File(FileName); if (!File.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); - return; - } + return lcUnexpected(tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); QTextStream Stream(&File); @@ -1697,10 +1667,7 @@ void Project::ExportHTML(const lcHTMLExportOptions& Options) QFile File(FileName); if (!File.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); - return; - } + return lcUnexpected(tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); QTextStream Stream(&File); @@ -1731,10 +1698,7 @@ void Project::ExportHTML(const lcHTMLExportOptions& Options) QFile File(FileName); if (!File.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); - return; - } + return lcUnexpected(tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); QTextStream Stream(&File); @@ -1763,10 +1727,7 @@ void Project::ExportHTML(const lcHTMLExportOptions& Options) QFile File(FileName); if (!File.open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); - return; - } + return lcUnexpected(tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString())); QTextStream Stream(&File); @@ -1803,24 +1764,26 @@ void Project::ExportHTML(const lcHTMLExportOptions& Options) Stream << QLatin1String("\r\n


Created by LeoCAD
\r\n"); } + + return lcResult(); } -std::pair Project::ExportPOVRay(const QString& FileName) +lcResult Project::ExportPOVRay(const QString& FileName) { std::vector ModelParts = GetModelParts(); if (ModelParts.empty()) - return { false, tr("Nothing to export.") }; + return lcUnexpected(tr("Nothing to export.")); QString SaveFileName = GetExportFileName(FileName, QLatin1String("pov"), tr("Export POV-Ray"), tr("POV-Ray Files (*.pov);;All Files (*.*)")); if (SaveFileName.isEmpty()) - return { false, QString() }; + return lcUnexpected(QString()); lcDiskFile POVFile(SaveFileName); if (!POVFile.Open(QIODevice::WriteOnly)) - return { false, tr("Could not open file '%1' for writing.").arg(SaveFileName) }; + return lcUnexpected(tr("Could not open file '%1' for writing.").arg(SaveFileName)); enum { @@ -2225,7 +2188,7 @@ std::pair Project::ExportPOVRay(const QString& FileName) lcDiskFile TableFile(QFileInfo(QDir(LGEOPath), QLatin1String("lg_elements.lst")).absoluteFilePath()); if (!TableFile.Open(QIODevice::ReadOnly)) - return { false, tr("Could not find LGEO files in folder '%1'.").arg(LGEOPath) }; + return lcUnexpected(tr("Could not find LGEO files in folder '%1'.").arg(LGEOPath)); while (TableFile.ReadLine(Line, sizeof(Line))) { @@ -2271,7 +2234,7 @@ std::pair Project::ExportPOVRay(const QString& FileName) lcDiskFile LgeoColorFile(QFileInfo(QDir(LGEOPath), QLatin1String("lg_colors.lst")).absoluteFilePath()); if (!LgeoColorFile.Open(QIODevice::ReadOnly)) - return { false, tr("Could not find LGEO files in folder '%1'.").arg(LGEOPath) }; + return lcUnexpected(tr("Could not find LGEO files in folder '%1'.").arg(LGEOPath)); while (LgeoColorFile.ReadLine(Line, sizeof(Line))) { @@ -2487,33 +2450,27 @@ std::pair Project::ExportPOVRay(const QString& FileName) snprintf(Line, sizeof(Line), "\n#include \"%s\"\n", POVRayOptions.FooterIncludeFile.toLatin1().constData()); POVFile.WriteLine(Line); } - - return { true, QString() }; + + return lcResult(); } -bool Project::ExportWavefront(const QString& FileName) +lcResult Project::ExportWavefront(const QString& FileName) { std::vector ModelParts = GetModelParts(); if (ModelParts.empty()) - { - QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export.")); - return false; - } + return lcUnexpected(tr("Nothing to export.")); QString SaveFileName = GetExportFileName(FileName, QLatin1String("obj"), tr("Export Wavefront"), tr("Wavefront Files (*.obj);;All Files (*.*)")); if (SaveFileName.isEmpty()) - return false; + return lcUnexpected(QString()); lcDiskFile OBJFile(SaveFileName); char Line[1024]; if (!OBJFile.Open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName)); - return false; - } + return lcUnexpected(tr("Could not open file '%1' for writing.").arg(SaveFileName)); quint32 vert = 1; @@ -2526,13 +2483,12 @@ bool Project::ExportWavefront(const QString& FileName) OBJFile.WriteLine(Line); lcDiskFile MaterialFile(MaterialFileName); + if (!MaterialFile.Open(QIODevice::WriteOnly)) - { - QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(MaterialFileName)); - return false; - } + return lcUnexpected(tr("Could not open file '%1' for writing.").arg(MaterialFileName)); MaterialFile.WriteLine("# Colors used by LeoCAD\n\n"); + for (const lcColor& Color : gColorList) { if (Color.Translucent) @@ -2596,8 +2552,8 @@ bool Project::ExportWavefront(const QString& FileName) vert += Mesh->mNumVertices; } } - - return true; + + return lcResult(); } void Project::SaveImage(const lcImageDialogOptions& Options) diff --git a/common/project.h b/common/project.h index 978ecbfc..b6e967af 100644 --- a/common/project.h +++ b/common/project.h @@ -1,6 +1,7 @@ #pragma once #include "lc_application.h" +#include "lc_result.h" #define LC_HTML_SINGLEPAGE 0x01 #define LC_HTML_INDEX 0x02 @@ -100,15 +101,15 @@ public: bool ImportInventory(const std::vector& SetInventory, const QString& Name, const QString& Description); void SaveImage(const lcImageDialogOptions& Options); - bool ExportCurrentStep(const QString& FileName); + lcResult ExportCurrentStep(const QString& FileName); bool ExportModel(const QString& FileName, lcModel* Model) const; - bool Export3DStudio(const QString& FileName); - void ExportBrickLink(); - bool ExportCOLLADA(const QString& FileName); - bool ExportCSV(const QString& FileName); - void ExportHTML(const lcHTMLExportOptions& Options); - std::pair ExportPOVRay(const QString& FileName); - bool ExportWavefront(const QString& FileName); + lcResult Export3DStudio(const QString& FileName); + lcResult ExportBrickLink(); + lcResult ExportCOLLADA(const QString& FileName); + lcResult ExportCSV(const QString& FileName); + lcResult ExportHTML(const lcHTMLExportOptions& Options); + lcResult ExportPOVRay(const QString& FileName); + lcResult ExportWavefront(const QString& FileName); void UpdatePieceInfo(PieceInfo* Info) const; diff --git a/leocad.pro b/leocad.pro index d3d742f8..4aa5a45e 100644 --- a/leocad.pro +++ b/leocad.pro @@ -46,10 +46,12 @@ win32-msvc* { INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtZlib QMAKE_LFLAGS += /INCREMENTAL LIBS += -ladvapi32 -lshell32 -lopengl32 -luser32 + QMAKE_CXXFLAGS += /we4834 } else { PRECOMPILED_HEADER = common/lc_global.h LIBS += -lz QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter + QMAKE_CXXFLAGS += -Werror=unused-result } isEmpty(QMAKE_LRELEASE) { @@ -252,6 +254,7 @@ HEADERS += \ common/group.h \ common/image.h \ common/lc_modelhistory.h \ + common/lc_result.h \ common/light.h \ common/minifig.h \ common/object.h \ diff --git a/qt/lc_renderdialog.cpp b/qt/lc_renderdialog.cpp index 5564beb1..be838fe9 100644 --- a/qt/lc_renderdialog.cpp +++ b/qt/lc_renderdialog.cpp @@ -6,7 +6,6 @@ #include "lc_profile.h" #include "lc_blenderpreferences.h" #include "lc_model.h" -#include "lc_qutils.h" #ifndef LC_DISABLE_RENDER_DIALOG @@ -234,26 +233,26 @@ void lcRenderDialog::RenderPOVRay() ui->RenderProgress->setValue(ui->RenderProgress->minimum()); ui->RenderProgress->setFormat(tr("Exporting Model")); - QFuture> exportThread = QtConcurrent::run([FileName]() + QFuture> ExportThread = QtConcurrent::run([FileName]() { return lcGetActiveProject()->ExportPOVRay(FileName); }); QApplication::setOverrideCursor(Qt::WaitCursor); - while (!exportThread.isFinished()) + while (!ExportThread.isFinished()) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QGuiApplication::restoreOverrideCursor(); ui->RenderProgress->setFormat("%p%"); - auto [Success, ErrorMessage] = exportThread.result(); + lcResult ExportResult = ExportThread.result(); - if (!Success) + if (!ExportResult) { - if (!ErrorMessage.isEmpty()) - QMessageBox::information(this, tr("Render Error"), ErrorMessage); + if (!ExportResult.error().isEmpty()) + QMessageBox::information(this, tr("Render Error"), ExportResult.error()); return; } @@ -382,7 +381,10 @@ void lcRenderDialog::RenderBlender() lcModel* Model = lcGetActiveProject()->GetActiveModel(); const QString ModelFileName = QFileInfo(QDir(lcGetProfileString(LC_PROFILE_PROJECTS_PATH)), QString("%1_Step_%2.ldr").arg(QFileInfo(Model->GetProperties().mFileName).baseName()).arg(Model->GetCurrentStep())).absoluteFilePath(); - lcGetActiveProject()->ExportCurrentStep(ModelFileName); + lcResult ExportResult = lcGetActiveProject()->ExportCurrentStep(ModelFileName); + + if (!ExportResult) + QMessageBox::warning(this, tr("Error"), ExportResult.error()); ui->RenderProgress->setFormat("%p%"); QApplication::processEvents();