src/documentmanager.cpp

Wed, 22 Sep 2021 13:28:53 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 22 Sep 2021 13:28:53 +0300
changeset 138
5d6a4ad46cc7
parent 35
98906a94732f
child 140
2f383e88acf4
permissions
-rw-r--r--

Document model.h

/*
 *  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>();
	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);
			}
		}
	}
}

mercurial