Commit 4c469e01 authored by Jochen Becher's avatar Jochen Becher

ModelEditor: Introduce swimlanes

Change-Id: I9ac9c51eabc00c6912fd47fbf51b50b2938846ae
Reviewed-by: Riitta-Leena Miettinen's avatarLeena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Tobias Hunger's avatarTobias Hunger <tobias.hunger@qt.io>
parent c562dce3
......@@ -119,9 +119,18 @@
// Toolbar {
// id: <id>
// title: <a Ui title. Defaults to the id of the toolbar>
// element: <List of elements the toolbar is assigned to. Each element can be one of package, component,
// class, item or any Icon definition. Default to nothing which defines an object toolbar for
// diagrams.>
// priority: <priority number which decides about the position of toolbar in toolbox. Defaults to 0>
// Tools {
// Tool { title: <Ui title>; element: <element type>; stereotype: <stereotype, defaults to nothing> }
// Tool {
// title: <Ui title>;
// element: <element type; one of package, class, component, item, annotation, boundary or swimlane
// for object toolbars. One of dependency, inheritance or association for element toolbars.
// Must be given>;
// stereotype: <stereotype, defaults to nothing>
// }
// Separator
// }
// }
......@@ -465,6 +474,7 @@ Toolbar {
Separator
Tool { title: "Annotation"; element: annotation }
Tool { title: "Boundary"; element: boundary }
Tool { title: "Swimlane"; element: swimlane }
}
}
......@@ -477,6 +487,7 @@ Toolbar {
Separator
Tool { title: "Annotation"; element: annotation }
Tool { title: "Boundary"; element: boundary }
Tool { title: "Swimlane"; element: swimlane }
}
}
......@@ -489,6 +500,7 @@ Toolbar {
Separator
Tool { title: "Annotation"; element: annotation }
Tool { title: "Boundary"; element: boundary }
Tool { title: "Swimlane"; element: swimlane }
}
}
......@@ -504,5 +516,6 @@ Toolbar {
Separator
Tool { title: "Annotation"; element: annotation }
Tool { title: "Boundary"; element: boundary }
Tool { title: "Swimlane"; element: swimlane }
}
}
......@@ -74,6 +74,8 @@ QtcLibrary {
"diagram/dpackage.h",
"diagram/drelation.cpp",
"diagram/drelation.h",
"diagram/dswimlane.cpp",
"diagram/dswimlane.h",
"diagram/dvisitor.h",
"diagram_controller/dclonevisitor.cpp",
"diagram_controller/dclonevisitor.h",
......@@ -130,6 +132,8 @@ QtcLibrary {
"diagram_scene/items/relationitem.h",
"diagram_scene/items/stereotypedisplayvisitor.cpp",
"diagram_scene/items/stereotypedisplayvisitor.h",
"diagram_scene/items/swimlaneitem.cpp",
"diagram_scene/items/swimlaneitem.h",
"diagram_scene/latchcontroller.cpp",
"diagram_scene/latchcontroller.h",
"diagram_scene/parts/alignbuttonsitem.cpp",
......
......@@ -36,7 +36,6 @@
#include <QHash>
#include <QSet>
#include <QPair>
#include <QDebug>
namespace qmt {
......@@ -884,7 +883,8 @@ void StereotypeDefinitionParser::parseToolbarTool(const Toolbar *toolbar, Toolba
<< QStringLiteral("class")
<< QStringLiteral("item")
<< QStringLiteral("annotation")
<< QStringLiteral("boundary");
<< QStringLiteral("boundary")
<< QStringLiteral("swimlane");
QString elementName = element.toLower();
if (!elementNames.contains(elementName))
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for element.")).arg(element), token.sourcePos());
......
......@@ -41,6 +41,7 @@ class DAssociation;
class DConnection;
class DAnnotation;
class DBoundary;
class DSwimlane;
class DConstVisitor
{
......@@ -61,6 +62,7 @@ public:
virtual void visitDConnection(const DConnection *connection) = 0;
virtual void visitDAnnotation(const DAnnotation *annotation) = 0;
virtual void visitDBoundary(const DBoundary *boundary) = 0;
virtual void visitDSwimlane(const DSwimlane *swimlane) = 0;
};
} // namespace qmt
/****************************************************************************
**
** Copyright (C) 2017 Jochen Becher
** Contact: https://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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "dswimlane.h"
#include "dvisitor.h"
#include "dconstvisitor.h"
namespace qmt {
DSwimlane::DSwimlane()
: DElement()
{
}
DSwimlane::DSwimlane(const DSwimlane &rhs)
: DElement(rhs),
m_text(rhs.m_text),
m_horizontal(rhs.m_horizontal),
m_pos(rhs.m_pos)
{
}
DSwimlane::~DSwimlane()
{
}
DSwimlane &DSwimlane::operator=(const DSwimlane &rhs)
{
if (this != &rhs) {
DElement::operator=(rhs);
m_text = rhs.m_text;
m_horizontal = rhs.m_horizontal;
m_pos = rhs.m_pos;
}
return *this;
}
void DSwimlane::setText(const QString &text)
{
m_text = text;
}
void DSwimlane::setHorizontal(bool horizontal)
{
m_horizontal = horizontal;
}
void DSwimlane::setPos(qreal pos)
{
m_pos = pos;
}
void DSwimlane::accept(DVisitor *visitor)
{
visitor->visitDSwimlane(this);
}
void DSwimlane::accept(DConstVisitor *visitor) const
{
visitor->visitDSwimlane(this);
}
} // namespoace qmt
/****************************************************************************
**
** Copyright (C) 2017 Jochen Becher
** Contact: https://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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "delement.h"
namespace qmt {
class QMT_EXPORT DSwimlane : public DElement
{
public:
DSwimlane();
DSwimlane(const DSwimlane &rhs);
~DSwimlane();
DSwimlane &operator=(const DSwimlane &rhs);
Uid modelUid() const override { return Uid::invalidUid(); }
QString text() const { return m_text; }
void setText(const QString &text);
bool isHorizontal() const { return m_horizontal; }
void setHorizontal(bool horizontal);
qreal pos() const { return m_pos; }
void setPos(qreal pos);
void accept(DVisitor *visitor) override;
void accept(DConstVisitor *visitor) const override;
private:
QString m_text;
bool m_horizontal = false; // false: vertical, true: horizontal
qreal m_pos = 0.0;
};
} // namespace qmt
......@@ -41,6 +41,7 @@ class DAssociation;
class DConnection;
class DAnnotation;
class DBoundary;
class DSwimlane;
class DVisitor
{
......@@ -61,6 +62,7 @@ public:
virtual void visitDConnection(DConnection *connection) = 0;
virtual void visitDAnnotation(DAnnotation *annotation) = 0;
virtual void visitDBoundary(DBoundary *boundary) = 0;
virtual void visitDSwimlane(DSwimlane *swimlane) = 0;
};
} // namespace qmt
......@@ -39,6 +39,7 @@
#include "qmt/diagram/dconnection.h"
#include "qmt/diagram/dannotation.h"
#include "qmt/diagram/dboundary.h"
#include "qmt/diagram/dswimlane.h"
#include "qmt/infrastructure/qmtassert.h"
namespace qmt {
......@@ -144,6 +145,14 @@ void DCloneVisitor::visitDBoundary(const DBoundary *boundary)
visitDElement(boundary);
}
void DCloneVisitor::visitDSwimlane(const DSwimlane *swimlane)
{
if (!m_cloned)
m_cloned = new DSwimlane(*swimlane);
visitDElement(swimlane);
}
DCloneDeepVisitor::DCloneDeepVisitor()
: m_cloned(0)
{
......@@ -244,4 +253,11 @@ void DCloneDeepVisitor::visitDBoundary(const DBoundary *boundary)
visitDElement(boundary);
}
void DCloneDeepVisitor::visitDSwimlane(const DSwimlane *swimlane)
{
if (!m_cloned)
m_cloned = new DSwimlane(*swimlane);
visitDElement(swimlane);
}
} // namespace qmt
......@@ -51,6 +51,7 @@ public:
void visitDConnection(const DConnection *connection) override;
void visitDAnnotation(const DAnnotation *annotation) override;
void visitDBoundary(const DBoundary *boundary) override;
void visitDSwimlane(const DSwimlane *swimlane) override;
private:
DElement *m_cloned;
......@@ -77,6 +78,7 @@ public:
void visitDConnection(const DConnection *connection) override;
void visitDAnnotation(const DAnnotation *annotation) override;
void visitDBoundary(const DBoundary *boundary) override;
void visitDSwimlane(const DSwimlane *swimlane) override;
private:
DElement *m_cloned;
......
......@@ -39,6 +39,7 @@
#include "qmt/diagram/dconnection.h"
#include "qmt/diagram/dannotation.h"
#include "qmt/diagram/dboundary.h"
#include "qmt/diagram/dswimlane.h"
#include "qmt/infrastructure/qmtassert.h"
namespace qmt {
......@@ -178,4 +179,14 @@ void DFlatAssignmentVisitor::visitDBoundary(const DBoundary *boundary)
target->setRect(boundary->rect());
}
void DFlatAssignmentVisitor::visitDSwimlane(const DSwimlane *swimlane)
{
visitDElement(swimlane);
auto target = dynamic_cast<DSwimlane *>(m_target);
QMT_ASSERT(target, return);
target->setText(swimlane->text());
target->setHorizontal(swimlane->isHorizontal());
target->setPos(swimlane->pos());
}
} // namespace qmt
......@@ -49,6 +49,7 @@ public:
void visitDConnection(const DConnection *connection) override;
void visitDAnnotation(const DAnnotation *annotation) override;
void visitDBoundary(const DBoundary *boundary) override;
void visitDSwimlane(const DSwimlane *swimlane) override;
private:
DElement *m_target;
......
......@@ -39,6 +39,7 @@
#include "qmt/diagram/dconnection.h"
#include "qmt/diagram/dannotation.h"
#include "qmt/diagram/dboundary.h"
#include "qmt/diagram/dswimlane.h"
namespace qmt {
......@@ -116,6 +117,11 @@ void DVoidVisitor::visitDBoundary(DBoundary *boundary)
visitDElement(boundary);
}
void DVoidVisitor::visitDSwimlane(DSwimlane *swimlane)
{
visitDElement(swimlane);
}
DConstVoidVisitor::DConstVoidVisitor()
{
}
......@@ -190,4 +196,9 @@ void DConstVoidVisitor::visitDBoundary(const DBoundary *boundary)
visitDElement(boundary);
}
void DConstVoidVisitor::visitDSwimlane(const DSwimlane *swimlane)
{
visitDElement(swimlane);
}
} // namespace qmt
......@@ -50,6 +50,7 @@ public:
void visitDConnection(DConnection *connection) override;
void visitDAnnotation(DAnnotation *annotation) override;
void visitDBoundary(DBoundary *boundary) override;
void visitDSwimlane(DSwimlane *swimlane) override;
};
class QMT_EXPORT DConstVoidVisitor : public DConstVisitor
......@@ -71,6 +72,7 @@ public:
void visitDConnection(const DConnection *connection) override;
void visitDAnnotation(const DAnnotation *annotation) override;
void visitDBoundary(const DBoundary *boundary) override;
void visitDSwimlane(const DSwimlane *swimlane) override;
};
} // namespace qmt
......@@ -25,6 +25,8 @@
#pragma once
#include <QRectF>
namespace qmt {
class ISelectable
......@@ -36,6 +38,8 @@ public:
virtual void setSecondarySelected(bool secondarySelected) = 0;
virtual bool isFocusSelected() const = 0;
virtual void setFocusSelected(bool focusSelected) = 0;
virtual QRectF getSecondarySelectionBoundary() = 0;
virtual void setBoundarySelected(const QRectF &boundary, bool secondary) = 0;
};
} // namespace qmt
......@@ -27,6 +27,7 @@
namespace qmt {
const int SWIMLANE_ITEMS_ZVALUE = -1100;
const int BOUNDARY_ITEMS_ZVALUE = -1000;
// all model objects have z-values from -500 to 500 depending on their depth in the model tree
const int RELATION_ITEMS_ZVALUE = 1000;
......
......@@ -39,6 +39,7 @@
#include "qmt/diagram_controller/diagramcontroller.h"
#include "qmt/diagram_controller/dselection.h"
#include "qmt/diagram_scene/items/objectitem.h"
#include "qmt/diagram_scene/items/swimlaneitem.h"
#include "qmt/model/mdiagram.h"
#include "qmt/model/mobject.h"
#include "qmt/model/mpackage.h"
......@@ -48,6 +49,8 @@
#include "qmt/tasks/diagramscenecontroller.h"
#include "qmt/tasks/ielementtasks.h"
#include "utils/asconst.h"
#include <QSet>
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
......@@ -241,7 +244,7 @@ ObjectItem *DiagramSceneModel::findTopmostObjectItem(const QPointF &scenePos) co
{
// fetch affected items from scene in correct drawing order to find topmost element
const QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos);
for (QGraphicsItem *item : items) {
for (QGraphicsItem *item : Utils::asConst(items)) {
if (m_graphicsItems.contains(item)) {
DObject *object = dynamic_cast<DObject *>(m_itemToElementMap.value(item));
if (object)
......@@ -705,6 +708,7 @@ void DiagramSceneModel::onEndResetDiagram(const MDiagram *diagram)
// update graphics items again so every item gets a correct list of colliding items
foreach (DElement *element, diagram->diagramElements())
updateGraphicsItem(m_elementToItemMap.value(element), element);
recalcSceneRectSize();
}
m_busyState = NotBusy;
}
......@@ -724,6 +728,7 @@ void DiagramSceneModel::onEndUpdateElement(int row, const MDiagram *diagram)
if (diagram == m_diagram) {
QGraphicsItem *item = m_graphicsItems.at(row);
updateGraphicsItem(item, diagram->diagramElements().at(row));
recalcSceneRectSize();
}
m_busyState = NotBusy;
}
......@@ -747,6 +752,7 @@ void DiagramSceneModel::onEndInsertElement(int row, const MDiagram *diagram)
updateGraphicsItem(item, element);
m_graphicsScene->invalidate();
updateGraphicsItem(item, element);
recalcSceneRectSize();
}
m_busyState = NotBusy;
}
......@@ -757,6 +763,7 @@ void DiagramSceneModel::onBeginRemoveElement(int row, const MDiagram *diagram)
if (diagram == m_diagram) {
QGraphicsItem *item = m_graphicsItems.takeAt(row);
deleteGraphicsItem(item, diagram->diagramElements().at(row));
recalcSceneRectSize();
}
m_busyState = RemoveElement;
}
......@@ -807,6 +814,27 @@ void DiagramSceneModel::onSelectionChanged()
}
}
// select more items secondarily
for (QGraphicsItem *selectedItem : Utils::asConst(m_selectedItems)) {
if (auto selectable = dynamic_cast<ISelectable *>(selectedItem)) {
QRectF boundary = selectable->getSecondarySelectionBoundary();
if (!boundary.isEmpty()) {
for (QGraphicsItem *item : Utils::asConst(m_graphicsItems)) {
if (auto secondarySelectable = dynamic_cast<ISelectable *>(item)) {
if (!item->isSelected() && !secondarySelectable->isSecondarySelected()) {
secondarySelectable->setBoundarySelected(boundary, true);
QMT_CHECK(!m_selectedItems.contains(item));
QMT_CHECK(!m_secondarySelectedItems.contains(item));
if (secondarySelectable->isSecondarySelected())
newSecondarySelectedItems.insert(item);
}
}
}
}
}
}
// select all relations where both ends are primary or secondary selected
foreach (DElement *element, m_diagram->diagramElements()) {
auto relation = dynamic_cast<DRelation *>(element);
......@@ -882,6 +910,17 @@ void DiagramSceneModel::addExtraSceneItems()
m_latchController->addToGraphicsScene(m_graphicsScene);
}
void DiagramSceneModel::recalcSceneRectSize()
{
QRectF sceneRect = m_originItem->mapRectToScene(m_originItem->boundingRect());
for (QGraphicsItem *item : Utils::asConst(m_graphicsItems)) {
// TODO use an interface to update sceneRect by item
if (!dynamic_cast<SwimlaneItem *>(item))
sceneRect |= item->mapRectToScene(item->boundingRect());
}
emit sceneRectChanged(sceneRect);
}
QGraphicsItem *DiagramSceneModel::createGraphicsItem(DElement *element)
{
QMT_ASSERT(element, return nullptr);
......
......@@ -80,6 +80,7 @@ public:
signals:
void diagramSceneActivated(const MDiagram *diagram);
void selectionHasChanged(const MDiagram *diagram);
void sceneRectChanged(const QRectF &sceneRect);
public:
DiagramController *diagramController() const { return m_diagramController; }
......@@ -152,6 +153,7 @@ private:
void clearGraphicsScene();
void removeExtraSceneItems();
void addExtraSceneItems();
void recalcSceneRectSize();
QGraphicsItem *createGraphicsItem(DElement *element);
void updateGraphicsItem(QGraphicsItem *item, DElement *element);
void deleteGraphicsItem(QGraphicsItem *item, DElement *element);
......
......@@ -35,6 +35,7 @@
#include "items/connectionitem.h"
#include "items/annotationitem.h"
#include "items/boundaryitem.h"
#include "items/swimlaneitem.h"
#include "qmt/diagram/delement.h"
#include "qmt/diagram/dobject.h"
......@@ -50,6 +51,7 @@
#include "qmt/diagram/dconnection.h"
#include "qmt/diagram/dannotation.h"
#include "qmt/diagram/dboundary.h"
#include "qmt/diagram/dswimlane.h"
#include "qmt/infrastructure/qmtassert.h"
namespace qmt {
......@@ -142,6 +144,12 @@ void DiagramSceneModel::CreationVisitor::visitDBoundary(DBoundary *boundary)
m_graphicsItem = new BoundaryItem(boundary, m_diagramSceneModel);
}
void DiagramSceneModel::CreationVisitor::visitDSwimlane(DSwimlane *swimlane)
{
QMT_CHECK(!m_graphicsItem);
m_graphicsItem = new SwimlaneItem(swimlane, m_diagramSceneModel);
}
DiagramSceneModel::UpdateVisitor::UpdateVisitor(QGraphicsItem *item, DiagramSceneModel *diagramSceneModel,
DElement *relatedElement)
: m_graphicsItem(item),
......@@ -297,4 +305,15 @@ void DiagramSceneModel::UpdateVisitor::visitDBoundary(DBoundary *boundary)
boundaryItem->update();
}
void DiagramSceneModel::UpdateVisitor::visitDSwimlane(DSwimlane *swimlane)
{
Q_UNUSED(swimlane); // avoid warning in release mode
QMT_ASSERT(m_graphicsItem, return);
SwimlaneItem *swimlaneItem = qgraphicsitem_cast<SwimlaneItem *>(m_graphicsItem);
QMT_ASSERT(swimlaneItem, return);
QMT_CHECK(swimlaneItem->swimlane() == swimlane);
swimlaneItem->update();
}
} // namespace qmt
......@@ -52,6 +52,7 @@ public:
void visitDConnection(DConnection *connection) override;
void visitDAnnotation(DAnnotation *annotation) override;
void visitDBoundary(DBoundary *boundary) override;
void visitDSwimlane(DSwimlane *swimlane) override;
private:
DiagramSceneModel *m_diagramSceneModel;
......@@ -78,6 +79,7 @@ public:
void visitDConnection(DConnection *connection) override;
void visitDAnnotation(DAnnotation *annotation) override;
void visitDBoundary(DBoundary *boundary) override;
void visitDSwimlane(DSwimlane *swimlane) override;
private:
QGraphicsItem *m_graphicsItem;
......
......@@ -213,6 +213,21 @@ void AnnotationItem::setFocusSelected(bool focusSelected)
}
}
QRectF AnnotationItem::getSecondarySelectionBoundary()
{
return QRectF();
}
void AnnotationItem::setBoundarySelected(const QRectF &boundary, bool secondary)
{
if (boundary.contains(mapRectToScene(boundingRect()))) {
if (secondary)
setSecondarySelected(true);
else
setSelected(true);
}
}
bool AnnotationItem::isEditable() const
{
return true;
......
......@@ -77,6 +77,8 @@ public:
void setSecondarySelected(bool secondarySelected) override;
bool isFocusSelected() const override;
void setFocusSelected(bool focusSelected) override;
QRectF getSecondarySelectionBoundary() override;
void setBoundarySelected(const QRectF &boundary, bool secondary) override;
bool isEditable() const override;
void edit() override;
......
......@@ -264,6 +264,21 @@ void BoundaryItem::setFocusSelected(bool focusSelected)
}
}
QRectF BoundaryItem::getSecondarySelectionBoundary()
{
return QRectF();
}
void BoundaryItem::setBoundarySelected(const QRectF &boundary, bool secondary)
{
if (boundary.contains(mapRectToScene(boundingRect()))) {
if (secondary)
setSecondarySelected(true);
else
setSelected(true);
}
}
bool BoundaryItem::isEditable() const
{
return true;
......
......@@ -76,6 +76,8 @@ public:
void setSecondarySelected(bool secondarySelected) override;
bool isFocusSelected() const override;
void setFocusSelected(bool focusSelected) override;
QRectF getSecondarySelectionBoundary() override;
void setBoundarySelected(const QRectF &boundary, bool secondary) override;
bool isEditable() const override;
void edit() override;
......
......@@ -208,6 +208,21 @@ void ObjectItem::setFocusSelected(bool focusSelected)
}
}
QRectF ObjectItem::getSecondarySelectionBoundary()
{
return QRectF();
}
void ObjectItem::setBoundarySelected(const QRectF &boundary, bool secondary)
{
if (boundary.contains(mapRectToScene(boundingRect()))) {
if (secondary)
setSecondarySelected(true);
else
setSelected(true);
}
}
ILatchable::Action ObjectItem::horizontalLatchAction() const
{
if (!m_selectionMarker)
......
......@@ -113,6 +113,8 @@ public:
void setSecondarySelected(bool secondarySelected) override;
bool isFocusSelected() const override;
void setFocusSelected(bool focusSelected) override;
QRectF getSecondarySelectionBoundary() override;
void setBoundarySelected(const QRectF &boundary, bool secondary) override;
Action horizontalLatchAction() const override;
Action verticalLatchAction() const override;
......
......@@ -316,6 +316,18 @@ void RelationItem::setFocusSelected(bool focusSelected)
}
}