diff --git a/app/src/errordialog.cpp b/app/src/errordialog.cpp index a69d243be3..e1d5ee9967 100644 --- a/app/src/errordialog.cpp +++ b/app/src/errordialog.cpp @@ -17,6 +17,9 @@ GNU General Public License for more details. #include "errordialog.h" #include "ui_errordialog.h" +#include +#include + ErrorDialog::ErrorDialog( QString title, QString description, QString details, QWidget *parent ) : QDialog( parent ), ui(new Ui::ErrorDialog) @@ -34,9 +37,18 @@ ErrorDialog::ErrorDialog( QString title, QString description, QString details, Q { ui->details->setText( QString( "
%1
" ).arg( details ) ); } + + QPushButton* copyToClipboard = new QPushButton(tr("Copy to Clipboard")); + ui->buttonBox->addButton(copyToClipboard, QDialogButtonBox::ActionRole); + + connect(copyToClipboard, &QPushButton::clicked, this, &ErrorDialog::onCopyToClipboard); } ErrorDialog::~ErrorDialog() { delete ui; } + +void ErrorDialog::onCopyToClipboard() { + QGuiApplication::clipboard()->setText(ui->details->toPlainText()); +} diff --git a/app/src/errordialog.h b/app/src/errordialog.h index 690c19ee7f..ae9b7169a1 100644 --- a/app/src/errordialog.h +++ b/app/src/errordialog.h @@ -32,6 +32,9 @@ class ErrorDialog : public QDialog explicit ErrorDialog(QString title, QString description, QString details = QString(), QWidget *parent = nullptr); ~ErrorDialog(); +public slots: + void onCopyToClipboard(); + private: Ui::ErrorDialog *ui; }; diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index 0fbd23d794..aba7dae7d2 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -773,7 +773,7 @@ bool MainWindow2::saveObject(QString strSavedFileName) ErrorDialog errorDialog(st.title(), st.description().append(tr("

