--- a/src/model.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/model.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -16,254 +16,169 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <QBrush> -#include <QFile> -#include <QFileInfo> -#include <QFont> -#include <QSaveFile> #include "model.h" -#include "modeleditor.h" -#include "documentmanager.h" -/** - * @brief Constructs a model - * @param parent QObject parent to pass forward - */ +QString modelElementToString(const ModelElement &element) +{ + return std::visit(overloaded{ + [](const Colored<SubfileReference>& ref) { + return QStringLiteral("1 %1 %2 %3") + .arg(ref.color.index) + .arg(transformToString(ref.transformation)) + .arg(ref.name); + }, + [](const Colored<LineSegment>& seg) { + return QStringLiteral("2 %1 %2 %3") + .arg(seg.color.index) + .arg(vertexToString(seg.p1)) + .arg(vertexToString(seg.p2)); + }, + [](const Colored<Triangle>& triangle) { + return QStringLiteral("3 %1 %2 %3 %4") + .arg(triangle.color.index) + .arg(vertexToString(triangle.p1)) + .arg(vertexToString(triangle.p2)) + .arg(vertexToString(triangle.p3)); + }, + [](const Colored<Quadrilateral>& quad) { + return QStringLiteral("4 %1 %2 %3 %4 %5") + .arg(quad.color.index) + .arg(vertexToString(quad.p1)) + .arg(vertexToString(quad.p2)) + .arg(vertexToString(quad.p3)) + .arg(vertexToString(quad.p4)); + }, + [](const Colored<ConditionalEdge>& cedge) { + return QStringLiteral("5 %1 %2 %3 %4 %5") + .arg(cedge.color.index) + .arg(vertexToString(cedge.p1)) + .arg(vertexToString(cedge.p2)) + .arg(vertexToString(cedge.c1)) + .arg(vertexToString(cedge.c2)); + }, + [](const Comment& comment) { + return "0 " + comment.text; + }, + [](const Empty&) { + return QStringLiteral(""); + }, + [](const ParseError& parseError) { + return parseError.code; + }, + }, element); +} + Model::Model(QObject *parent) : QAbstractListModel{parent} { } -/** - * @returns the amount of elements in the model - */ -int Model::size() const +Model::~Model() +{ +} + +ModelId Model::append(const ModelElement &value) { - return static_cast<int>(this->body.size()); + const int position = static_cast<int>(this->body.size()); + const ModelId id = this->runningId; + this->runningId.value += 1; + Q_EMIT this->beginInsertRows({}, position, position); + this->body.push_back({value, id}); + this->positions[id] = position; + Q_EMIT this->endInsertRows(); + return id; +} + +const ModelElement &Model::at(int position) const +{ + return this->body[position].data; } -/** - * @brief Looks up the object ID at the specified index. If out of bounds, returns NULL_ID. - * @param index Index of object to look up - * @return object ID - */ -ldraw::id_t Model::at(int index) const +ModelId Model::idAt(int position) const +{ + return this->body[position].id; +} + +void Model::assignAt(int position, const ModelElement &element) { - if (index >= 0 and index < this->size()) - { - return this->body[index]->id; - } - else - { - return ldraw::NULL_ID; + this->body[position].data = element; + const QModelIndex index = this->index(position); + Q_EMIT this->dataChanged(index, index); +} + +std::optional<int> Model::find(ModelId id) const +{ + return pointerToOptional(findInMap(this->positions, id)); +} + +void Model::remove(int index) +{ + if (index >= 0 and index < this->size()) { + Q_EMIT this->beginRemoveRows({}, index, index); + this->body.erase(this->body.begin() + index); + Q_EMIT this->endRemoveRows(); } } -/** - * @brief @overload QAbstractListModel::rowCount - * @return size - */ -int Model::rowCount(const QModelIndex&) const +int Model::rowCount(const QModelIndex &) const { return this->size(); } -/** - * @brief @overload QAbstractListModel::data - * @param index - * @param role - * @return QVariant - */ -QVariant Model::data(const QModelIndex& index, int role) const +QVariant Model::data(const QModelIndex &index, int role) const { - const ldraw::Object* object = (*this)[index.row()]; + const int i = index.row(); switch(role) { + /* case Qt::DecorationRole: return QPixmap{object->iconName()}.scaledToHeight(24); + */ case Qt::DisplayRole: - return object->textRepresentation(); + return modelElementToString(this->body[i].data); + /* case Qt::ForegroundRole: return object->textRepresentationForeground(); case Qt::BackgroundRole: return object->textRepresentationBackground(); case Qt::FontRole: return object->textRepresentationFont(); + */ default: return {}; } } -/** - * @brief Finds the position of the specified object in the model - * @param id Object id to look for - * @return model index - */ -QModelIndex Model::find(ldraw::id_t id) const +const ModelElement &Model::operator[](int index) const +{ + return this->body[index].data; +} + +int Model::size() const { - if (this->needObjectsByIdRebuild) - { - this->objectsById.clear(); - for (std::size_t i = 0; i < this->body.size(); ++i) - { - this->objectsById[this->body[i]->id] = i; - } - this->needObjectsByIdRebuild = false; - } - const auto it = this->objectsById.find(id); - if (it != this->objectsById.end()) - { - return this->index(it->second); - } - else - { - return {}; + return this->body.size(); +} + +void save(const Model &model, QIODevice *device) +{ + QTextStream out{device}; + for (int i = 0; i < model.size(); ++i) { + out << modelElementToString(model[i]) << "\r\n"; } } /** - * @brief Gets an object id by position in the model - * @param index Position of the object in the model - * @return id - */ -ldraw::id_t Model::idAt(const QModelIndex& index) const -{ - return (*this)[index.row()]->id; -} - -#if 0 -/** * @brief Sets the path to the model * @param path New path to use */ -void Model::setPath(const QString &path) +void updateHeaderNameField(Model& model, const QString &name) { - this->storedPath = path; - this->header.name = QFileInfo{path}.fileName(); // Update the "Name: 1234.dat" comment - if (this->body.size() >= 2) - { - const ldraw::id_t id = this->body[1]->id; - if (this->isA<ldraw::MetaCommand>(id)) - { - const QString& textBody = this->body[1]->getProperty<ldraw::Property::Text>(); - if (textBody.startsWith("Name: ")) - { - auto editor = this->edit(); - editor.setObjectProperty<ldraw::Property::Text>(id, "Name: " + this->header.name); + if (model.size() >= 2) { + if (const Comment* nameObject = std::get_if<Comment>(&model[1])) { + if (nameObject->text.startsWith("Name: ")) { + model[1] = Comment{"Name: " + name}; } } } } -#endif - -/** - * @brief Adds the given object into the model. - * @param object r-value reference to the object - */ -ldraw::id_t Model::append(ModelObjectPointer&& object) -{ - const int position = static_cast<int>(this->body.size()); - Q_EMIT this->beginInsertRows({}, position, position); - this->body.push_back(std::move(object)); - Q_EMIT this->endInsertRows(); - const ldraw::id_t id = this->body.back()->id; - this->objectsById[id] = this->body.size() - 1; - return id; -} - -/** - * @brief Removes the object at the specified position - * @param position - */ -void Model::remove(int position) -{ - if (position >= 0 and position < signed_cast(this->body.size())) - { - Q_EMIT this->beginRemoveRows({}, position, position); - this->body.erase(std::begin(this->body) + position); - this->needObjectsByIdRebuild = true; - Q_EMIT this->endRemoveRows(); - } -} - -void Model::emitDataChangedSignal(int position) -{ - Q_EMIT this->dataChanged(this->index(position), this->index(position)); -} - -/** - * @brief Gets the object pointer at the specified position - * @param index Position of the object - * @returns object pointer - */ -ldraw::Object* Model::operator[](int index) -{ - if (index >= 0 and index < this->size()) - { - return this->body[index].get(); - } - else - { - throw std::out_of_range{"index out of range"}; - } -} - -/** - * @brief Gets the object pointer at the specified position - * @param index Position of the object - * @returns object pointer - */ -const ldraw::Object* Model::operator[](int index) const -{ - if (index >= 0 and index < this->size()) - { - return this->body[index].get(); - } - else - { - throw std::out_of_range{"index out of range"}; - } -} - -/** - * @brief Gets an object pointer by id. Used by the editing context to actually modify objects. - * @param id - * @return object pointer - */ -ldraw::Object* Model::findObjectById(const ldraw::id_t id) -{ - const QModelIndex index = this->find(id); - if (index.isValid()) - { - return (*this)[index.row()]; - } - else - { - return nullptr; - } -} - -const ldraw::Object* Model::findObjectById(const ldraw::id_t id) const -{ - const QModelIndex index = this->find(id); - if (index.isValid()) - { - return (*this)[index.row()]; - } - else - { - return nullptr; - } -} - -/** - * @brief Attempts the save the model - */ -void save(const Model &model, QIODevice *device) -{ - QTextStream out{device}; - applyToModel<ldraw::Object>(model, [&](const ldraw::Object* object) { - out << object->toLDrawCode() << "\r\n"; - }); -}