summaryrefslogtreecommitdiff
path: root/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp
diff options
context:
space:
mode:
authorJochen Becher <jochen_becher@gmx.de>2016-01-07 20:32:43 +0100
committerJochen Becher <jochen_becher@gmx.de>2016-01-11 11:34:21 +0000
commitdc3ca42eeaa9b74c98c48ce68ca4c9aa795ca301 (patch)
tree87810329442766e5b7429467f3bfbe2bd3a74b34 /src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp
parent9e0631ea794c20903ea7901f741293a590b6b53f (diff)
downloadqt-creator-dc3ca42eeaa9b74c98c48ce68ca4c9aa795ca301.tar.gz
ModelEditor: move 3rd_party/modeling into libs/modelinglib
Only moved the files and adapted .pro and .qbs files accordingly. Change-Id: I7c17c2ebf246595c104edf60013bf78379955aa7 Reviewed-by: Tobias Hunger <tobias.hunger@theqtcompany.com>
Diffstat (limited to 'src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp')
-rw-r--r--src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp1220
1 files changed, 1220 insertions, 0 deletions
diff --git a/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp b/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp
new file mode 100644
index 0000000000..6f0e8f0c63
--- /dev/null
+++ b/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp
@@ -0,0 +1,1220 @@
+/***************************************************************************
+**
+** Copyright (C) 2015 Jochen Becher
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#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 <QDebug>
+
+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 = 0;
+};
+
+class ModelController::UpdateObjectCommand : public UndoCommand
+{
+public:
+ UpdateObjectCommand(ModelController *modelController, MObject *object)
+ : UndoCommand(tr("Change Object")),
+ m_modelController(modelController)
+ {
+ MCloneVisitor visitor;
+ object->accept(&visitor);
+ m_object = dynamic_cast<MObject *>(visitor.cloned());
+ QMT_CHECK(m_object);
+ }
+
+ ~UpdateObjectCommand() override
+ {
+ delete m_object;
+ }
+
+ bool mergeWith(const UndoCommand *other) override
+ {
+ auto updateCommand = dynamic_cast<const UpdateObjectCommand *>(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<MObject>(m_object->uid());
+ QMT_CHECK(object);
+ 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<MObject *>(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 = 0;
+ MObject *m_object = 0;
+};
+
+class ModelController::UpdateRelationCommand :
+ public UndoCommand
+{
+public:
+ UpdateRelationCommand(ModelController *modelController, MRelation *relation)
+ : UndoCommand(tr("Change Relation")),
+ m_modelController(modelController)
+ {
+ MCloneVisitor visitor;
+ relation->accept(&visitor);
+ m_relation = dynamic_cast<MRelation *>(visitor.cloned());
+ QMT_CHECK(m_relation);
+ }
+
+ ~UpdateRelationCommand() override
+ {
+ delete m_relation;
+ }
+
+ bool mergeWith(const UndoCommand *other) override
+ {
+ auto updateCommand = dynamic_cast<const UpdateRelationCommand *>(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<MRelation>(m_relation->uid());
+ QMT_CHECK(relation);
+ MObject *owner = relation->owner();
+ QMT_CHECK(owner);
+ int row = owner->relations().indexOf(relation);
+ emit m_modelController->beginUpdateRelation(row, owner);
+ MCloneVisitor cloneVisitor;
+ relation->accept(&cloneVisitor);
+ auto newRelation = dynamic_cast<MRelation *>(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 = 0;
+ MRelation *m_relation = 0;
+};
+
+class ModelController::AddElementsCommand : public UndoCommand
+{
+public:
+ AddElementsCommand(ModelController *modelController, const QString &commandLabel)
+ : UndoCommand(commandLabel),
+ m_modelController(modelController)
+ {
+ }
+
+ ~AddElementsCommand() override
+ {
+ foreach (const Clone &clone, 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_CHECK(clone.m_clonedElement);
+ QMT_CHECK(clone.m_clonedElement->uid() == clone.m_elementKey);
+ MObject *owner = m_modelController->findObject<MObject>(clone.m_ownerKey);
+ QMT_CHECK(owner);
+ QMT_CHECK(clone.m_indexOfElement >= 0);
+ switch (clone.m_elementType) {
+ case TypeObject:
+ {
+ emit m_modelController->beginInsertObject(clone.m_indexOfElement, owner);
+ auto object = dynamic_cast<MObject *>(clone.m_clonedElement);
+ QMT_CHECK(object);
+ m_modelController->mapObject(object);
+ owner->insertChild(clone.m_indexOfElement, object);
+ clone.m_clonedElement = 0;
+ 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<MRelation *>(clone.m_clonedElement);
+ QMT_CHECK(relation);
+ m_modelController->mapRelation(relation);
+ owner->insertRelation(clone.m_indexOfElement, relation);
+ clone.m_clonedElement = 0;
+ 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<MObject>(clone.m_ownerKey);
+ QMT_CHECK(owner);
+ switch (clone.m_elementType) {
+ case TypeObject:
+ {
+ MObject *object = m_modelController->findObject<MObject>(clone.m_elementKey);
+ QMT_CHECK(object);
+ 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<MRelation>(clone.m_elementKey);
+ QMT_CHECK(relation);
+ 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 = 0;
+ QList<ModelController::Clone> m_clonedElements;
+};
+
+class ModelController::RemoveElementsCommand : public UndoCommand
+{
+public:
+ RemoveElementsCommand(ModelController *modelController, const QString &commandLabel)
+ : UndoCommand(commandLabel),
+ m_modelController(modelController)
+ {
+ }
+
+ ~RemoveElementsCommand() override
+ {
+ foreach (const Clone &clone, 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<MObject *>(element)) {
+ clone.m_elementType = TypeObject;
+ clone.m_indexOfElement = owner->children().indexOf(object);
+ QMT_CHECK(clone.m_indexOfElement >= 0);
+ } else if (auto relation = dynamic_cast<MRelation *>(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_CHECK(clone.m_clonedElement);
+ 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<MObject>(clone.m_ownerKey);
+ QMT_CHECK(owner);
+ switch (clone.m_elementType) {
+ case TypeObject:
+ {
+ MObject *object = m_modelController->findObject<MObject>(clone.m_elementKey);
+ QMT_CHECK(object);
+ 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<MRelation>(clone.m_elementKey);
+ QMT_CHECK(relation);
+ 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_CHECK(clone.m_clonedElement);
+ MObject *owner = m_modelController->findObject<MObject>(clone.m_ownerKey);
+ QMT_CHECK(owner);
+ QMT_CHECK(clone.m_indexOfElement >= 0);
+ switch (clone.m_elementType) {
+ case TypeObject:
+ {
+ emit m_modelController->beginInsertObject(clone.m_indexOfElement, owner);
+ auto object = dynamic_cast<MObject *>(clone.m_clonedElement);
+ QMT_CHECK(object);
+ m_modelController->mapObject(object);
+ owner->insertChild(clone.m_indexOfElement, object);
+ clone.m_clonedElement = 0;
+ 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<MRelation *>(clone.m_clonedElement);
+ QMT_CHECK(relation);
+ m_modelController->mapRelation(relation);
+ owner->insertRelation(clone.m_indexOfElement, relation);
+ clone.m_clonedElement = 0;
+ 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 = 0;
+ QList<ModelController::Clone> m_clonedElements;
+};
+
+class ModelController::MoveObjectCommand : public UndoCommand
+{
+public:
+ MoveObjectCommand(ModelController *modelController, MObject *object)
+ : UndoCommand(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_CHECK(object);
+ MObject *formerOwner = object->owner();
+ int formerRow = formerOwner->children().indexOf(object);
+ emit m_modelController->beginMoveObject(formerRow, formerOwner);
+ formerOwner->decontrolChild(object);
+ MObject *newOwner = m_modelController->findObject(m_ownerKey);
+ 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 = 0;
+ Uid m_objectKey;
+ Uid m_ownerKey;
+ int m_indexOfElement = -1;
+};
+
+class ModelController::MoveRelationCommand : public UndoCommand
+{
+public:
+ MoveRelationCommand(ModelController *modelController, MRelation *relation)
+ : UndoCommand(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_CHECK(relation);
+ MObject *formerOwner = relation->owner();
+ int formerRow = formerOwner->relations().indexOf(relation);
+ emit m_modelController->beginMoveRelation(formerRow, formerOwner);
+ formerOwner->decontrolRelation(relation);
+ MObject *newOwner = m_modelController->findObject(m_ownerKey);
+ 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 = 0;
+ Uid m_relationKey;
+ Uid m_ownerKey;
+ int m_indexOfElement = -1;
+};
+
+ModelController::ModelController(QObject *parent)
+ : QObject(parent),
+ m_rootPackage(0),
+ m_undoController(0),
+ m_isResettingModel(false)
+{
+}
+
+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 MElement *element) const
+{
+ QMT_CHECK(element);
+ MObject *owner = element->owner();
+ if (!owner)
+ return Uid();
+ return owner->uid();
+}
+
+MElement *ModelController::findElement(const Uid &key)
+{
+ if (MObject *object = findObject(key))
+ return object;
+ else if (MRelation *relation = findRelation(key))
+ return relation;
+ return 0;
+}
+
+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_CHECK(row >= 0 && row < owner->children().size());
+ 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_CHECK(parentPackage);
+ QMT_CHECK(object);
+ int row = parentPackage->children().size();
+ if (!m_isResettingModel)
+ emit beginInsertObject(row, parentPackage);
+ mapObject(object);
+ if (m_undoController) {
+ auto undoCommand = new AddElementsCommand(this, 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_CHECK(object);
+ if (m_undoController)
+ m_undoController->beginMergeSequence(tr("Delete Object"));
+ removeRelatedRelations(object);
+ // remove object
+ QMT_CHECK(object->owner());
+ 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("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_CHECK(object);
+ 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<MPackage *>(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_CHECK(object);
+
+ 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) {
+ QList<MRelation *> relations = findRelationsOfObject(object);
+ foreach (MRelation *relation, relations)
+ emit relationEndChanged(relation, object);
+ if (auto package = dynamic_cast<MPackage *>(object)) {
+ if (m_oldPackageName != package->name())
+ emit packageNameChanged(package, m_oldPackageName);
+ }
+ emit modified();
+ }
+ }
+ verifyModelIntegrity();
+}
+
+void ModelController::moveObject(MPackage *newOwner, MObject *object)
+{
+ QMT_CHECK(newOwner);
+ QMT_CHECK(object);
+ QMT_CHECK(object != m_rootPackage);
+
+ if (newOwner != object->owner()) {
+ int formerRow = 0;
+ MObject *formerOwner = object->owner();
+ QMT_CHECK(formerOwner);
+ 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_CHECK(owner);
+ QMT_CHECK(relation);
+ QMT_CHECK(findObject(relation->endAUid()));
+ QMT_CHECK(findObject(relation->endBUid()));
+
+ int row = owner->relations().size();
+ if (!m_isResettingModel)
+ emit beginInsertRelation(row, owner);
+ mapRelation(relation);
+ if (m_undoController) {
+ auto undoCommand = new AddElementsCommand(this, 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_CHECK(relation);
+ MObject *owner = relation->owner();
+ QMT_CHECK(owner);
+ int row = owner->relations().indexOf(relation);
+ if (!m_isResettingModel)
+ emit beginRemoveRelation(row, owner);
+ if (m_undoController) {
+ auto undoCommand = new RemoveElementsCommand(this, 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_CHECK(relation);
+ MObject *owner = relation->owner();
+ QMT_CHECK(owner);
+ 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_CHECK(relation);
+ QMT_CHECK(findObject(relation->endAUid()));
+ QMT_CHECK(findObject(relation->endBUid()));
+ MObject *owner = relation->owner();
+ QMT_CHECK(owner);
+ if (!m_isResettingModel) {
+ emit endUpdateRelation(owner->relations().indexOf(relation), owner);
+ if (!cancelled)
+ emit modified();
+ }
+ verifyModelIntegrity();
+}
+
+void ModelController::moveRelation(MObject *newOwner, MRelation *relation)
+{
+ QMT_CHECK(newOwner);
+ QMT_CHECK(relation);
+
+ if (newOwner != relation->owner()) {
+ int formerRow = 0;
+ MObject *formerOwner = relation->owner();
+ QMT_CHECK(formerOwner);
+ 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<MRelation *> ModelController::findRelationsOfObject(const MObject *object) const
+{
+ QMT_CHECK(object);
+ 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("Cut"));
+ return copiedElements;
+}
+
+MContainer ModelController::copyElements(const MSelection &modelSelection)
+{
+ MReferences simplifiedSelection = simplify(modelSelection);
+ MContainer copiedElements;
+ foreach (MElement *element, simplifiedSelection.elements()) {
+ MCloneDeepVisitor visitor;
+ element->accept(&visitor);
+ MElement *clonedElement = visitor.cloned();
+ copiedElements.submit(clonedElement);
+ }
+ return copiedElements;
+}
+
+void ModelController::pasteElements(MObject *owner, const MContainer &modelContainer)
+{
+ // clone all elements and renew their keys
+ QHash<Uid, Uid> renewedKeys;
+ QList<MElement *> clonedElements;
+ foreach (MElement *element, modelContainer.elements()) {
+ MCloneDeepVisitor visitor;
+ element->accept(&visitor);
+ MElement *clonedElement = visitor.cloned();
+ renewElementKey(clonedElement, &renewedKeys);
+ clonedElements.append(clonedElement);
+ }
+ // fix all keys referencing between pasting elements
+ foreach (MElement *clonedElement, clonedElements)
+ updateRelationKeys(clonedElement, renewedKeys);
+ if (m_undoController)
+ m_undoController->beginMergeSequence(tr("Paste"));
+ // insert all elements
+ bool added = false;
+ foreach (MElement *clonedElement, clonedElements) {
+ if (auto object = dynamic_cast<MObject *>(clonedElement)) {
+ MObject *objectOwner = owner;
+ if (!dynamic_cast<MPackage*>(owner))
+ objectOwner = owner->owner();
+ QMT_CHECK(dynamic_cast<MPackage*>(objectOwner));
+ int row = objectOwner->children().size();
+ emit beginInsertObject(row, objectOwner);
+ mapObject(object);
+ if (m_undoController) {
+ auto undoCommand = new AddElementsCommand(this, 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<MRelation *>(clonedElement)) {
+ int row = owner->relations().size();
+ emit beginInsertRelation(row, owner);
+ mapRelation(relation);
+ if (m_undoController) {
+ auto undoCommand = new AddElementsCommand(this, 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("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;
+ foreach (MElement *element, simplifiedSelection.elements()) {
+ // element may have been deleted indirectly by predecessor element in loop
+ if ((element = findElement(element->uid()))) {
+ if (auto object = dynamic_cast<MObject *>(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<MRelation *>(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)
+{
+ foreach (MRelation *relation, m_objectRelationsMap.values(object->uid()))
+ removeRelation(relation);
+ QMT_CHECK(m_objectRelationsMap.values(object->uid()).isEmpty());
+}
+
+void ModelController::renewElementKey(MElement *element, QHash<Uid, Uid> *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<MObject *>(element);
+ if (object) {
+ foreach (const Handle<MObject> &child, object->children())
+ renewElementKey(child.target(), renewedKeys);
+ foreach (const Handle<MRelation> &relation, object->relations())
+ renewElementKey(relation.target(), renewedKeys);
+ }
+ }
+}
+
+void ModelController::updateRelationKeys(MElement *element, const QHash<Uid, Uid> &renewedKeys)
+{
+ if (auto object = dynamic_cast<MObject *>(element)) {
+ foreach (const Handle<MRelation> &handle, object->relations())
+ updateRelationEndKeys(handle.target(), renewedKeys);
+ foreach (const Handle<MObject> &child, object->children())
+ updateRelationKeys(child.target(), renewedKeys);
+ } else if (auto relation = dynamic_cast<MRelation *>(element)) {
+ updateRelationEndKeys(relation, renewedKeys);
+ }
+}
+
+void ModelController::updateRelationEndKeys(MRelation *relation, const QHash<Uid, Uid> &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);
+ foreach (const Handle<MObject> &child, object->children())
+ mapObject(child.target());
+ foreach (const Handle<MRelation> &relation, object->relations())
+ mapRelation(relation.target());
+ }
+}
+
+void ModelController::unmapObject(MObject *object)
+{
+ if (object) {
+ QMT_CHECK(m_objectsMap.contains(object->uid()));
+ foreach (const Handle<MRelation> &relation, object->relations())
+ unmapRelation(relation.target());
+ foreach (const Handle<MObject> &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;
+ foreach (const MSelection::Index &index, modelSelection.indices()) {
+ MElement *element = findElement(index.elementKey());
+ QMT_CHECK(element);
+ // 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();
+ foreach (const MSelection::Index &index, modelSelection.indices()) {
+ if (index.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_CHECK(m_rootPackage);
+
+ QHash<Uid, const MObject *> objectsMap;
+ QHash<Uid, const MRelation *> relationsMap;
+ QMultiHash<Uid, MRelation *> objectRelationsMap;
+ verifyModelIntegrity(m_rootPackage, &objectsMap, &relationsMap, &objectRelationsMap);
+
+ QMT_CHECK(objectsMap.size() == m_objectsMap.size());
+ foreach (const MObject *object, m_objectsMap) {
+ QMT_CHECK(object);
+ QMT_CHECK(m_objectsMap.contains(object->uid()));
+ QMT_CHECK(objectsMap.contains(object->uid()));
+ }
+ QMT_CHECK(relationsMap.size() == m_relationsMap.size());
+ foreach (const MRelation *relation, m_relationsMap) {
+ QMT_CHECK(relation);
+ QMT_CHECK(m_relationsMap.contains(relation->uid()));
+ QMT_CHECK(relationsMap.contains(relation->uid()));
+ }
+ QMT_CHECK(objectRelationsMap.size() == m_objectRelationsMap.size());
+ for (auto it = m_objectRelationsMap.cbegin(); it != m_objectRelationsMap.cend(); ++it) {
+ QMT_CHECK(objectRelationsMap.contains(it.key(), it.value()));
+ }
+ }
+}
+
+void ModelController::verifyModelIntegrity(const MObject *object, QHash<Uid, const MObject *> *objectsMap,
+ QHash<Uid, const MRelation *> *relationsMap,
+ QMultiHash<Uid, MRelation *> *objectRelationsMap) const
+{
+ QMT_CHECK(object);
+ QMT_CHECK(!objectsMap->contains(object->uid()));
+ objectsMap->insert(object->uid(), object);
+ foreach (const Handle<MRelation> &handle, object->relations()) {
+ MRelation *relation = handle.target();
+ if (relation) {
+ QMT_CHECK(!relationsMap->contains(relation->uid()));
+ relationsMap->insert(relation->uid(), relation);
+ QMT_CHECK(findObject(relation->endAUid()));
+ QMT_CHECK(findObject(relation->endBUid()));
+ QMT_CHECK(!objectRelationsMap->contains(relation->endAUid(), relation));
+ objectRelationsMap->insert(relation->endAUid(), relation);
+ QMT_CHECK(!objectRelationsMap->contains(relation->endBUid(), relation));
+ objectRelationsMap->insert(relation->endBUid(), relation);
+ }
+ }
+ foreach (const Handle<MObject> &handle, object->children()) {
+ MObject *childObject = handle.target();
+ if (childObject)
+ verifyModelIntegrity(childObject, objectsMap, relationsMap, objectRelationsMap);
+ }
+}
+
+} // namespace qmt