// Copyright (C) 2016 Jochen Becher // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "modelcontroller.h" #include "mcontainer.h" #include "mselection.h" #include "mreferences.h" #include "mclonevisitor.h" #include "mflatassignmentvisitor.h" #include "qmt/controller/undocommand.h" #include "qmt/controller/undocontroller.h" #include "qmt/model/mobject.h" #include "qmt/model/mpackage.h" #include "qmt/model/mrelation.h" #include "../../modelinglibtr.h" #include namespace qmt { class ModelController::Clone { public: ModelController::ElementType m_elementType = ModelController::TypeUnknown; Uid m_elementKey; Uid m_ownerKey; int m_indexOfElement = -1; MElement *m_clonedElement = nullptr; }; class ModelController::UpdateObjectCommand : public UndoCommand { public: UpdateObjectCommand(ModelController *modelController, MObject *object) : UndoCommand(Tr::tr("Change Object")), m_modelController(modelController) { MCloneVisitor visitor; object->accept(&visitor); m_object = dynamic_cast(visitor.cloned()); QMT_CHECK(m_object); } ~UpdateObjectCommand() override { delete m_object; } bool mergeWith(const UndoCommand *other) override { auto updateCommand = dynamic_cast(other); if (!updateCommand) return false; if (m_object->uid() != updateCommand->m_object->uid()) return false; // the last update is a complete update of all changes... return true; } void redo() override { if (canRedo()) { assign(); UndoCommand::redo(); } } void undo() override { assign(); UndoCommand::undo(); } private: void assign() { MObject *object = m_modelController->findObject(m_object->uid()); QMT_ASSERT(object, return); int row = 0; MObject *parent = object->owner(); if (!parent) { QMT_CHECK(object == m_modelController->m_rootPackage); } else { row = object->owner()->children().indexOf(object); } emit m_modelController->beginUpdateObject(row, parent); MCloneVisitor cloneVisitor; object->accept(&cloneVisitor); auto newObject = dynamic_cast(cloneVisitor.cloned()); QMT_CHECK(newObject); MFlatAssignmentVisitor assignVisitor(object); m_object->accept(&assignVisitor); delete m_object; m_object = newObject; emit m_modelController->endUpdateObject(row, parent); emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); } ModelController *m_modelController = nullptr; MObject *m_object = nullptr; }; class ModelController::UpdateRelationCommand : public UndoCommand { public: UpdateRelationCommand(ModelController *modelController, MRelation *relation) : UndoCommand(Tr::tr("Change Relation")), m_modelController(modelController) { MCloneVisitor visitor; relation->accept(&visitor); m_relation = dynamic_cast(visitor.cloned()); QMT_CHECK(m_relation); } ~UpdateRelationCommand() override { delete m_relation; } bool mergeWith(const UndoCommand *other) override { auto updateCommand = dynamic_cast(other); if (!updateCommand) return false; if (m_relation->uid() != updateCommand->m_relation->uid()) return false; // the last update is a complete update of all changes... return true; } void redo() override { if (canRedo()) { assign(); UndoCommand::redo(); } } void undo() override { assign(); UndoCommand::undo(); } private: void assign() { MRelation *relation = m_modelController->findRelation(m_relation->uid()); QMT_ASSERT(relation, return); MObject *owner = relation->owner(); QMT_ASSERT(owner, return); int row = owner->relations().indexOf(relation); emit m_modelController->beginUpdateRelation(row, owner); MCloneVisitor cloneVisitor; relation->accept(&cloneVisitor); auto newRelation = dynamic_cast(cloneVisitor.cloned()); QMT_CHECK(newRelation); MFlatAssignmentVisitor assignVisitor(relation); m_relation->accept(&assignVisitor); delete m_relation; m_relation = newRelation; emit m_modelController->endUpdateRelation(row, owner); emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); } ModelController *m_modelController = nullptr; MRelation *m_relation = nullptr; }; class ModelController::AddElementsCommand : public UndoCommand { public: AddElementsCommand(ModelController *modelController, const QString &commandLabel) : UndoCommand(commandLabel), m_modelController(modelController) { } ~AddElementsCommand() override { for (const Clone &clone : std::as_const(m_clonedElements)) delete clone.m_clonedElement; } void add(ElementType elementsType, const Uid &objectKey, const Uid &ownerKey) { Clone clone; clone.m_elementType = elementsType; clone.m_elementKey = objectKey; clone.m_ownerKey = ownerKey; clone.m_indexOfElement = -1; m_clonedElements.append(clone); } void redo() override { if (canRedo()) { bool inserted = false; for (int i = m_clonedElements.count() - 1; i >= 0; --i) { Clone &clone = m_clonedElements[i]; QMT_ASSERT(clone.m_clonedElement, return); QMT_CHECK(clone.m_clonedElement->uid() == clone.m_elementKey); MObject *owner = m_modelController->findObject(clone.m_ownerKey); QMT_ASSERT(owner, return); QMT_CHECK(clone.m_indexOfElement >= 0); switch (clone.m_elementType) { case TypeObject: { emit m_modelController->beginInsertObject(clone.m_indexOfElement, owner); auto object = dynamic_cast(clone.m_clonedElement); QMT_CHECK(object); m_modelController->mapObject(object); owner->insertChild(clone.m_indexOfElement, object); clone.m_clonedElement = nullptr; emit m_modelController->endInsertObject(clone.m_indexOfElement, owner); inserted = true; break; } case TypeRelation: { emit m_modelController->beginInsertRelation(clone.m_indexOfElement, owner); auto relation = dynamic_cast(clone.m_clonedElement); QMT_CHECK(relation); m_modelController->mapRelation(relation); owner->insertRelation(clone.m_indexOfElement, relation); clone.m_clonedElement = nullptr; emit m_modelController->endInsertRelation(clone.m_indexOfElement, owner); inserted = true; break; } default: QMT_CHECK(false); break; } } if (inserted) emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); UndoCommand::redo(); } } void undo() override { bool removed = false; for (int i = 0; i < m_clonedElements.count(); ++i) { Clone &clone = m_clonedElements[i]; QMT_CHECK(!clone.m_clonedElement); MObject *owner = m_modelController->findObject(clone.m_ownerKey); QMT_ASSERT(owner, return); switch (clone.m_elementType) { case TypeObject: { MObject *object = m_modelController->findObject(clone.m_elementKey); QMT_ASSERT(object, return); clone.m_indexOfElement = owner->children().indexOf(object); QMT_CHECK(clone.m_indexOfElement >= 0); emit m_modelController->beginRemoveObject(clone.m_indexOfElement, owner); MCloneDeepVisitor visitor; object->accept(&visitor); clone.m_clonedElement = visitor.cloned(); m_modelController->unmapObject(object); owner->removeChild(object); emit m_modelController->endRemoveObject(clone.m_indexOfElement, owner); removed = true; break; } case TypeRelation: { MRelation *relation = m_modelController->findRelation(clone.m_elementKey); QMT_ASSERT(relation, return); clone.m_indexOfElement = owner->relations().indexOf(relation); QMT_CHECK(clone.m_indexOfElement >= 0); emit m_modelController->beginRemoveRelation(clone.m_indexOfElement, owner); MCloneDeepVisitor visitor; relation->accept(&visitor); clone.m_clonedElement = visitor.cloned(); m_modelController->unmapRelation(relation); owner->removeRelation(relation); emit m_modelController->endRemoveRelation(clone.m_indexOfElement, owner); removed = true; break; } default: QMT_CHECK(false); break; } } if (removed) emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); UndoCommand::undo(); } private: ModelController *m_modelController = nullptr; QList m_clonedElements; }; class ModelController::RemoveElementsCommand : public UndoCommand { public: RemoveElementsCommand(ModelController *modelController, const QString &commandLabel) : UndoCommand(commandLabel), m_modelController(modelController) { } ~RemoveElementsCommand() override { for (const Clone &clone : std::as_const(m_clonedElements)) delete clone.m_clonedElement; } void add(MElement *element, MObject *owner) { ModelController::Clone clone; clone.m_elementKey = element->uid(); clone.m_ownerKey = owner->uid(); if (auto object = dynamic_cast(element)) { clone.m_elementType = TypeObject; clone.m_indexOfElement = owner->children().indexOf(object); QMT_CHECK(clone.m_indexOfElement >= 0); } else if (auto relation = dynamic_cast(element)) { clone.m_elementType = TypeRelation; clone.m_indexOfElement = owner->relations().indexOf(relation); QMT_CHECK(clone.m_indexOfElement >= 0); } else { QMT_CHECK(false); } MCloneDeepVisitor visitor; element->accept(&visitor); clone.m_clonedElement = visitor.cloned(); QMT_ASSERT(clone.m_clonedElement, return); m_clonedElements.append(clone); } void redo() override { if (canRedo()) { bool removed = false; for (int i = 0; i < m_clonedElements.count(); ++i) { Clone &clone = m_clonedElements[i]; QMT_CHECK(!clone.m_clonedElement); MObject *owner = m_modelController->findObject(clone.m_ownerKey); QMT_ASSERT(owner, return); switch (clone.m_elementType) { case TypeObject: { MObject *object = m_modelController->findObject(clone.m_elementKey); QMT_ASSERT(object, return); clone.m_indexOfElement = owner->children().indexOf(object); QMT_CHECK(clone.m_indexOfElement >= 0); emit m_modelController->beginRemoveObject(clone.m_indexOfElement, owner); MCloneDeepVisitor visitor; object->accept(&visitor); clone.m_clonedElement = visitor.cloned(); m_modelController->unmapObject(object); owner->removeChild(object); emit m_modelController->endRemoveObject(clone.m_indexOfElement, owner); removed = true; break; } case TypeRelation: { MRelation *relation = m_modelController->findRelation(clone.m_elementKey); QMT_ASSERT(relation, return); clone.m_indexOfElement = owner->relations().indexOf(relation); QMT_CHECK(clone.m_indexOfElement >= 0); emit m_modelController->beginRemoveRelation(clone.m_indexOfElement, owner); MCloneDeepVisitor visitor; relation->accept(&visitor); clone.m_clonedElement = visitor.cloned(); m_modelController->unmapRelation(relation); owner->removeRelation(relation); emit m_modelController->endRemoveRelation(clone.m_indexOfElement, owner); removed = true; break; } default: QMT_CHECK(false); break; } } if (removed) emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); UndoCommand::redo(); } } void undo() override { bool inserted = false; for (int i = m_clonedElements.count() - 1; i >= 0; --i) { Clone &clone = m_clonedElements[i]; QMT_ASSERT(clone.m_clonedElement, return); MObject *owner = m_modelController->findObject(clone.m_ownerKey); QMT_ASSERT(owner, return); QMT_CHECK(clone.m_indexOfElement >= 0); switch (clone.m_elementType) { case TypeObject: { emit m_modelController->beginInsertObject(clone.m_indexOfElement, owner); auto object = dynamic_cast(clone.m_clonedElement); QMT_CHECK(object); m_modelController->mapObject(object); owner->insertChild(clone.m_indexOfElement, object); clone.m_clonedElement = nullptr; emit m_modelController->endInsertObject(clone.m_indexOfElement, owner); inserted = true; break; } case TypeRelation: { emit m_modelController->beginInsertRelation(clone.m_indexOfElement, owner); auto relation = dynamic_cast(clone.m_clonedElement); QMT_CHECK(relation); m_modelController->mapRelation(relation); owner->insertRelation(clone.m_indexOfElement, relation); clone.m_clonedElement = nullptr; emit m_modelController->endInsertRelation(clone.m_indexOfElement, owner); inserted = true; break; } default: QMT_CHECK(false); break; } } if (inserted) emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); UndoCommand::undo(); } private: ModelController *m_modelController = nullptr; QList m_clonedElements; }; class ModelController::MoveObjectCommand : public UndoCommand { public: MoveObjectCommand(ModelController *modelController, MObject *object) : UndoCommand(Tr::tr("Move Object")), m_modelController(modelController), m_objectKey(object->uid()), m_ownerKey(object->owner()->uid()), m_indexOfElement(object->owner()->children().indexOf(object)) { } ~MoveObjectCommand() override { } void redo() override { if (canRedo()) { swap(); UndoCommand::redo(); } } void undo() override { swap(); UndoCommand::undo(); } private: void swap() { MObject *object = m_modelController->findObject(m_objectKey); QMT_ASSERT(object, return); MObject *formerOwner = object->owner(); int formerRow = formerOwner->children().indexOf(object); MObject *newOwner = m_modelController->findObject(m_ownerKey); QMT_ASSERT(newOwner, return); emit m_modelController->beginMoveObject(formerRow, formerOwner); formerOwner->decontrolChild(object); newOwner->insertChild(m_indexOfElement, object); int newRow = m_indexOfElement; m_ownerKey = formerOwner->uid(); m_indexOfElement = formerRow; emit m_modelController->endMoveObject(newRow, newOwner); emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); } ModelController *m_modelController = nullptr; Uid m_objectKey; Uid m_ownerKey; int m_indexOfElement = -1; }; class ModelController::MoveRelationCommand : public UndoCommand { public: MoveRelationCommand(ModelController *modelController, MRelation *relation) : UndoCommand(Tr::tr("Move Relation")), m_modelController(modelController), m_relationKey(relation->uid()), m_ownerKey(relation->owner()->uid()), m_indexOfElement(relation->owner()->relations().indexOf(relation)) { } ~MoveRelationCommand() override { } void redo() override { if (canRedo()) { swap(); UndoCommand::redo(); } } void undo() override { swap(); UndoCommand::undo(); } private: void swap() { MRelation *relation = m_modelController->findRelation(m_relationKey); QMT_ASSERT(relation, return); MObject *formerOwner = relation->owner(); int formerRow = formerOwner->relations().indexOf(relation); MObject *newOwner = m_modelController->findObject(m_ownerKey); QMT_ASSERT(newOwner, return); emit m_modelController->beginMoveRelation(formerRow, formerOwner); formerOwner->decontrolRelation(relation); newOwner->insertRelation(m_indexOfElement, relation); int newRow = m_indexOfElement; m_ownerKey = formerOwner->uid(); m_indexOfElement = formerRow; emit m_modelController->endMoveRelation(newRow, newOwner); emit m_modelController->modified(); m_modelController->verifyModelIntegrity(); } ModelController *m_modelController = nullptr; Uid m_relationKey; Uid m_ownerKey; int m_indexOfElement = -1; }; ModelController::ModelController(QObject *parent) : QObject(parent) { } ModelController::~ModelController() { delete m_rootPackage; } void ModelController::setRootPackage(MPackage *rootPackage) { startResetModel(); unmapObject(m_rootPackage); m_rootPackage = rootPackage; mapObject(m_rootPackage); finishResetModel(false); } void ModelController::setUndoController(UndoController *undoController) { m_undoController = undoController; } Uid ModelController::ownerKey(const Uid &key) const { MElement *element = findElement(key); if (!element) return Uid::invalidUid(); return ownerKey(element); } Uid ModelController::ownerKey(const MElement *element) const { QMT_ASSERT(element, return Uid()); MObject *owner = element->owner(); if (!owner) return Uid::invalidUid(); return owner->uid(); } MElement *ModelController::findElement(const Uid &key) const { if (MObject *object = findObject(key)) return object; else if (MRelation *relation = findRelation(key)) return relation; return nullptr; } void ModelController::startResetModel() { QMT_CHECK(!m_isResettingModel); m_isResettingModel = true; emit beginResetModel(); QMT_CHECK(m_isResettingModel); } void ModelController::finishResetModel(bool modified) { QMT_CHECK(m_isResettingModel); emit endResetModel(); if (modified) emit this->modified(); QMT_CHECK(m_isResettingModel); m_isResettingModel = false; } MObject *ModelController::object(int row, const MObject *owner) const { if (!owner) { QMT_CHECK(row == 0); return m_rootPackage; } QMT_ASSERT(row >= 0 && row < owner->children().size(), return nullptr); return owner->children().at(row); } MObject *ModelController::findObject(const Uid &key) const { return m_objectsMap.value(key); } void ModelController::addObject(MPackage *parentPackage, MObject *object) { QMT_ASSERT(parentPackage, return); QMT_ASSERT(object, return); int row = parentPackage->children().size(); if (!m_isResettingModel) emit beginInsertObject(row, parentPackage); mapObject(object); if (m_undoController) { auto undoCommand = new AddElementsCommand(this, Tr::tr("Add Object")); m_undoController->push(undoCommand); undoCommand->add(TypeObject, object->uid(), parentPackage->uid()); } parentPackage->addChild(object); if (!m_isResettingModel) { emit endInsertObject(row, parentPackage); emit modified(); } verifyModelIntegrity(); } void ModelController::removeObject(MObject *object) { QMT_ASSERT(object, return); if (m_undoController) m_undoController->beginMergeSequence(Tr::tr("Delete Object")); removeRelatedRelations(object); // remove object QMT_ASSERT(object->owner(), return); int row = object->owner()->children().indexOf(object); MObject *owner = object->owner(); if (!m_isResettingModel) emit beginRemoveObject(row, owner); if (m_undoController) { auto undoCommand = new RemoveElementsCommand(this, Tr::tr("Delete Object")); m_undoController->push(undoCommand); undoCommand->add(object, object->owner()); } unmapObject(object); owner->removeChild(object); if (!m_isResettingModel) { emit endRemoveObject(row, owner); emit modified(); } if (m_undoController) m_undoController->endMergeSequence(); verifyModelIntegrity(); } void ModelController::startUpdateObject(MObject *object) { QMT_ASSERT(object, return); int row = 0; MObject *parent = object->owner(); if (!parent) { QMT_CHECK(object == m_rootPackage); } else { row = parent->children().indexOf(object); } if (auto package = dynamic_cast(object)) m_oldPackageName = package->name(); if (!m_isResettingModel) emit beginUpdateObject(row, parent); if (m_undoController) m_undoController->push(new UpdateObjectCommand(this, object)); } void ModelController::finishUpdateObject(MObject *object, bool cancelled) { QMT_ASSERT(object, return); int row = 0; MObject *parent = object->owner(); if (!parent) { QMT_CHECK(object == m_rootPackage); } else { row = parent->children().indexOf(object); } if (!m_isResettingModel) { emit endUpdateObject(row, parent); if (!cancelled) { const QList relations = findRelationsOfObject(object); for (MRelation *relation : relations) emit relationEndChanged(relation, object); if (auto package = dynamic_cast(object)) { if (m_oldPackageName != package->name()) emit packageNameChanged(package, m_oldPackageName); } emit modified(); } } verifyModelIntegrity(); } void ModelController::moveObject(MPackage *newOwner, MObject *object) { QMT_ASSERT(newOwner, return); QMT_ASSERT(object, return); QMT_ASSERT(object != m_rootPackage, return); // verify that newOwner is not a child of object MObject *newOwnerObject = newOwner; while (newOwnerObject && newOwnerObject != object) newOwnerObject = newOwnerObject->owner(); if (newOwnerObject == object) return; if (newOwner != object->owner()) { int formerRow = 0; MObject *formerOwner = object->owner(); QMT_ASSERT(formerOwner, return); formerRow = formerOwner->children().indexOf(object); if (!m_isResettingModel) emit beginMoveObject(formerRow, formerOwner); if (m_undoController) { auto undoCommand = new MoveObjectCommand(this, object); m_undoController->push(undoCommand); } formerOwner->decontrolChild(object); newOwner->addChild(object); int row = newOwner->children().indexOf(object); if (!m_isResettingModel) { emit endMoveObject(row, newOwner); emit modified(); } } verifyModelIntegrity(); } MRelation *ModelController::findRelation(const Uid &key) const { return m_relationsMap.value(key); } void ModelController::addRelation(MObject *owner, MRelation *relation) { QMT_ASSERT(owner, return); QMT_ASSERT(relation, return); QMT_ASSERT(findObject(relation->endAUid()), return); QMT_ASSERT(findObject(relation->endBUid()), return); int row = owner->relations().size(); if (!m_isResettingModel) emit beginInsertRelation(row, owner); mapRelation(relation); if (m_undoController) { auto undoCommand = new AddElementsCommand(this, Tr::tr("Add Relation")); m_undoController->push(undoCommand); undoCommand->add(TypeRelation, relation->uid(), owner->uid()); } owner->addRelation(relation); if (!m_isResettingModel) { emit endInsertRelation(row, owner); emit modified(); } verifyModelIntegrity(); } void ModelController::removeRelation(MRelation *relation) { QMT_ASSERT(relation, return); MObject *owner = relation->owner(); QMT_ASSERT(owner, return); int row = owner->relations().indexOf(relation); if (!m_isResettingModel) emit beginRemoveRelation(row, owner); if (m_undoController) { auto undoCommand = new RemoveElementsCommand(this, Tr::tr("Delete Relation")); m_undoController->push(undoCommand); undoCommand->add(relation, owner); } unmapRelation(relation); owner->removeRelation(relation); if (!m_isResettingModel) { emit endRemoveRelation(row, owner); emit modified(); } verifyModelIntegrity(); } void ModelController::startUpdateRelation(MRelation *relation) { QMT_ASSERT(relation, return); MObject *owner = relation->owner(); QMT_ASSERT(owner, return); if (!m_isResettingModel) emit beginUpdateRelation(owner->relations().indexOf(relation), owner); if (m_undoController) m_undoController->push(new UpdateRelationCommand(this, relation)); } void ModelController::finishUpdateRelation(MRelation *relation, bool cancelled) { QMT_ASSERT(relation, return); QMT_ASSERT(findObject(relation->endAUid()), return); QMT_ASSERT(findObject(relation->endBUid()), return); MObject *owner = relation->owner(); QMT_ASSERT(owner, return); if (!m_isResettingModel) { emit endUpdateRelation(owner->relations().indexOf(relation), owner); if (!cancelled) emit modified(); } verifyModelIntegrity(); } void ModelController::moveRelation(MObject *newOwner, MRelation *relation) { QMT_ASSERT(newOwner, return); QMT_ASSERT(relation, return); if (newOwner != relation->owner()) { int formerRow = 0; MObject *formerOwner = relation->owner(); QMT_ASSERT(formerOwner, return); formerRow = formerOwner->relations().indexOf(relation); if (!m_isResettingModel) emit beginMoveRelation(formerRow, formerOwner); if (m_undoController) { auto undoCommand = new MoveRelationCommand(this, relation); m_undoController->push(undoCommand); } formerOwner->decontrolRelation(relation); newOwner->addRelation(relation); int row = newOwner->relations().indexOf(relation); if (!m_isResettingModel) { emit endMoveRelation(row, newOwner); emit modified(); } } verifyModelIntegrity(); } QList ModelController::findRelationsOfObject(const MObject *object) const { QMT_ASSERT(object, return QList()); return m_objectRelationsMap.values(object->uid()); } MContainer ModelController::cutElements(const MSelection &modelSelection) { // PERFORM avoid duplicate call of simplify(modelSelection) MContainer copiedElements = copyElements(modelSelection); deleteElements(modelSelection, Tr::tr("Cut")); return copiedElements; } MContainer ModelController::copyElements(const MSelection &modelSelection) { MReferences simplifiedSelection = simplify(modelSelection); MContainer copiedElements; const QList elements = simplifiedSelection.elements(); for (MElement *element : elements) { MCloneDeepVisitor visitor; element->accept(&visitor); MElement *clonedElement = visitor.cloned(); copiedElements.submit(clonedElement); } return copiedElements; } void ModelController::pasteElements(MObject *owner, const MReferences &modelContainer, PasteOption option) { // clone all elements and renew their keys QHash renewedKeys; QList clonedElements; const QList elements = modelContainer.elements(); for (MElement *element : elements) { if (option == PasteAlwaysWithNewKeys || option == PasteAlwaysAndKeepKeys || !findElement(element->uid())) { MCloneDeepVisitor visitor; element->accept(&visitor); MElement *clonedElement = visitor.cloned(); if (option == PasteAlwaysWithNewKeys || (option == PasteAlwaysAndKeepKeys && findElement(element->uid()))) renewElementKey(clonedElement, &renewedKeys); clonedElements.append(clonedElement); } } // fix all keys referencing between pasting elements for (MElement *clonedElement : std::as_const(clonedElements)) updateRelationKeys(clonedElement, renewedKeys); if (m_undoController) m_undoController->beginMergeSequence(Tr::tr("Paste")); // insert all elements bool added = false; for (MElement *clonedElement : std::as_const(clonedElements)) { if (auto object = dynamic_cast(clonedElement)) { MObject *objectOwner = owner; if (!dynamic_cast(owner)) objectOwner = owner->owner(); QMT_CHECK(dynamic_cast(objectOwner)); int row = objectOwner->children().size(); emit beginInsertObject(row, objectOwner); mapObject(object); if (m_undoController) { auto undoCommand = new AddElementsCommand(this, Tr::tr("Paste")); m_undoController->push(undoCommand); undoCommand->add(TypeObject, object->uid(), objectOwner->uid()); } objectOwner->insertChild(row, object); emit endInsertObject(row, objectOwner); added = true; } else if (auto relation = dynamic_cast(clonedElement)) { int row = owner->relations().size(); emit beginInsertRelation(row, owner); mapRelation(relation); if (m_undoController) { auto undoCommand = new AddElementsCommand(this, Tr::tr("Paste")); m_undoController->push(undoCommand); undoCommand->add(TypeRelation, relation->uid(), owner->uid()); } owner->addRelation(relation); emit endInsertRelation(row, owner); added = true; } } if (added) emit modified(); verifyModelIntegrity(); if (m_undoController) m_undoController->endMergeSequence(); } void ModelController::deleteElements(const MSelection &modelSelection) { deleteElements(modelSelection, Tr::tr("Delete")); } void ModelController::deleteElements(const MSelection &modelSelection, const QString &commandLabel) { MReferences simplifiedSelection = simplify(modelSelection); if (simplifiedSelection.elements().isEmpty()) return; if (m_undoController) m_undoController->beginMergeSequence(commandLabel); bool removed = false; const QList elements = simplifiedSelection.elements(); for (MElement *element : elements) { // element may have been deleted indirectly by predecessor element in loop if ((element = findElement(element->uid()))) { if (auto object = dynamic_cast(element)) { removeRelatedRelations(object); MObject *owner = object->owner(); int row = owner->children().indexOf(object); emit beginRemoveObject(row, owner); if (m_undoController) { auto cutCommand = new RemoveElementsCommand(this, commandLabel); m_undoController->push(cutCommand); cutCommand->add(element, owner); } unmapObject(object); owner->removeChild(object); emit endRemoveObject(row, owner); removed = true; } else if (auto relation = dynamic_cast(element)) { MObject *owner = relation->owner(); int row = owner->relations().indexOf(relation); emit beginRemoveRelation(row, owner); if (m_undoController) { auto cutCommand = new RemoveElementsCommand(this, commandLabel); m_undoController->push(cutCommand); cutCommand->add(element, owner); } unmapRelation(relation); owner->removeRelation(relation); emit endRemoveRelation(row, owner); removed = true; } else { QMT_CHECK(false); } } } if (removed) emit modified(); verifyModelIntegrity(); if (m_undoController) m_undoController->endMergeSequence(); } void ModelController::removeRelatedRelations(MObject *object) { const QList relations = m_objectRelationsMap.values(object->uid()); for (MRelation *relation : relations) removeRelation(relation); QMT_CHECK(m_objectRelationsMap.values(object->uid()).isEmpty()); } void ModelController::renewElementKey(MElement *element, QHash *renewedKeys) { if (element) { MElement *otherElement = findObject(element->uid()); if (otherElement) { QMT_CHECK(otherElement != element); } if (m_objectsMap.contains(element->uid()) || m_relationsMap.contains(element->uid())) { Uid oldKey = element->uid(); element->renewUid(); Uid newKey = element->uid(); renewedKeys->insert(oldKey, newKey); } auto object = dynamic_cast(element); if (object) { for (const Handle &child : object->children()) renewElementKey(child.target(), renewedKeys); for (const Handle &relation : object->relations()) renewElementKey(relation.target(), renewedKeys); } } } void ModelController::updateRelationKeys(MElement *element, const QHash &renewedKeys) { if (auto object = dynamic_cast(element)) { for (const Handle &handle : object->relations()) updateRelationEndKeys(handle.target(), renewedKeys); for (const Handle &child : object->children()) updateRelationKeys(child.target(), renewedKeys); } else if (auto relation = dynamic_cast(element)) { updateRelationEndKeys(relation, renewedKeys); } } void ModelController::updateRelationEndKeys(MRelation *relation, const QHash &renewedKeys) { if (relation) { Uid newEndAKey = renewedKeys.value(relation->endAUid(), Uid::invalidUid()); if (newEndAKey.isValid()) relation->setEndAUid(newEndAKey); Uid newEndBKey = renewedKeys.value(relation->endBUid(), Uid::invalidUid()); if (newEndBKey.isValid()) relation->setEndBUid(newEndBKey); } } void ModelController::mapObject(MObject *object) { if (object) { QMT_CHECK(!m_objectsMap.contains(object->uid())); m_objectsMap.insert(object->uid(), object); for (const Handle &child : object->children()) mapObject(child.target()); for (const Handle &relation : object->relations()) mapRelation(relation.target()); } } void ModelController::unmapObject(MObject *object) { if (object) { QMT_CHECK(m_objectsMap.contains(object->uid())); for (const Handle &relation : object->relations()) unmapRelation(relation.target()); for (const Handle &child : object->children()) unmapObject(child.target()); m_objectsMap.remove(object->uid()); } } void ModelController::mapRelation(MRelation *relation) { if (relation) { QMT_CHECK(!m_relationsMap.contains(relation->uid())); m_relationsMap.insert(relation->uid(), relation); QMT_CHECK(!m_objectRelationsMap.contains(relation->endAUid(), relation)); m_objectRelationsMap.insert(relation->endAUid(), relation); if (relation->endAUid() != relation->endBUid()) { QMT_CHECK(!m_objectRelationsMap.contains(relation->endBUid(), relation)); m_objectRelationsMap.insert(relation->endBUid(), relation); } } } void ModelController::unmapRelation(MRelation *relation) { if (relation) { QMT_CHECK(m_relationsMap.contains(relation->uid())); m_relationsMap.remove(relation->uid()); QMT_CHECK(m_objectRelationsMap.contains(relation->endAUid(), relation)); m_objectRelationsMap.remove(relation->endAUid(), relation); if (relation->endAUid() != relation->endBUid()) { QMT_CHECK(m_objectRelationsMap.contains(relation->endBUid(), relation)); m_objectRelationsMap.remove(relation->endBUid(), relation); } } } MReferences ModelController::simplify(const MSelection &modelSelection) { // PERFORM improve performance by using a set of Uid build from modelSelection MReferences references; const QList indices = modelSelection.indices(); for (const MSelection::Index &index : indices) { MElement *element = findElement(index.elementKey()); QMT_ASSERT(element, return MReferences()); // if any (grand-)parent of element is in modelSelection then ignore element bool ignore = false; MObject *owner = element->owner(); while (owner) { Uid ownerKey = owner->uid(); for (const MSelection::Index &otherIndex : indices) { if (otherIndex.elementKey() == ownerKey) { ignore = true; break; } } if (ignore) break; owner = owner->owner(); } if (!ignore) references.append(element); } return references; } void ModelController::verifyModelIntegrity() const { static const bool debugModelIntegrity = false; if (debugModelIntegrity) { QMT_ASSERT(m_rootPackage, return); QHash objectsMap; QHash relationsMap; QMultiHash objectRelationsMap; verifyModelIntegrity(m_rootPackage, &objectsMap, &relationsMap, &objectRelationsMap); QMT_ASSERT(objectsMap.size() == m_objectsMap.size(), return); for (const MObject *object : m_objectsMap) { QMT_ASSERT(object, return); QMT_ASSERT(m_objectsMap.contains(object->uid()), return); QMT_ASSERT(objectsMap.contains(object->uid()), return); } QMT_ASSERT(relationsMap.size() == m_relationsMap.size(), return); for (const MRelation *relation : m_relationsMap) { QMT_ASSERT(relation, return); QMT_ASSERT(m_relationsMap.contains(relation->uid()), return); QMT_ASSERT(relationsMap.contains(relation->uid()), return); } QMT_ASSERT(objectRelationsMap.size() == m_objectRelationsMap.size(), return); for (auto it = m_objectRelationsMap.cbegin(); it != m_objectRelationsMap.cend(); ++it) { QMT_ASSERT(objectRelationsMap.contains(it.key(), it.value()), return); } } } void ModelController::verifyModelIntegrity(const MObject *object, QHash *objectsMap, QHash *relationsMap, QMultiHash *objectRelationsMap) const { QMT_ASSERT(object, return); QMT_ASSERT(!objectsMap->contains(object->uid()), return); objectsMap->insert(object->uid(), object); for (const Handle &handle : object->relations()) { MRelation *relation = handle.target(); if (relation) { QMT_ASSERT(!relationsMap->contains(relation->uid()), return); relationsMap->insert(relation->uid(), relation); QMT_ASSERT(findObject(relation->endAUid()), return); QMT_ASSERT(findObject(relation->endBUid()), return); QMT_ASSERT(!objectRelationsMap->contains(relation->endAUid(), relation), return); objectRelationsMap->insert(relation->endAUid(), relation); QMT_ASSERT(!objectRelationsMap->contains(relation->endBUid(), relation), return); objectRelationsMap->insert(relation->endBUid(), relation); } } for (const Handle &handle : object->children()) { MObject *childObject = handle.target(); if (childObject) verifyModelIntegrity(childObject, objectsMap, relationsMap, objectRelationsMap); } } } // namespace qmt