Tue, 28 Sep 2021 23:07:23 +0300
Use QSaveFile to save the file more safely
/* * LDForge: LDraw parts authoring CAD * Copyright (C) 2013 - 2020 Teemu Piippo * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ #include <QFile> #include <QDir> #include <QFileInfo> #include "documentmanager.h" #include "modeleditcontext.h" #include "linetypes/comment.h" #include "parser.h" /** * @brief Constructs a new document manager * @param parent Parent object */ DocumentManager::DocumentManager(QObject* parent) : QObject{parent} { } /** * @brief Creates a new model. * @returns the name to the new model */ QString DocumentManager::newModel() { const QString name = makeNewModelName(); this->openModels.emplace(name, new Model); return name; } /** * @brief Looks for a model by name * @param name Name of the model * @returns model or null * ' */ Model* DocumentManager::findModelByName(const QString& name) { const auto iterator = this->openModels.find(name); if (iterator == std::end(this->openModels)) { return nullptr; } else { return iterator->second.get(); } } QString pathToName(const QFileInfo& path) { static const char* paths[] = { "s", "48" "8" }; const QString baseName = path.fileName(); const QString dirName = QFileInfo{path.dir().path()}.fileName(); QString result; if (std::find(std::begin(paths), std::end(paths), dirName) != std::end(paths)) { result = dirName + "\\" + baseName; } else { result = baseName; } return result; } QString DocumentManager::openModel(const QString& path, QTextStream& errorStream) { QFile file{path}; const QString name = pathToName(path); file.open(QFile::ReadOnly | QFile::Text); std::unique_ptr<Model> newModel = std::make_unique<Model>(path); QTextStream textStream{&file}; Model::EditContext editor = newModel->edit(); Parser parser{file}; parser.parseBody(editor); QString result; if (file.error() == QFile::NoError) { openModels[name] = std::move(newModel); result = name; } else { errorStream << file.errorString(); } return result; } QString DocumentManager::makeNewModelName() { untitledNameCounter += 1; return "untitled-" + QString::number(untitledNameCounter); } void DocumentManager::loadDependenciesForModel( const QString& modelName, const QString& path, const LibraryManager& libraries, QTextStream& errorStream) { QStringList missing; QStringList processed; loadDependenciesForModel(modelName, path, libraries, missing, processed, errorStream); if (not missing.empty()) { missing.sort(Qt::CaseInsensitive); errorStream << utility::format( "The following files could not be opened: %1", missing.join(", ")); } } static QString findFile(QString referenceName, const QString& path, const LibraryManager& libraries) { // Try to find the file in the same place as the model itself referenceName.replace("\\", "/"); const QDir dir = QFileInfo{path}.dir(); QString referencedFilePath = dir.filePath(referenceName); if (not QFileInfo{referencedFilePath}.exists()) { // Look for it in the libraries referencedFilePath = libraries.findFile(referenceName); } return referencedFilePath; } void DocumentManager::loadDependenciesForModel( const QString& modelName, const QString &path, const LibraryManager& libraries, QStringList& missing, QStringList& processed, QTextStream& errorStream) { struct LoadingError { QString message; }; processed.append(modelName); Model* model = this->findModelByName(modelName); for (int i = 0; i < model->size(); i += 1) { const QString referenceName = model->getObjectProperty(i, ldraw::Property::ReferenceName).toString(); if (not referenceName.isEmpty() and openModels.find(referenceName) == std::end(openModels) and not missing.contains(referenceName)) { try { const QString referencedFilePath = findFile(referenceName, path, libraries); if (referencedFilePath.isEmpty()) { throw LoadingError{utility::format("'%1' was not found.", referenceName)}; } QString errorString; QTextStream localErrorStream{&errorString}; QString resultName = this->openModel(referencedFilePath, localErrorStream); if (resultName.isEmpty()) { throw LoadingError{utility::format( "could not load '%1': %2", referencedFilePath, errorString)}; } if (not processed.contains(referenceName)) { loadDependenciesForModel(referenceName, path, libraries, missing, processed, errorStream); } } catch(const LoadingError& error) { errorStream << error.message << "\n"; missing.append(referenceName); processed.append(referenceName); } } } }