Mon, 05 Mar 2018 22:40:34 +0200
Added LDObject serialization and refactored the internal resource managing to use it. No more tearing objects from one model into another, and this provides a stable way to keep an object's state in memory such as the edit history.
--- a/CMakeLists.txt Sat Mar 03 17:59:56 2018 +0200 +++ b/CMakeLists.txt Mon Mar 05 22:40:34 2018 +0200 @@ -53,6 +53,7 @@ src/partdownloader.cpp src/partdownloadrequest.cpp src/primitives.cpp + src/serializer.cpp src/ringFinder.cpp src/version.cpp src/dialogs/colorselector.cpp @@ -120,6 +121,7 @@ src/partdownloadrequest.h src/primitives.h src/ringFinder.h + src/serializer.h src/transform.h src/dialogs/colorselector.h src/dialogs/configdialog.h
--- a/src/colors.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/colors.h Mon Mar 05 22:40:34 2018 +0200 @@ -92,6 +92,7 @@ qint32 m_index; }; +Q_DECLARE_METATYPE(LDColor) uint qHash(LDColor color); /*
--- a/src/editHistory.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/editHistory.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -28,6 +28,11 @@ m_isIgnoring (false), m_position (-1) {} +EditHistory::~EditHistory() +{ + clear(); +} + void EditHistory::undo() { if (m_changesets.isEmpty() or position() == -1) @@ -58,7 +63,7 @@ const Changeset& set = changesetAt (position() + 1); // Redo things in original order - for (const AbstractHistoryEntry* change : set) + for (AbstractHistoryEntry* change : set) change->redo(); ++m_position; @@ -96,18 +101,6 @@ emit stepAdded(); } -void EditHistory::add (AbstractHistoryEntry* entry) -{ - if (isIgnoring()) - { - delete entry; - return; - } - - entry->setParent (this); - m_currentChangeset << entry; -} - int EditHistory::size() const { return countof(m_changesets); @@ -138,11 +131,9 @@ return m_document; } -// -// --------------------------------------------------------------------------------------------------------------------- -// +AbstractHistoryEntry::AbstractHistoryEntry(EditHistory* parent) : + m_parent {parent} {} -AbstractHistoryEntry::AbstractHistoryEntry() {} AbstractHistoryEntry::~AbstractHistoryEntry() {} EditHistory* AbstractHistoryEntry::parent() const @@ -150,83 +141,65 @@ return m_parent; } -void AbstractHistoryEntry::setParent (EditHistory* parent) -{ - m_parent = parent; -} +AddHistoryEntry::AddHistoryEntry(const QModelIndex& index, EditHistory* parent) : + AbstractHistoryEntry {parent}, + m_row {index.row()}, + m_code {Serializer::store(parent->document()->lookup(index))} {} -// -// --------------------------------------------------------------------------------------------------------------------- -// - -AddHistoryEntry::AddHistoryEntry (int idx, LDObject* obj) : - m_index (idx), - m_code (obj->asText()) {} - -void AddHistoryEntry::undo() const +void AddHistoryEntry::undo() { - LDObject* object = parent()->document()->getObject(m_index); + LDObject* object = parent()->document()->getObject(m_row); parent()->document()->remove(object); } -void AddHistoryEntry::redo() const +void AddHistoryEntry::redo() { - parent()->document()->insertFromString(m_index, m_code); + parent()->document()->insertFromArchive(m_row, m_code); } -// -// --------------------------------------------------------------------------------------------------------------------- -// - -DelHistoryEntry::DelHistoryEntry (int idx, LDObject* obj) : - AddHistoryEntry (idx, obj) {} - -void DelHistoryEntry::undo() const +void DelHistoryEntry::undo() { AddHistoryEntry::redo(); } -void DelHistoryEntry::redo() const +void DelHistoryEntry::redo() { AddHistoryEntry::undo(); } -// -// --------------------------------------------------------------------------------------------------------------------- -// +EditHistoryEntry::EditHistoryEntry( + const QModelIndex& index, + const Serializer::Archive& oldState, + const Serializer::Archive& newState, + EditHistory* parent +) : + AbstractHistoryEntry {parent}, + row {index.row()}, + oldState {oldState}, + newState {newState} {} -EditHistoryEntry::EditHistoryEntry (int idx, QString oldCode, QString newCode) : - m_index (idx), - m_oldCode (oldCode), - m_newCode (newCode) {} - -void EditHistoryEntry::undo() const +void EditHistoryEntry::undo() { - LDObject* object = parent()->document()->getObject (m_index); - parent()->document()->replaceWithFromString(object, m_oldCode); + parent()->document()->setObjectAt(row, oldState); } -void EditHistoryEntry::redo() const +void EditHistoryEntry::redo() { - LDObject* object = parent()->document()->getObject (m_index); - parent()->document()->replaceWithFromString(object, m_newCode); + parent()->document()->setObjectAt(row, newState); } -// -// --------------------------------------------------------------------------------------------------------------------- -// - -SwapHistoryEntry::SwapHistoryEntry (int a, int b) : +SwapHistoryEntry::SwapHistoryEntry (int a, int b, EditHistory* parent) : + AbstractHistoryEntry {parent}, m_a (a), m_b (b) {} -void SwapHistoryEntry::undo() const +void SwapHistoryEntry::undo() { parent()->document()->swapObjects(LDObject::fromID (m_a), LDObject::fromID (m_b)); } -void SwapHistoryEntry::redo() const +void SwapHistoryEntry::redo() { undo(); }
--- a/src/editHistory.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/editHistory.h Mon Mar 05 22:40:34 2018 +0200 @@ -18,6 +18,7 @@ #pragma once #include "main.h" +#include "serializer.h" #include "linetypes/modelobject.h" class AbstractHistoryEntry; @@ -30,8 +31,15 @@ using Changeset = QList<AbstractHistoryEntry*>; EditHistory (LDDocument* document); + ~EditHistory(); - void add (AbstractHistoryEntry* entry); + template<typename T, typename... Args> + void add(Args&&... args) + { + if (not isIgnoring()) + m_currentChangeset << new T {args..., this}; + } + void addStep(); const Changeset& changesetAt (int pos) const; void clear(); @@ -59,57 +67,61 @@ class AbstractHistoryEntry { public: - AbstractHistoryEntry(); + AbstractHistoryEntry(EditHistory* parent); virtual ~AbstractHistoryEntry(); EditHistory* parent() const; - virtual void redo() const = 0; - void setParent (EditHistory* parent); - virtual void undo() const = 0; + virtual void redo() = 0; + virtual void undo() = 0; private: - EditHistory* m_parent; + EditHistory* const m_parent; }; class AddHistoryEntry : public AbstractHistoryEntry { public: - AddHistoryEntry (int idx, LDObject* obj); - void undo() const override; - void redo() const override; + AddHistoryEntry (const QModelIndex& index, EditHistory* parent); + void undo() override; + void redo() override; private: - int m_index; - QString m_code; + int m_row; + Serializer::Archive m_code; }; class DelHistoryEntry : public AddHistoryEntry { public: - DelHistoryEntry (int idx, LDObject* obj); - void undo() const override; - void redo() const override; + using AddHistoryEntry::AddHistoryEntry; + void undo() override; + void redo() override; }; class EditHistoryEntry : public AbstractHistoryEntry { public: - EditHistoryEntry (int idx, QString oldCode, QString newCode); - void undo() const override; - void redo() const override; + EditHistoryEntry( + const QModelIndex& index, + const Serializer::Archive& oldCode, + const Serializer::Archive& newCode, + EditHistory* parent + ); + void undo() override; + void redo() override; private: - int m_index; - QString m_oldCode; - QString m_newCode; + int row; + Serializer::Archive oldState; + Serializer::Archive newState; }; class SwapHistoryEntry : public AbstractHistoryEntry { public: - SwapHistoryEntry (int a, int b); - void undo() const override; - void redo() const override; + SwapHistoryEntry (int a, int b, EditHistory* parent); + void undo() override; + void redo() override; private: int m_a;
--- a/src/lddocument.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/lddocument.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -33,7 +33,15 @@ m_history (new EditHistory (this)), m_savePosition(-1), m_tabIndex(-1), - m_manager (parent) {} + m_manager (parent) +{ + connect( + this, + SIGNAL(objectAdded(QModelIndex)), + this, + SLOT(handleNewObject(QModelIndex)) + ); +} LDDocument::~LDDocument() { @@ -131,11 +139,6 @@ history()->clear(); } -void LDDocument::addToHistory (AbstractHistoryEntry* entry) -{ - history()->add (entry); -} - void LDDocument::close() { if (not isFrozen()) @@ -257,41 +260,53 @@ return true; } -// ============================================================================= -// -void LDDocument::insertObject (int pos, LDObject* obj) +void LDDocument::handleNewObject(const QModelIndex& index) { - Model::insertObject(pos, obj); - history()->add(new AddHistoryEntry {pos, obj}); - connect(obj, SIGNAL(codeChanged(QString,QString)), this, SLOT(objectChanged(QString,QString))); + LDObject* object = lookup(index); + history()->add<AddHistoryEntry>(index); + connect( + object, + SIGNAL(codeChanged(LDObjectState, LDObjectState)), + this, + SLOT(objectChanged(LDObjectState, LDObjectState)) + ); + connect( + this, + SIGNAL(aboutToRemoveObject(QModelIndex)), + this, + SLOT(handleImminentObjectRemoval(QModelIndex)), + Qt::DirectConnection + ); #ifdef DEBUG if (not isFrozen()) - print("Inserted object #%1 (%2) at %3\n", obj->id(), obj->typeName(), pos); + print("Inserted object #%1 (%2) at %3\n", object->id(), object->typeName(), index.row()); #endif } -void LDDocument::objectChanged(QString before, QString after) +void LDDocument::objectChanged(const LDObjectState& before, const LDObjectState& after) { - LDObject* object = static_cast<LDObject*>(sender()); - QModelIndex index = this->indexOf(object); - addToHistory(new EditHistoryEntry {index.row(), before, after}); - redoVertices(); - emit objectModified(object); - emit dataChanged(index, index); + LDObject* object = qobject_cast<LDObject*>(sender()); + + if (object) + { + QModelIndex index = this->indexOf(object); + history()->add<EditHistoryEntry>(index, before, after); + redoVertices(); + emit objectModified(object); + emit dataChanged(index, index); + } } -LDObject* LDDocument::withdrawAt(int position) +void LDDocument::handleImminentObjectRemoval(const QModelIndex& index) { - LDObject* object = getObject(position); + LDObject* object = lookup(index); if (not isFrozen() and not m_isBeingDestroyed) { - history()->add(new DelHistoryEntry {position, object}); + history()->add<DelHistoryEntry>(index); m_objectVertices.remove(object); } - - return Model::withdrawAt(position); } // ============================================================================= @@ -408,7 +423,7 @@ { if (Model::swapObjects(one, other)) { - addToHistory(new SwapHistoryEntry {one->id(), other->id()}); + history()->add<SwapHistoryEntry>(one->id(), other->id()); return true; } else
--- a/src/lddocument.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/lddocument.h Mon Mar 05 22:40:34 2018 +0200 @@ -47,7 +47,6 @@ ~LDDocument(); void addHistoryStep(); - void addToHistory (AbstractHistoryEntry* entry); void clearHistory(); void close(); QString defaultName() const; @@ -59,7 +58,6 @@ void inlineContents(Model& model, bool deep, bool renderinline); QList<LDPolygon> inlinePolygons(); const QSet<Vertex>& inlineVertices(); - void insertObject (int pos, LDObject* obj); bool isFrozen() const; bool isSafeToClose(); QString name() const; @@ -83,9 +81,6 @@ static QString shortenName (QString a); // Turns a full path into a relative path -public slots: - void objectChanged(QString before, QString after); - protected: LDObject* withdrawAt(int position); @@ -105,6 +100,17 @@ QMap<LDObject*, QSet<Vertex>> m_objectVertices; QSet<Vertex> m_vertices; DocumentManager* m_manager; + + template<typename T, typename... Args> + void addToHistory(Args&&... args) + { + m_history->add<T>(args...); + } + +private slots: + void objectChanged(const LDObjectState &before, const LDObjectState &after); + void handleNewObject(const QModelIndex& index); + void handleImminentObjectRemoval(const QModelIndex& index); }; // Parses a string line containing an LDraw object and returns the object parsed.
--- a/src/linetypes/comment.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/comment.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -17,12 +17,9 @@ */ #include "comment.h" +#include "../editHistory.h" -LDComment::LDComment(Model* model) : - LDObject {model} {} - -LDComment::LDComment(QString text, Model* model) : - LDObject {model}, +LDComment::LDComment(QString text) : m_text {text} {} LDObjectType LDComment::type() const @@ -64,3 +61,9 @@ { return format ("0 %1", text()); } + +void LDComment::serialize(Serializer& serializer) +{ + LDObject::serialize(serializer); + serializer << m_text; +}
--- a/src/linetypes/comment.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/comment.h Mon Mar 05 22:40:34 2018 +0200 @@ -25,7 +25,9 @@ class LDComment : public LDObject { public: - static constexpr LDObjectType SubclassType = LDObjectType::Comment; + static const LDObjectType SubclassType = LDObjectType::Comment; + + LDComment(QString text = ""); QString asText() const override; bool isColored() const override; @@ -35,11 +37,7 @@ LDObjectType type() const override; QString typeName() const override; void setText(QString value); - -protected: - LDComment(QString text, Model* model); - LDComment(Model* model); - friend class Model; + void serialize(class Serializer& serializer) override; private: QString m_text;
--- a/src/linetypes/conditionaledge.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/conditionaledge.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -19,11 +19,7 @@ #include "../model.h" #include "conditionaledge.h" -LDConditionalEdge::LDConditionalEdge(Model* model) : - LDEdgeLine {model} {} - -LDConditionalEdge::LDConditionalEdge (const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model) : - LDEdgeLine {model} +LDConditionalEdge::LDConditionalEdge (const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3) { setVertex (0, v0); setVertex (1, v1);
--- a/src/linetypes/conditionaledge.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/conditionaledge.h Mon Mar 05 22:40:34 2018 +0200 @@ -25,11 +25,14 @@ class LDConditionalEdge : public LDEdgeLine { public: - static constexpr LDObjectType SubclassType = LDObjectType::ConditionalEdge; + static const LDObjectType SubclassType = LDObjectType::ConditionalEdge; + + LDConditionalEdge() = default; + LDConditionalEdge(const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3); virtual LDObjectType type() const override { - return SubclassType; + return LDObjectType::ConditionalEdge; } virtual QString asText() const override; @@ -37,9 +40,4 @@ int numPolygonVertices() const override { return 2; } LDColor defaultColor() const override { return EdgeColor; } QString typeName() const override { return "condline"; } - -protected: - friend class Model; - LDConditionalEdge (Model* model); - LDConditionalEdge (const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model = nullptr); };
--- a/src/linetypes/edgeline.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/edgeline.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -19,16 +19,9 @@ #include "edgeline.h" /* - * Default-constructs this edge line. - */ -LDEdgeLine::LDEdgeLine(Model* model) : - LDObject {model} {} - -/* * Constructs this edge line from two vertices. */ -LDEdgeLine::LDEdgeLine (Vertex v1, Vertex v2, Model* model) : - LDObject {model} +LDEdgeLine::LDEdgeLine (Vertex v1, Vertex v2) { setVertex(0, v1); setVertex(1, v2);
--- a/src/linetypes/edgeline.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/edgeline.h Mon Mar 05 22:40:34 2018 +0200 @@ -25,20 +25,18 @@ class LDEdgeLine : public LDObject { public: - static constexpr LDObjectType SubclassType = LDObjectType::EdgeLine; + static const LDObjectType SubclassType = LDObjectType::EdgeLine; + + LDEdgeLine() = default; + LDEdgeLine(Vertex v1, Vertex v2); virtual LDObjectType type() const override { - return SubclassType; + return LDObjectType::EdgeLine; } virtual QString asText() const override; int numVertices() const override { return 2; } LDColor defaultColor() const override { return EdgeColor; } QString typeName() const override { return "line"; } - -protected: - friend class Model; - LDEdgeLine (Model* model); - LDEdgeLine (Vertex v1, Vertex v2, Model* model = nullptr); };
--- a/src/linetypes/empty.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/empty.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -19,9 +19,6 @@ #include "empty.h" -LDEmpty::LDEmpty(Model* model) : - LDObject{model} {} - QString LDEmpty::typeName() const { return "empty";
--- a/src/linetypes/empty.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/empty.h Mon Mar 05 22:40:34 2018 +0200 @@ -31,8 +31,4 @@ QString objectListText() const override; LDObjectType type() const override; QString typeName() const override; - -protected: - friend class Model; - LDEmpty(Model* model); };
--- a/src/linetypes/modelobject.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/modelobject.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -27,27 +27,23 @@ #include "../colors.h" #include "../glcompiler.h" #include "edgeline.h" +#include "triangle.h" +#include "quadrilateral.h" +#include "conditionaledge.h" +#include "comment.h" +#include "empty.h" // List of all LDObjects QMap<qint32, LDObject*> g_allObjects; enum { MAX_LDOBJECT_IDS = (1 << 24) }; -#define LDOBJ_DEFAULT_CTOR(T,BASE) \ - T :: T (Model* model) : \ - BASE {model} {} - // ============================================================================= // LDObject constructors // -LDObject::LDObject (Model* model) : - m_isHidden {false}, - m_isSelected {false}, - _model {model}, - m_coords {{0, 0, 0}} +LDObject::LDObject() : + m_isHidden {false} { - assert(_model != nullptr); - // Let's hope that nobody goes to create 17 million objects anytime soon... static qint32 nextId = 1; // 0 shalt be null if (nextId < MAX_LDOBJECT_IDS) @@ -61,13 +57,6 @@ m_randomColor = QColor::fromHsv (rand() % 360, rand() % 256, rand() % 96 + 128); } -LDSubfileReference::LDSubfileReference (Model* model) : - LDMatrixObject (model) {} - -LDOBJ_DEFAULT_CTOR (LDError, LDObject) -LDOBJ_DEFAULT_CTOR (LDBfc, LDObject) -LDOBJ_DEFAULT_CTOR (LDBezierCurve, LDObject) - LDObject::~LDObject() { // Remove this object from the list of LDObjects @@ -132,13 +121,12 @@ // ============================================================================= // -LDBezierCurve::LDBezierCurve(const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model) : - LDObject {model} +LDBezierCurve::LDBezierCurve(const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3) { - setVertex (0, v0); - setVertex (1, v1); - setVertex (2, v2); - setVertex (3, v3); + setVertex(0, v0); + setVertex(1, v1); + setVertex(2, v2); + setVertex(3, v3); } // ============================================================================= @@ -287,11 +275,6 @@ m_isHidden = value; } -bool LDObject::isSelected() const -{ - return m_isSelected; -} - qint32 LDObject::id() const { return m_id; @@ -319,6 +302,47 @@ return g_allObjects.value(id); } +LDObject* LDObject::newFromType(LDObjectType type) +{ + switch (type) + { + case LDObjectType::SubfileReference: + return new LDSubfileReference {}; + + case LDObjectType::Quadrilateral: + return new LDQuadrilateral {}; + + case LDObjectType::Triangle: + return new LDTriangle {}; + + case LDObjectType::EdgeLine: + return new LDEdgeLine {}; + + case LDObjectType::ConditionalEdge: + return new LDConditionalEdge {}; + + case LDObjectType::Bfc: + return new LDBfc {}; + + case LDObjectType::Comment: + return new LDComment {}; + + case LDObjectType::Error: + return new LDError {}; + + case LDObjectType::Empty: + return new LDEmpty {}; + + case LDObjectType::BezierCurve: + return new LDBezierCurve {}; + + case LDObjectType::_End: + break; + } + + return nullptr; +} + // ============================================================================= // void LDObject::setColor (LDColor color) @@ -344,12 +368,7 @@ changeProperty(&m_coords[i], vert); } -LDMatrixObject::LDMatrixObject (Model* model) : - LDObject (model), - m_position {0, 0, 0} {} - -LDMatrixObject::LDMatrixObject (const Matrix& transform, const Vertex& pos, Model* model) : - LDObject (model), +LDMatrixObject::LDMatrixObject (const Matrix& transform, const Vertex& pos) : m_position (pos), m_transformationMatrix (transform) {} @@ -391,8 +410,7 @@ changeProperty(&m_transformationMatrix, val); } -LDError::LDError (QString contents, QString reason, Model* model) : - LDObject (model), +LDError::LDError (QString contents, QString reason) : m_contents (contents), m_reason (reason) {} @@ -406,8 +424,7 @@ return m_contents; } -LDBfc::LDBfc (const BfcStatement type, Model* model) : - LDObject {model}, +LDBfc::LDBfc (const BfcStatement type) : m_statement {type} {} BfcStatement LDBfc::statement() const @@ -501,10 +518,9 @@ LDSubfileReference::LDSubfileReference( QString referenceName, const Matrix& transformationMatrix, - const Vertex& position, - Model* model + const Vertex& position ) : - LDMatrixObject {transformationMatrix, position, model}, + LDMatrixObject {transformationMatrix, position}, m_referenceName {referenceName} {} // ============================================================================= @@ -582,3 +598,42 @@ { changeProperty(&m_hasInvertNext, value); } + +void LDObject::serialize(Serializer& serializer) +{ + serializer << m_hasInvertNext; + serializer << m_isHidden; + serializer << m_id; + serializer << m_color; + serializer << m_randomColor; + serializer << m_coords[0]; + serializer << m_coords[1]; + serializer << m_coords[2]; + serializer << m_coords[3]; +} + +void LDMatrixObject::serialize(Serializer& serializer) +{ + LDObject::serialize(serializer); + serializer << m_position; + serializer << m_transformationMatrix; +} + +void LDBfc::serialize(Serializer& serializer) +{ + LDObject::serialize(serializer); + serializer << m_statement; +} + +void LDError::serialize(Serializer& serializer) +{ + LDObject::serialize(serializer); + serializer << m_contents; + serializer << m_reason; +} + +void LDSubfileReference::serialize(Serializer& serializer) +{ + LDMatrixObject::serialize(serializer); + serializer << m_referenceName; +}
--- a/src/linetypes/modelobject.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/modelobject.h Mon Mar 05 22:40:34 2018 +0200 @@ -22,6 +22,7 @@ #include "../basics.h" #include "../glShared.h" #include "../colors.h" +#include "../serializer.h" class Model; class LDDocument; @@ -45,6 +46,7 @@ _End }; +Q_DECLARE_METATYPE(LDObjectType) MAKE_ITERABLE_ENUM(LDObjectType) inline int qHash(LDObjectType type) @@ -60,6 +62,9 @@ Q_OBJECT public: + LDObject(); + virtual ~LDObject(); + virtual QString asText() const = 0; // This object as LDraw code LDColor color() const; virtual LDColor defaultColor() const; // What color does the object default to? @@ -71,7 +76,6 @@ virtual bool isColored() const; bool isHidden() const; virtual bool isScemantic() const; // Does this object have meaning in the part model? - bool isSelected() const; void move (Vertex vect); virtual int numVertices() const; virtual int numPolygonVertices() const; @@ -86,16 +90,15 @@ virtual LDObjectType type() const = 0; virtual QString typeName() const = 0; const Vertex& vertex (int i) const; + virtual void serialize(class Serializer& serializer); static LDObject* fromID(qint32 id); + static LDObject* newFromType(LDObjectType type); signals: - void codeChanged(QString before, QString after); + void codeChanged(const LDObjectState& before, const LDObjectState& after); protected: - friend class Model; - LDObject (Model* model = nullptr); - virtual ~LDObject(); void setDocument(Model* model); template<typename T> @@ -104,7 +107,6 @@ private: bool m_hasInvertNext = false; bool m_isHidden; - bool m_isSelected; Model* _model; qint32 m_id; LDColor m_color; @@ -119,20 +121,19 @@ */ class LDMatrixObject : public LDObject { - Vertex m_position; +public: + LDMatrixObject() = default; + LDMatrixObject(const Matrix& transformationMatrix, const Vertex& pos); -public: const Vertex& position() const; void setCoordinate (const Axis ax, double value); void setPosition (const Vertex& a); void setTransformationMatrix (const Matrix& value); const Matrix& transformationMatrix() const; - -protected: - LDMatrixObject (Model* model = nullptr); - LDMatrixObject (const Matrix& transformationMatrix, const Vertex& pos, Model* model = nullptr); + void serialize(class Serializer& serializer) override; private: + Vertex m_position; Matrix m_transformationMatrix; }; @@ -142,11 +143,12 @@ class LDError : public LDObject { public: - static constexpr LDObjectType SubclassType = LDObjectType::Error; + LDError() = default; + LDError(QString contents, QString reason); virtual LDObjectType type() const override { - return SubclassType; + return LDObjectType::Error; } virtual QString asText() const override; @@ -155,11 +157,7 @@ QString objectListText() const override; bool isColored() const override { return false; } QString typeName() const override { return "error"; } - -protected: - friend class Model; - LDError (Model* model); - LDError (QString contents, QString reason, Model* model = nullptr); + void serialize(class Serializer& serializer) override; private: QString m_contents; // The LDraw code that was being parsed @@ -184,24 +182,23 @@ _End }; +Q_DECLARE_METATYPE(BfcStatement) MAKE_ITERABLE_ENUM(BfcStatement) class LDBfc : public LDObject { - public: - static constexpr LDObjectType SubclassType = LDObjectType::Bfc; +public: + static const LDObjectType SubclassType = LDObjectType::Bfc; + + LDBfc(BfcStatement type = BfcStatement::CertifyCCW); virtual LDObjectType type() const override { - return SubclassType; + return LDObjectType::Bfc; } virtual QString asText() const override; -protected: - friend class Model; - LDBfc (Model* model); -public: bool isScemantic() const override { return statement() == BfcStatement::InvertNext; } QString objectListText() const override; BfcStatement statement() const; @@ -209,12 +206,10 @@ QString statementToString() const; bool isColored() const override { return false; } QString typeName() const override { return "bfc"; } + void serialize(class Serializer& serializer) override; static QString statementToString (BfcStatement statement); -protected: - LDBfc (const BfcStatement type, Model* model = nullptr); - private: BfcStatement m_statement; }; @@ -225,7 +220,10 @@ class LDSubfileReference : public LDMatrixObject { public: - static constexpr LDObjectType SubclassType = LDObjectType::SubfileReference; + static const LDObjectType SubclassType = LDObjectType::SubfileReference; + + LDSubfileReference() = default; + LDSubfileReference(QString referenceName, const Matrix& transformationMatrix, const Vertex& position); virtual LDObjectType type() const override { @@ -242,11 +240,7 @@ int triangleCount(DocumentManager *context) const override; bool hasMatrix() const override { return true; } QString typeName() const override { return "subfilereference"; } - -protected: - friend class Model; - LDSubfileReference(Model* model); - LDSubfileReference(QString referenceName, const Matrix& transformationMatrix, const Vertex& position, Model* model = nullptr); + void serialize(class Serializer& serializer) override; private: QString m_referenceName; @@ -258,7 +252,10 @@ class LDBezierCurve : public LDObject { public: - static constexpr LDObjectType SubclassType = LDObjectType::BezierCurve; + static const LDObjectType SubclassType = LDObjectType::BezierCurve; + + LDBezierCurve() = default; + LDBezierCurve(const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3); virtual LDObjectType type() const override { @@ -272,11 +269,6 @@ int numVertices() const override { return 4; } LDColor defaultColor() const override { return EdgeColor; } QString typeName() const override { return "beziercurve"; } - -protected: - friend class Model; - LDBezierCurve (Model* model); - LDBezierCurve (const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model = nullptr); }; enum @@ -293,8 +285,8 @@ { if (*property != value) { - QString before = asText(); + Serializer::Archive before = Serializer::store(this); *property = value; - emit codeChanged(before, asText()); + emit codeChanged(before, Serializer::store(this)); } }
--- a/src/linetypes/quadrilateral.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/quadrilateral.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -18,11 +18,7 @@ #include "quadrilateral.h" -LDQuadrilateral::LDQuadrilateral(Model *model) : - LDObject {model} {} - -LDQuadrilateral::LDQuadrilateral(const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4, Model* model) : - LDObject {model} +LDQuadrilateral::LDQuadrilateral(const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4) { setVertex(0, v1); setVertex(1, v2); @@ -57,5 +53,5 @@ LDObjectType LDQuadrilateral::type() const { - return SubclassType; + return LDObjectType::Quadrilateral; }
--- a/src/linetypes/quadrilateral.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/quadrilateral.h Mon Mar 05 22:40:34 2018 +0200 @@ -27,14 +27,12 @@ public: static constexpr LDObjectType SubclassType = LDObjectType::Quadrilateral; + LDQuadrilateral() = default; + LDQuadrilateral(const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4); + QString asText() const override; int numVertices() const override; int triangleCount(DocumentManager*) const override; LDObjectType type() const override; QString typeName() const override; - -protected: - friend class Model; - LDQuadrilateral(Model* model); - LDQuadrilateral(const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4, Model* model = nullptr); };
--- a/src/linetypes/triangle.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/triangle.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -18,11 +18,7 @@ #include "triangle.h" -LDTriangle::LDTriangle(Model *model) : - LDObject {model} {} - -LDTriangle::LDTriangle(const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model) : - LDObject {model} +LDTriangle::LDTriangle(const Vertex& v1, const Vertex& v2, const Vertex& v3) { setVertex(0, v1); setVertex(1, v2);
--- a/src/linetypes/triangle.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/linetypes/triangle.h Mon Mar 05 22:40:34 2018 +0200 @@ -27,18 +27,16 @@ public: static constexpr LDObjectType SubclassType = LDObjectType::Triangle; + LDTriangle() = default; + LDTriangle(Vertex const& v1, Vertex const& v2, Vertex const& v3); + virtual LDObjectType type() const override { - return SubclassType; + return LDObjectType::Triangle; } virtual QString asText() const override; int triangleCount(DocumentManager*) const override; int numVertices() const override { return 3; } QString typeName() const override { return "triangle"; } - -protected: - friend class Model; - LDTriangle (Model* model); - LDTriangle (Vertex const& v1, Vertex const& v2, Vertex const& v3, Model* model = nullptr); };
--- a/src/model.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/model.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -25,6 +25,7 @@ #include "linetypes/empty.h" #include "linetypes/quadrilateral.h" #include "linetypes/triangle.h" +#include "editHistory.h" Model::Model(DocumentManager* manager) : QAbstractListModel {manager}, @@ -36,12 +37,14 @@ delete _objects[i]; } -/* - * Takes an existing object and migrates it to the end of this model. The object is removed from its old model in the process. - */ -void Model::addObject(LDObject *object) +void Model::installObject(int row, LDObject* object) { - insertObject(size(), object); + connect(object, SIGNAL(codeChanged(LDObjectState, LDObjectState)), this, SLOT(recountTriangles())); + beginInsertRows({}, row, row); + _objects.insert(row, object); + recountTriangles(); + emit objectAdded(index(row)); + endInsertRows(); } /* @@ -61,22 +64,19 @@ } /* - * Takes an existing object and migrates it to this model, at the specified position. + * Takes an existing object and copies it to this model, at the specified position. */ -void Model::insertObject(int position, LDObject* object) +void Model::insertCopy(int position, LDObject* object) { - connect(object, SIGNAL(codeChanged(QString,QString)), this, SLOT(recountTriangles())); + installObject(position, Serializer::clone(object)); +} - if (object->model() and object->model() != this) - object->model()->withdraw(object); +void Model::insertFromArchive(int row, Serializer::Archive& archive) +{ + LDObject* object = Serializer::restore(archive); - // TODO: check that the object isn't in the vector once there's a cheap way to do so! - beginInsertRows({}, position, position); - _objects.insert(position, object); - _needsTriangleRecount = true; - object->setDocument(this); - emit objectAdded(object); - endInsertRows(); + if (object) + installObject(row, object); } /* @@ -84,13 +84,14 @@ */ bool Model::swapObjects(LDObject* one, LDObject* other) { - int a = _objects.indexOf(one); - int b = _objects.indexOf(other); + QModelIndex a = indexOf(one); + QModelIndex b = indexOf(other); - if (a != b and a != -1 and b != -1) + if (a.isValid() and b.isValid() and a != b) { - _objects[b] = one; - _objects[a] = other; + _objects[b.row()] = one; + _objects[a.row()] = other; + emit objectsSwapped(a, b); return true; } else @@ -102,17 +103,19 @@ /* * Assigns a new object to the specified position in the model. The object that already is in the position is deleted in the process. */ -bool Model::setObjectAt(int idx, LDObject* obj) +bool Model::setObjectAt(int row, Serializer::Archive& archive) { - if (idx < 0 or idx >= countof(_objects)) + LDObject* object = Serializer::restore(archive); + + if (object and row >= 0 and row < countof(_objects)) { - return false; + removeAt(row); + insertCopy(row, object); + return true; } else { - removeAt(idx); - insertObject(idx, obj); - return true; + return false; } } @@ -143,7 +146,12 @@ */ void Model::removeAt(int position) { - LDObject* object = withdrawAt(position); + beginRemoveRows({}, position, position); + LDObject* object = _objects[position]; + emit aboutToRemoveObject(this->index(position)); + _objects.removeAt(position); + _needsTriangleRecount = true; + endRemoveRows(); delete object; } @@ -161,10 +169,10 @@ if (index.isValid()) { - for (int i = countof(model.objects()) - 1; i >= 1; i -= 1) - insertObject(index.row() + i, model.objects()[i]); + removeAt(index.row()); - setObjectAt(index.row(), model.objects()[0]); + for (signed int i = countof(model.objects()) - 1; i >= 0; i -= 1) + insertCopy(index.row() + i, model.objects()[i]); } } @@ -208,7 +216,7 @@ // Inform the contents of their new owner for (LDObject* object : objectsCopy) { - insertObject(position, object); + insertCopy(position, object); position += 1; } @@ -244,34 +252,6 @@ } /* - * Drops the object from the model. The object becomes a free object as a result (thus violating the invariant that every object - * has a model!). The caller must immediately add the withdrawn object to another model. - * - * This private method is only used to implement public API. - */ -void Model::withdraw(LDObject* object) -{ - QModelIndex index = this->indexOf(object); - - if (index.isValid()) - withdrawAt(index.row()); -} - -/* - * Drops an object from the model at the provided position. The caller must immediately put the result value object into a new model. - */ -LDObject* Model::withdrawAt(int position) -{ - beginRemoveRows({}, position, position); - LDObject* object = _objects[position]; - emit aboutToRemoveObject(object); - _objects.removeAt(position); - _needsTriangleRecount = true; - endRemoveRows(); - return object; -} - -/* * Returns whether or not this model is empty. */ bool Model::isEmpty() const
--- a/src/model.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/model.h Mon Mar 05 22:40:34 2018 +0200 @@ -19,6 +19,7 @@ #pragma once #include <QAbstractListModel> #include "main.h" +#include "serializer.h" #include "linetypes/modelobject.h" class IndexGenerator @@ -86,14 +87,12 @@ Model(const Model& other) = delete; ~Model(); - void addObject(LDObject* object); - virtual void insertObject(int position, LDObject* object); - virtual bool swapObjects(LDObject* one, LDObject* other); - virtual bool setObjectAt(int idx, LDObject* obj); + void insertCopy(int position, LDObject* object); + void insertFromArchive(int row, Serializer::Archive& archive); + bool swapObjects(LDObject* one, LDObject* other); + bool setObjectAt(int idx, Serializer::Archive& archive); template<typename T, typename... Args> T* emplace(Args&& ...args); template<typename T, typename... Args> T* emplaceAt(int position, Args&& ...args); - template<typename T, typename... Args> T* emplaceReplacement(LDObject* object, Args&& ...args); - template<typename T, typename... Args> T* emplaceReplacementAt(int position, Args&& ...args); void removeAt(int position); void removeAt(const QModelIndex& index); void remove(LDObject* object); @@ -119,22 +118,23 @@ int rowCount(const QModelIndex& parent) const override; QVariant data(const QModelIndex& index, int role) const override; - // bool removeRows(int row, int count, const QModelIndex& ) override; signals: - void objectAdded(LDObject* object); - void aboutToRemoveObject(LDObject* object); + void objectAdded(const QModelIndex& object); + void aboutToRemoveObject(const QModelIndex& index); void objectModified(LDObject* object); + void objectsSwapped(const QModelIndex& index_1, const QModelIndex& index_2); protected: template<typename T, typename... Args> T* constructObject(Args&& ...args); - void withdraw(LDObject* object); - virtual LDObject* withdrawAt(int position); QVector<LDObject*> _objects; class DocumentManager* _manager; mutable int _triangleCount = 0; mutable bool _needsTriangleRecount; + +private: + void installObject(int row, LDObject* object); }; int countof(Model& model); @@ -146,7 +146,7 @@ * * For instance, the LDLine contains a constructor as such: * - * LDLine(Vertex v1, Vertex v2, Model* model); + * LDLine(Vertex v1, Vertex v2); * * This constructor can be invoked as such: * @@ -155,9 +155,7 @@ template<typename T, typename... Args> T* Model::emplace(Args&& ...args) { - T* object = constructObject<T>(args...); - addObject(object); - return object; + return emplaceAt<T>(size(), args...); } /* @@ -168,51 +166,18 @@ T* Model::emplaceAt(int position, Args&& ...args) { T* object = constructObject<T>(args...); - insertObject(position, object); + installObject(position, object); return object; } /* - * Like emplace<>() but instead of inserting the constructed object, the new object replaces the object given in the first parameter. - * If the old object cannot be replaced, the new object will not be constructed at all. - */ -template<typename T, typename... Args> -T* Model::emplaceReplacement(LDObject* object, Args&& ...args) -{ - QModelIndex position = this->indexOf(object); - - if (position.isValid()) - { - T* replacement = constructObject<T>(args...); - setObjectAt(position.row(), replacement); - return replacement; - } - else - { - return nullptr; - } -} - -/* - * Like emplaceAt<>() but instead of inserting the constructed object, it replaces the document at the given spot instead. - * The replaced object is deleted in the process. - */ -template<typename T, typename... Args> -T* Model::emplaceReplacementAt(int position, Args&& ...args) -{ - T* replacement = constructObject<T>(args...); - setObjectAt(position, replacement); - return replacement; -} - -/* * Constructs an LDObject such that it gets this model as its model pointer. */ template<typename T, typename... Args> T* Model::constructObject(Args&& ...args) { static_assert (std::is_base_of<LDObject, T>::value, "Can only use this function with LDObject-derivatives"); - T* object = new T {args..., this}; + T* object = new T {args...}; // Set default color. Relying on virtual functions, this cannot be done in the c-tor. // TODO: store -1 as the default color
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/serializer.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -0,0 +1,59 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2017 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 "serializer.h" +#include "linetypes/modelobject.h" + +Serializer::Serializer(Archive& archive, Action action) : + archive {archive}, + action {action} {} + +Serializer::Archive Serializer::store(LDObject* object) +{ + Archive result; + result.append(static_cast<int>(object->type())); + Serializer serializer {result, Store}; + object->serialize(serializer); + return result; +} + +LDObject* Serializer::restore(Archive& archive) +{ + if (archive.size() > 0) + { + Serializer serializer {archive, Restore}; + int type; + serializer << type; + LDObject* object = LDObject::newFromType(static_cast<LDObjectType>(type)); + + if (object) + object->serialize(serializer); + + return object; + } + else + { + return nullptr; + } +} + +LDObject* Serializer::clone(LDObject* object) +{ + Archive archive = store(object); + return restore(archive); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/serializer.h Mon Mar 05 22:40:34 2018 +0200 @@ -0,0 +1,74 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2018 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/>. + */ + +#pragma once +#include "main.h" + +using LDObjectState = QVector<QVariant>; + +class Serializer +{ +public: + using Archive = LDObjectState; + + enum Action + { + Store, + Restore, + }; + + Serializer(Archive& archive, Action action); + + template<typename T> + Serializer& operator<<(T& value); + + static Archive store(LDObject* object); + static LDObject* restore(Archive& archive) __attribute__((warn_unused_result)); + static LDObject* clone(LDObject* object) __attribute__((warn_unused_result)); + +private: + Archive& archive; + int cursor = 0; + Action action; +}; + +template<typename T> +Serializer& Serializer::operator<<(T& value) +{ + switch (action) + { + case Store: + this->archive.append(QVariant::fromValue(value)); + break; + + case Restore: + if (this->cursor < this->archive.size()) + { + value = this->archive[this->cursor].value<T>(); + this->cursor += 1; + } + else + { + fprintf(stderr, "warning: archive ran out while restoring\n"); + value = T {}; + } + break; + } + + return *this; +}
--- a/src/toolsets/algorithmtoolset.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/toolsets/algorithmtoolset.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -69,8 +69,10 @@ // │ │ --→ │ ╱ ╱ │ // │ │ --→ │╱ ╱ │ // 1───2 1 1───2 - LDTriangle* triangle1 = currentDocument()->emplaceReplacementAt<LDTriangle>(index.row(), v0, v1, v3); - LDTriangle* triangle2 = currentDocument()->emplaceAt<LDTriangle>(index.row() + 1, v1, v2, v3); + int row = index.row(); + currentDocument()->removeAt(index); + LDTriangle* triangle1 = currentDocument()->emplaceAt<LDTriangle>(row, v0, v1, v3); + LDTriangle* triangle2 = currentDocument()->emplaceAt<LDTriangle>(row + 1, v1, v2, v3); // The triangles also inherit the quad's color triangle1->setColor(color);
--- a/src/toolsets/basictoolset.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/toolsets/basictoolset.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -109,7 +109,7 @@ // Merge in the inlined objects for (LDObject* inlinedObject : inlined.objects()) { - currentDocument()->insertObject(row, inlinedObject); + currentDocument()->insertCopy(row, inlinedObject); mainWindow()->select(currentDocument()->index(row)); row += 1; }
--- a/src/toolsets/extprogramtoolset.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/toolsets/extprogramtoolset.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -327,7 +327,7 @@ for (LDObject* object : model.objects()) { if (object->isScemantic()) - currentDocument()->addObject(object); + currentDocument()->insertCopy(currentDocument()->size(), object); } m_window->doFullRefresh();
--- a/src/toolsets/filetoolset.cpp Sat Mar 03 17:59:56 2018 +0200 +++ b/src/toolsets/filetoolset.cpp Mon Mar 05 22:40:34 2018 +0200 @@ -123,7 +123,7 @@ for (LDObject* object : model.objects()) { - currentDocument()->insertObject (position, object); + currentDocument()->insertCopy (position, object); mainWindow()->select(currentDocument()->index(position)); position += 1; }
--- a/src/types/matrix.h Sat Mar 03 17:59:56 2018 +0200 +++ b/src/types/matrix.h Mon Mar 05 22:40:34 2018 +0200 @@ -59,6 +59,8 @@ double m_values[9]; }; +Q_DECLARE_METATYPE(Matrix) + /* * A structure that provides a view into a row in a matrix. * This is returned by operator[] so that the matrix can be accessed by A[i][j]