An error has occurred and your file may not have saved successfully." - "If you believe that this error is an issue with Pencil2D, please create a new issue at:" + "\nIf you believe that this error is an issue with Pencil2D, please create a new issue at:" "
https://github.com/pencil2d/pencil/issues
" "Please be sure to include the following details in your issue:")), st.details().html()); errorDialog.exec(); diff --git a/app/ui/errordialog.ui b/app/ui/errordialog.ui index 315bf36b77..8078968a47 100644 --- a/app/ui/errordialog.ui +++ b/app/ui/errordialog.ui @@ -82,6 +82,16 @@ + + + + This report contains vital information. Copy all of it when submitting a bug. + + + true + + + diff --git a/core_lib/src/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp index 6ec5b019f9..7a952fec06 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.cpp +++ b/core_lib/src/graphics/bitmap/bitmapimage.cpp @@ -742,9 +742,14 @@ Status BitmapImage::writeFile(const QString& filename) if(f.exists()) { bool b = f.remove(); - return (b) ? Status::OK : Status::FAIL; + if (!b) { + return Status::FAIL; + } } - return Status::SAFE; + + // The frame is likely empty, act like there's no file name + // so we don't end up writing to it later. + setFileName(""); } return Status::SAFE; } diff --git a/core_lib/src/graphics/vector/vectorimage.cpp b/core_lib/src/graphics/vector/vectorimage.cpp index 54a0b90fba..3d5dd0598d 100644 --- a/core_lib/src/graphics/vector/vectorimage.cpp +++ b/core_lib/src/graphics/vector/vectorimage.cpp @@ -23,6 +23,7 @@ GNU General Public License for more details. #include #include #include "object.h" +#include "util.h" VectorImage::VectorImage() diff --git a/core_lib/src/managers/undoredomanager.cpp b/core_lib/src/managers/undoredomanager.cpp index 7dea4748fe..66e2a9ef2c 100644 --- a/core_lib/src/managers/undoredomanager.cpp +++ b/core_lib/src/managers/undoredomanager.cpp @@ -86,7 +86,7 @@ Status UndoRedoManager::save(Object* /*o*/) { if (mNewBackupSystemEnabled) { mUndoStack.setClean(); - } else { + } else if (!mLegacyBackupList.isEmpty() && mLegacyBackupIndex < mLegacyBackupList.count()) { mLegacyBackupAtSave = mLegacyBackupList[mLegacyBackupIndex]; } return Status::OK; diff --git a/core_lib/src/qminiz.cpp b/core_lib/src/qminiz.cpp index 376075fb87..51e4252005 100644 --- a/core_lib/src/qminiz.cpp +++ b/core_lib/src/qminiz.cpp @@ -39,11 +39,13 @@ Status MiniZ::sanityCheck(const QString& sZipFilePath) if (!readOk || !closeOk) { DebugDetails dd; + + dd << "\n[Miniz sanity check]\n"; if (read_err != MZ_ZIP_NO_ERROR) { - dd << QString("Miniz found an error while reading the file. - %1, %2").arg(static_cast(read_err)).arg(mz_zip_get_error_string(read_err)); + dd << QString("Found an error while reading the file. Error code: %2, reason: %3").arg(static_cast(read_err)).arg(mz_zip_get_error_string(read_err)); } if (close_err != MZ_ZIP_NO_ERROR) { - dd << QString("Miniz found an error while closing file file. - %1, %2").arg(static_cast(close_err)).arg(mz_zip_get_error_string(close_err)); + dd << QString("Found an error while closing the file. Error code: %2, reason: %3").arg(static_cast(close_err)).arg(mz_zip_get_error_string(close_err)); } return Status(Status::ERROR_MINIZ_FAIL, dd); } @@ -66,28 +68,33 @@ size_t MiniZ::istreamReadCallback(void *pOpaque, mz_uint64 file_ofs, void * pBuf Status MiniZ::compressFolder(QString zipFilePath, QString srcFolderPath, const QStringList& fileList, QString mimetype) { DebugDetails dd; + dd << "\n[Miniz COMPRESSION diagnostics]\n"; dd << QString("Creating Zip %1 from folder %2").arg(zipFilePath, srcFolderPath); if (!srcFolderPath.endsWith("/")) { + dd << "Adding / to path"; srcFolderPath.append("/"); } mz_zip_archive* mz = new mz_zip_archive; - mz_zip_zero_struct(mz); - - mz_bool ok = mz_zip_writer_init_file(mz, zipFilePath.toUtf8().data(), 0); - ScopeGuard mzScopeGuard([&] { - mz_zip_writer_end(mz); delete mz; }); + mz_zip_zero_struct(mz); + + mz_bool ok = mz_zip_writer_init_file(mz, zipFilePath.toUtf8().data(), 0); + if (!ok) { mz_zip_error err = mz_zip_get_last_error(mz); - dd << QString("Miniz writer init failed: error %1, %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); + dd << QString("Error: Failed to init writer. Error code: %1, reason: %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); + return Status(Status::FAIL, dd); } + ScopeGuard mzScopeGuard2([&] { + mz_zip_writer_end(mz); + }); // Add special uncompressed mimetype file to help with the identification of projects { @@ -99,7 +106,8 @@ Status MiniZ::compressFolder(QString zipFilePath, QString srcFolderPath, const Q if (!ok) { mz_zip_error err = mz_zip_get_last_error(mz); - dd << QString("Cannot add mimetype: error %1").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); + dd << QString("ERROR: Unable to add mimetype. Error code: %1, reason: %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); + return Status(Status::FAIL, dd); } } @@ -119,26 +127,15 @@ Status MiniZ::compressFolder(QString zipFilePath, QString srcFolderPath, const Q if (!ok) { mz_zip_error err = mz_zip_get_last_error(mz); - dd << QString("Cannot add %3: error %1, %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err), sRelativePath); + dd << QString("Error: Unable to add file: %3. Error code: %1, reason: %2 - Aborting!").arg(static_cast(err)).arg(mz_zip_get_error_string(err), sRelativePath); + return Status(Status::FAIL, dd); } } ok &= mz_zip_writer_finalize_archive(mz); if (!ok) { mz_zip_error err = mz_zip_get_last_error(mz); - dd << QString("Miniz finalize archive failed: error %1, %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); - return Status(Status::FAIL, dd); - } - - ok &= mz_zip_writer_end(mz); - - mzScopeGuard.dismiss(); - ScopeGuard mzScopeGuard2([&] { delete mz; }); - - if (!ok) - { - mz_zip_error err = mz_zip_get_last_error(mz); - dd << QString("Miniz writer end failed: error %1, %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); + dd << QString("Error: Failed to finalize archive. Error code %1, reason: %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); return Status(Status::FAIL, dd); } @@ -148,10 +145,12 @@ Status MiniZ::compressFolder(QString zipFilePath, QString srcFolderPath, const Q Status MiniZ::uncompressFolder(QString zipFilePath, QString destPath) { DebugDetails dd; + dd << "\n[Miniz EXTRACTION diagnostics]\n"; dd << QString("Unzip file %1 to folder %2").arg(zipFilePath, destPath); if (!QFile::exists(zipFilePath)) { + dd << QString("Error: Zip file does not exist."); return Status::FILE_NOT_FOUND; } @@ -166,17 +165,21 @@ Status MiniZ::uncompressFolder(QString zipFilePath, QString destPath) baseDir.makeAbsolute(); mz_zip_archive* mz = new mz_zip_archive; - mz_zip_zero_struct(mz); - - mz_bool ok = mz_zip_reader_init_file(mz, zipFilePath.toUtf8().data(), 0); - ScopeGuard mzScopeGuard([&] { - mz_zip_reader_end(mz); delete mz; }); - if (!ok) + mz_zip_zero_struct(mz); + + mz_bool ok = mz_zip_reader_init_file(mz, zipFilePath.toUtf8().data(), 0); + if (!ok) { + mz_zip_error err = mz_zip_get_last_error(mz); + dd << QString("Error: Failed to init reader. Error code: %1, reason: %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); return Status(Status::FAIL, dd); + } + ScopeGuard mzScopeGuard2([&] { + mz_zip_reader_end(mz); + }); int num = mz_zip_reader_get_num_files(mz); @@ -215,21 +218,15 @@ Status MiniZ::uncompressFolder(QString zipFilePath, QString destPath) if (!extractOK) { ok = false; - dd << "File extraction failed."; + mz_zip_error err = mz_zip_get_last_error(mz); + dd << QString("WARNING: Unable to extract file. Error code: %1, reason: %2").arg(static_cast(err)).arg(mz_zip_get_error_string(err)); } } } - ok &= mz_zip_reader_end(mz); - - mzScopeGuard.dismiss(); - ScopeGuard mzScopeGuard2([&] { - delete mz; - }); - if (!ok) { - dd << "Unzip error!"; + return Status(Status::FAIL, dd); } return Status::OK; } diff --git a/core_lib/src/soundplayer.cpp b/core_lib/src/soundplayer.cpp index 6120b67b03..28495c7454 100644 --- a/core_lib/src/soundplayer.cpp +++ b/core_lib/src/soundplayer.cpp @@ -19,6 +19,7 @@ GNU General Public License for more details. #include #include #include "soundclip.h" +#include "util.h" SoundPlayer::SoundPlayer() { diff --git a/core_lib/src/structure/filemanager.cpp b/core_lib/src/structure/filemanager.cpp index 5a696af457..898ebb824b 100644 --- a/core_lib/src/structure/filemanager.cpp +++ b/core_lib/src/structure/filemanager.cpp @@ -24,7 +24,7 @@ GNU General Public License for more details. #include "fileformat.h" #include "object.h" #include "layercamera.h" -#include "util/util.h" +#include "util.h" FileManager::FileManager(QObject* parent) : QObject(parent) { @@ -34,6 +34,7 @@ FileManager::FileManager(QObject* parent) : QObject(parent) Object* FileManager::load(const QString& sFileName) { DebugDetails dd; + dd << "\n[Project LOAD diagnostics]\n"; dd << QString("File name: ").append(sFileName); if (!QFile::exists(sFileName)) { @@ -53,35 +54,46 @@ Object* FileManager::load(const QString& sFileName) // Test file format: new zipped .pclx or old .pcl? bool isArchive = isArchiveFormat(sFileName); + QString fileFormat = "Project format: %1"; if (!isArchive) { - dd << "Recognized Old Pencil2D File Format (*.pcl) !"; + dd << fileFormat.arg(".pcl"); strMainXMLFile = sFileName; strDataFolder = strMainXMLFile + "." + PFF_OLD_DATA_DIR; } else { - dd << "Recognized New zipped Pencil2D File Format (*.pclx) !"; + QString workingDirPath = obj->workingDir(); + + dd << QString("Working dir: %1").arg(workingDirPath); + dd << fileFormat.arg(".pclx"); Status sanityCheck = MiniZ::sanityCheck(sFileName); // Let's check if we can read the file before we try to unzip. if (!sanityCheck.ok()) { dd.collect(sanityCheck.details()); + dd << "\nError: Unable to extract project, miniz sanity check failed."; + handleOpenProjectError(Status::ERROR_INVALID_XML_FILE, dd); + return nullptr; } else { - Status unzipStatus = unzip(sFileName, obj->workingDir()); + Status unzipStatus = unzip(sFileName, workingDirPath); dd.collect(unzipStatus.details()); + + if(unzipStatus.ok()) { + dd << QString("Unzipped at: %1 ").arg(workingDirPath); + } else { + dd << QString("Error: Unzipping failed: %1 ").arg(workingDirPath); + handleOpenProjectError(Status::ERROR_INVALID_XML_FILE, dd); + return nullptr; + } } - strMainXMLFile = QDir(obj->workingDir()).filePath(PFF_XML_FILE_NAME); - strDataFolder = QDir(obj->workingDir()).filePath(PFF_DATA_DIR); + strMainXMLFile = QDir(workingDirPath).filePath(PFF_XML_FILE_NAME); + strDataFolder = QDir(workingDirPath).filePath(PFF_DATA_DIR); } - dd << QString("XML file: ").append(strMainXMLFile) - << QString("Data folder: ").append(strDataFolder) - << QString("Working folder: ").append(obj->workingDir()); - obj->setDataDir(strDataFolder); obj->setMainXMLFile(strMainXMLFile); @@ -92,21 +104,24 @@ Object* FileManager::load(const QString& sFileName) QFile file(strMainXMLFile); if (!file.exists()) { - dd << "Main XML file does not exist"; + dd << "Error: No main XML exists!"; handleOpenProjectError(Status::ERROR_INVALID_XML_FILE, dd); return nullptr; } if (!file.open(QFile::ReadOnly)) { + dd << "Error: Main XML file is read only!"; handleOpenProjectError(Status::ERROR_FILE_CANNOT_OPEN, dd); return nullptr; } + dd << "Main XML exists: Yes"; + QDomDocument xmlDoc; if (!xmlDoc.setContent(&file)) { FILEMANAGER_LOG("Couldn't open the main XML file"); - dd << "Error parsing or opening the main XML file"; + dd << "Error: Unable to parse or open the main XML file"; handleOpenProjectError(Status::ERROR_INVALID_XML_FILE, dd); return nullptr; } @@ -115,7 +130,7 @@ Object* FileManager::load(const QString& sFileName) if (!(type.name() == "PencilDocument" || type.name() == "MyObject")) { FILEMANAGER_LOG("Invalid main XML doctype"); - dd << QString("Invalid main XML doctype: ").append(type.name()); + dd << QString("Error: Invalid main XML doctype: ").append(type.name()); handleOpenProjectError(Status::ERROR_INVALID_PENCIL_FILE, dd); return nullptr; } @@ -123,7 +138,7 @@ Object* FileManager::load(const QString& sFileName) QDomElement root = xmlDoc.documentElement(); if (root.isNull()) { - dd << "Main XML root node is null"; + dd << "Error: Main XML root node is null"; handleOpenProjectError(Status::ERROR_INVALID_PENCIL_FILE, dd); return nullptr; } @@ -144,7 +159,7 @@ Object* FileManager::load(const QString& sFileName) if (!ok) { obj.reset(); - dd << "Issue occurred during object loading"; + dd << "Error: Issue occurred during object loading"; handleOpenProjectError(Status::ERROR_INVALID_PENCIL_FILE, dd); return nullptr; } @@ -216,16 +231,16 @@ bool FileManager::isArchiveFormat(const QString& fileName) const Status FileManager::save(const Object* object, const QString& sFileName) { DebugDetails dd; - dd << __FUNCTION__; - dd << ("sFileName = " + sFileName); + dd << "\n[Project SAVE diagnostics]\n"; + dd << ("file name:" + sFileName); if (object == nullptr) { - dd << "Object parameter is null"; + dd << "Error: Object parameter is null"; return Status(Status::INVALID_ARGUMENT, dd); } if (sFileName.isEmpty()) { - dd << "File name is empty"; + dd << "Error: File name is empty, unable to save."; return Status(Status::INVALID_ARGUMENT, dd, tr("Invalid Save Path"), tr("The path is empty.")); @@ -240,7 +255,7 @@ Status FileManager::save(const Object* object, const QString& sFileName) QFileInfo fileInfo(sFileName); if (fileInfo.isDir()) { - dd << "FileName points to a directory"; + dd << "Error: File name must point to a file, not a directory."; return Status(Status::INVALID_ARGUMENT, dd, tr("Invalid Save Path"), tr("The path (\"%1\") points to a directory.").arg(fileInfo.absoluteFilePath())); @@ -248,14 +263,14 @@ Status FileManager::save(const Object* object, const QString& sFileName) QFileInfo parentDirInfo(fileInfo.dir().absolutePath()); if (!parentDirInfo.exists()) { - dd << "The parent directory of sFileName does not exist"; + dd << QString("Error: The parent directory of %1 does not exist").arg(sFileName); return Status(Status::INVALID_ARGUMENT, dd, tr("Invalid Save Path"), tr("The directory (\"%1\") does not exist.").arg(parentDirInfo.absoluteFilePath())); } if ((fileInfo.exists() && !fileInfo.isWritable()) || !parentDirInfo.isWritable()) { - dd << "Filename points to a location that is not writable"; + dd << "Error: File name points to a location that is not writable"; return Status(Status::INVALID_ARGUMENT, dd, tr("Invalid Save Path"), tr("The path (\"%1\") is not writable.").arg(fileInfo.absoluteFilePath())); @@ -265,22 +280,23 @@ Status FileManager::save(const Object* object, const QString& sFileName) QString sMainXMLFile; QString sDataFolder; + QString fileFormat = QString("Project format: %1"); bool isArchive = isArchiveFormat(sFileName); if (!isArchive) { - dd << "Old Pencil2D File Format (*.pcl) !"; + dd << fileFormat.arg(".pcl"); sMainXMLFile = sFileName; sDataFolder = sMainXMLFile + "." + PFF_OLD_DATA_DIR; } else { - dd << "New zipped Pencil2D File Format (*.pclx) !"; - dd.collect(MiniZ::sanityCheck(sFileName).details()); - sTempWorkingFolder = object->workingDir(); + + dd << QString("Working dir: %1").arg(sTempWorkingFolder); + dd << fileFormat.arg(".pclx"); + Q_ASSERT(QDir(sTempWorkingFolder).exists()); - dd << QString("TempWorkingFolder = ").append(sTempWorkingFolder); sMainXMLFile = QDir(sTempWorkingFolder).filePath(PFF_XML_FILE_NAME); sDataFolder = QDir(sTempWorkingFolder).filePath(PFF_OLD_DATA_DIR); @@ -293,7 +309,7 @@ Status FileManager::save(const Object* object, const QString& sFileName) if (!dir.mkpath(sDataFolder)) { - dd << QString("dir.absolutePath() = %1").arg(dir.absolutePath()); + dd << QString("Error: Unable to create data directory, tried to save to: %1").arg(dir.absolutePath()); return Status(Status::FAIL, dd, tr("Cannot Create Data Directory"), tr("Failed to create directory \"%1\". Please make sure you have sufficient permissions.").arg(sDataFolder)); @@ -301,7 +317,7 @@ Status FileManager::save(const Object* object, const QString& sFileName) } if (!dataInfo.isDir()) { - dd << QString("dataInfo.absoluteFilePath() = ").append(dataInfo.absoluteFilePath()); + dd << QString("Error: Expected data to be a directory but found %1 instead").arg(dataInfo.absoluteFilePath()); return Status(Status::FAIL, dd, tr("Cannot Create Data Directory"), @@ -324,28 +340,36 @@ Status FileManager::save(const Object* object, const QString& sFileName) if (isArchive) { + dd << "\n[Archiving diagnostics]\n"; QString sBackupFile = backupPreviousFile(sFileName); + if (!sBackupFile.isEmpty()) { + dd << QString("\nNote: A backup has been made here: %1").arg(sBackupFile); + } + if (!saveOk) { return Status(Status::FAIL, dd, tr("Internal Error"), - tr("An internal error occurred. Your file may not be saved successfully.")); + tr("An internal error occurred. The project could not be saved.")); } - dd << "Miniz"; + dd << "Miniz: Zipping..."; Status stMiniz = MiniZ::compressFolder(sFileName, sTempWorkingFolder, filesToZip, "application/x-pencil2d-pclx"); if (!stMiniz.ok()) { dd.collect(stMiniz.details()); + dd << "\nError: Miniz failed to zip project"; return Status(Status::ERROR_MINIZ_FAIL, dd, tr("Miniz Error"), - tr("An internal error occurred. Your file may not be saved successfully.")); + tr("An internal error occurred. The project may not have been saved successfully.")); } - dd << "Zip file saved successfully"; + dd << "Miniz: Zip file saved successfully"; Q_ASSERT(stMiniz.ok()); - if (saveOk) + if (saveOk) { + dd << "Project saved successfully, deleting backup"; deleteBackupFile(sBackupFile); + } } progressForward(); @@ -354,7 +378,7 @@ Status FileManager::save(const Object* object, const QString& sFileName) { return Status(Status::FAIL, dd, tr("Internal Error"), - tr("An internal error occurred. Your file may not be saved successfully.")); + tr("An internal error occurred. The project may not have been saved successfully.")); } return Status::OK; @@ -559,12 +583,15 @@ int FileManager::countExistingBackups(const QString& fileName) const { QFileInfo fileInfo(fileName); QDir directory(fileInfo.absoluteDir()); - const QString& baseName = fileInfo.completeBaseName(); + const QString& baseFileName = fileInfo.completeBaseName(); int backupCount = 0; for (const QFileInfo &dirFileInfo : directory.entryInfoList(QDir::Filter::Files)) { - QString searchFileBaseName = dirFileInfo.completeBaseName(); - if (baseName.compare(searchFileBaseName) == 0 && searchFileBaseName.contains(PFF_BACKUP_IDENTIFIER)) { + QString searchFileAbsPath = dirFileInfo.absoluteFilePath(); + QString searchFileName = dirFileInfo.baseName(); + + bool sameBaseName = baseFileName.compare(searchFileName) == 0; + if (sameBaseName && searchFileAbsPath.contains(PFF_BACKUP_IDENTIFIER)) { backupCount++; } } @@ -624,9 +651,10 @@ bool FileManager::loadPalette(Object* obj) Status FileManager::writeKeyFrameFiles(const Object* object, const QString& dataFolder, QStringList& filesFlushed) { DebugDetails dd; + dd << "\n[Keyframes WRITE diagnostics]\n"; const int numLayers = object->getLayerCount(); - dd << QString("Total %1 layers").arg(numLayers); + dd << QString("Total layer count: %1").arg(numLayers); for (int i = 0; i < numLayers; ++i) { @@ -646,27 +674,37 @@ Status FileManager::writeKeyFrameFiles(const Object* object, const QString& data { saveLayersOK = false; dd.collect(st.details()); - dd << QString(" !! Failed to save Layer[%1] %2").arg(i).arg(layer->name()); + dd << QString("\nError: Failed to save Layer[%1] %2").arg(i).arg(layer->name()); } } - dd << "All Layers saved"; progressForward(); auto errorCode = (saveLayersOK) ? Status::OK : Status::FAIL; + + if (saveLayersOK) { + dd << "\nAll Layers saved"; + } else { + dd << "\nError: Unable to save all layers"; + } + return Status(errorCode, dd); } Status FileManager::writeMainXml(const Object* object, const QString& mainXmlPath, QStringList& filesWritten) { DebugDetails dd; + dd << "\n[XML WRITE diagnostics]\n"; QFile file(mainXmlPath); if (!file.open(QFile::WriteOnly | QFile::Text)) { - dd << "Failed to open Main XML" << mainXmlPath; + dd << QString("Error: Failed to open Main XML at: %1, \nReason: %2").arg(mainXmlPath).arg(file.errorString()); return Status(Status::ERROR_FILE_CANNOT_OPEN, dd); } + ScopeGuard fileScopeGuard([&] { + file.close(); + }); QDomDocument xmlDoc("PencilDocument"); QDomElement root = xmlDoc.createElement("document"); @@ -696,7 +734,6 @@ Status FileManager::writeMainXml(const Object* object, const QString& mainXmlPat QTextStream out(&file); xmlDoc.save(out, indentSize); out.flush(); - file.close(); dd << "Done writing main xml file: " << mainXmlPath; @@ -710,7 +747,7 @@ Status FileManager::writePalette(const Object* object, const QString& dataFolder if (paletteFile.isEmpty()) { DebugDetails dd; - dd << "Failed to save palette"; + dd << "\nError: Failed to save palette"; return Status(Status::FAIL, dd); } filesWritten.append(paletteFile); @@ -920,7 +957,6 @@ Status FileManager::rebuildMainXML(Object* object) QTextStream fout(&file); xmlDoc.save(fout, 2); fout.flush(); - file.close(); return Status::OK; } diff --git a/core_lib/src/structure/layer.cpp b/core_lib/src/structure/layer.cpp index c05ca919f6..9ae1a831fd 100644 --- a/core_lib/src/structure/layer.cpp +++ b/core_lib/src/structure/layer.cpp @@ -340,7 +340,7 @@ bool Layer::loadKey(KeyFrame* pKey) Status Layer::save(const QString& sDataFolder, QStringList& attachedFiles, ProgressCallback progressStep) { DebugDetails dd; - dd << __FUNCTION__; + dd << "\n[Layer SAVE diagnostics]\n"; bool ok = true; @@ -364,6 +364,7 @@ Status Layer::save(const QString& sDataFolder, QStringList& attachedFiles, Progr } if (!ok) { + dd << "\nError: Failed to save one or more files"; return Status(Status::FAIL, dd); } return Status::OK; diff --git a/core_lib/src/structure/layerbitmap.cpp b/core_lib/src/structure/layerbitmap.cpp index 974ce2b028..fc88797d4e 100644 --- a/core_lib/src/structure/layerbitmap.cpp +++ b/core_lib/src/structure/layerbitmap.cpp @@ -95,7 +95,7 @@ Status LayerBitmap::saveKeyFrameFile(KeyFrame* keyframe, QString path) dd << "LayerBitmap::saveKeyFrame"; dd << QString(" KeyFrame.pos() = %1").arg(keyframe->pos()); dd << QString(" strFilePath = %1").arg(strFilePath); - dd << QString("BitmapImage could not be saved"); + dd << QString("Error: Failed to save BitmapImage"); dd.collect(st.details()); return Status(Status::FAIL, dd); } @@ -193,7 +193,9 @@ QDomElement LayerBitmap::createDomElement(QDomDocument& doc) const imageTag.setAttribute("opacity", pImg->getOpacity()); layerElem.appendChild(imageTag); - Q_ASSERT(QFileInfo(pKeyFrame->fileName()).fileName() == fileName(pKeyFrame)); + if (!pKeyFrame->fileName().isEmpty()) { + Q_ASSERT(QFileInfo(pKeyFrame->fileName()).fileName() == fileName(pKeyFrame)); + } }); return layerElem; diff --git a/core_lib/src/structure/layersound.cpp b/core_lib/src/structure/layersound.cpp index 2cb42f72d0..512e9a5abe 100644 --- a/core_lib/src/structure/layersound.cpp +++ b/core_lib/src/structure/layersound.cpp @@ -145,11 +145,11 @@ Status LayerSound::saveKeyFrameFile(KeyFrame* key, QString path) key->setFileName(""); DebugDetails dd; - dd << __FUNCTION__; + dd << "LayerSound::saveKeyFrameFile"; dd << QString(" KeyFrame.pos() = %1").arg(key->pos()); dd << QString(" Key->fileName() = %1").arg(key->fileName()); dd << QString(" FilePath = %1").arg(sDestFileLocation); - dd << QString("Couldn't save the sound clip"); + dd << QString("Error: Failed to save SoundClip"); return Status(Status::FAIL, dd); } key->setFileName(sDestFileLocation); diff --git a/core_lib/src/structure/layervector.cpp b/core_lib/src/structure/layervector.cpp index deea0a3cdb..b789ada28d 100644 --- a/core_lib/src/structure/layervector.cpp +++ b/core_lib/src/structure/layervector.cpp @@ -92,10 +92,10 @@ Status LayerVector::saveKeyFrameFile(KeyFrame* keyFrame, QString path) vecImage->setFileName(""); DebugDetails dd; - dd << __FUNCTION__; - dd << QString("KeyFrame.pos() = %1").arg(keyFrame->pos()); - dd << QString("FilePath = ").append(strFilePath); - dd << "- VectorImage failed to write"; + dd << "LayerVector::saveKeyFrameFile"; + dd << QString(" KeyFrame.pos() = %1").arg(keyFrame->pos()); + dd << QString(" strFilePath = ").append(strFilePath); + dd << "Error: Failed to save VectorImage"; dd.collect(st.details()); return Status(Status::FAIL, dd); } diff --git a/core_lib/src/structure/object.cpp b/core_lib/src/structure/object.cpp index bb25cdb1e0..0828cd9c70 100644 --- a/core_lib/src/structure/object.cpp +++ b/core_lib/src/structure/object.cpp @@ -525,7 +525,6 @@ bool Object::exportPalette(const QString& filePath) const else exportPalettePencil(file); - file.close(); return true; } @@ -672,7 +671,6 @@ bool Object::importPalette(const QString& filePath) } else { importPalettePencil(file); } - file.close(); return true; } diff --git a/core_lib/src/util/pencilerror.cpp b/core_lib/src/util/pencilerror.cpp index 37198c48c3..cc6be5e934 100644 --- a/core_lib/src/util/pencilerror.cpp +++ b/core_lib/src/util/pencilerror.cpp @@ -18,6 +18,7 @@ GNU General Public License for more details. #include "pencilerror.h" #include #include +#include DebugDetails::DebugDetails() { @@ -55,22 +56,22 @@ void DebugDetails::appendSystemInfo() return; #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) - mDetails << "System Info"; + mDetails << "\n[System Info]\n"; #if defined(PENCIL2D_RELEASE_BUILD) - mDetails << "Pencil2D version: " APP_VERSION " (stable)"; + mDetails << "  Pencil2D version: " APP_VERSION " (stable)"; #elif defined(PENCIL2D_NIGHTLY_BUILD) - mDetails << "Pencil2D version: " APP_VERSION " (nightly)"; + mDetails << "  Pencil2D version: " APP_VERSION " (nightly)"; #else - mDetails << "Pencil2D version: " APP_VERSION " (dev)"; + mDetails << "  Pencil2D version: " APP_VERSION " (dev)"; #endif #if defined(GIT_EXISTS) - mDetails << "Commit: " S__GIT_COMMIT_HASH; + mDetails << "  Commit: " S__GIT_COMMIT_HASH; #endif - mDetails << "Build ABI: " + QSysInfo::buildAbi(); - mDetails << "Kernel: " + QSysInfo::kernelType() + ", " + QSysInfo::kernelVersion(); - mDetails << "Operating System: " + QSysInfo::prettyProductName(); - mDetails << "end"; + mDetails << "  Build ABI: " + QSysInfo::buildAbi(); + mDetails << "  Kernel: " + QSysInfo::kernelType() + ", " + QSysInfo::kernelVersion(); + mDetails << "  Operating System: " + QSysInfo::prettyProductName(); + mDetails << "  Language: " + QLocale::system().name(); #endif }