ModelEditor: Show relation templates in object toolbars

Change-Id: I06de22538e500c133693ff0c791ac8a2d3be3402
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Jochen Becher
2016-08-20 21:46:55 +02:00
parent 6ea47c98fb
commit 82cb2b248e
19 changed files with 464 additions and 159 deletions

View File

@@ -38,6 +38,7 @@
#include "qmt/diagram/drelation.h"
#include "qmt/diagram_controller/diagramcontroller.h"
#include "qmt/diagram_controller/dselection.h"
#include "qmt/diagram_scene/items/objectitem.h"
#include "qmt/model/mdiagram.h"
#include "qmt/model/mobject.h"
#include "qmt/model/mpackage.h"
@@ -229,17 +230,25 @@ DElement *DiagramSceneModel::findTopmostElement(const QPointF &scenePos) const
}
DObject *DiagramSceneModel::findTopmostObject(const QPointF &scenePos) const
{
ObjectItem *item = findTopmostObjectItem(scenePos);
if (!item)
return nullptr;
return item->object();
}
ObjectItem *DiagramSceneModel::findTopmostObjectItem(const QPointF &scenePos) const
{
// fetch affected items from scene in correct drawing order to find topmost element
QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos);
foreach (QGraphicsItem *item, items) {
const QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos);
for (QGraphicsItem *item : items) {
if (m_graphicsItems.contains(item)) {
DObject *object = dynamic_cast<DObject *>(m_itemToElementMap.value(item));
if (object)
return object;
return dynamic_cast<ObjectItem *>(item);
}
}
return 0;
return nullptr;
}
QGraphicsItem *DiagramSceneModel::graphicsItem(DElement *element) const

View File

@@ -54,6 +54,7 @@ class DSelection;
class MDiagram;
class DElement;
class DObject;
class ObjectItem;
class QMT_EXPORT DiagramSceneModel : public QObject
{
@@ -98,6 +99,7 @@ public:
DSelection selectedElements() const;
DElement *findTopmostElement(const QPointF &scenePos) const;
DObject *findTopmostObject(const QPointF &scenePos) const;
ObjectItem *findTopmostObjectItem(const QPointF &scenePos) const;
QList<QGraphicsItem *> graphicsItems() const { return m_graphicsItems; }
QGraphicsItem *graphicsItem(DElement *element) const;

View File

@@ -40,6 +40,7 @@
#include "qmt/infrastructure/qmtassert.h"
#include "qmt/model/mclass.h"
#include "qmt/model/mclassmember.h"
#include "qmt/model/massociation.h"
#include "qmt/model_controller/modelcontroller.h"
#include "qmt/stereotype/stereotypecontroller.h"
#include "qmt/stereotype/stereotypeicon.h"
@@ -59,15 +60,20 @@
#include <algorithm>
#include <qmt/stereotype/customrelation.h>
namespace qmt {
static const char ASSOCIATION[] = "association";
static const char INHERITANCE[] = "inheritance";
static const qreal MINIMUM_AUTO_WIDTH = 80.0;
static const qreal MINIMUM_AUTO_HEIGHT = 60.0;
static const qreal BODY_VERT_BORDER = 4.0;
static const qreal BODY_HORIZ_BORDER = 4.0;
ClassItem::ClassItem(DClass *klass, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
: ObjectItem(klass, diagramSceneModel, parent)
: ObjectItem(QStringLiteral("class"), klass, diagramSceneModel, parent)
{
}
@@ -247,30 +253,83 @@ QSizeF ClassItem::minimumSize() const
return calcMinimumGeometry();
}
void ClassItem::relationDrawn(const QString &id, const QPointF &toScenePos, const QList<QPointF> &intermediatePoints)
void ClassItem::relationDrawn(const QString &id, ObjectItem *targetItem, const QList<QPointF> &intermediatePoints)
{
DElement *targetElement = diagramSceneModel()->findTopmostElement(toScenePos);
if (targetElement) {
if (id == QLatin1String("inheritance")) {
auto baseClass = dynamic_cast<DClass *>(targetElement);
if (baseClass) {
auto derivedClass = dynamic_cast<DClass *>(object());
QMT_ASSERT(derivedClass, return);
diagramSceneModel()->diagramSceneController()->createInheritance(derivedClass, baseClass, intermediatePoints, diagramSceneModel()->diagram());
DiagramSceneController *diagramSceneController = diagramSceneModel()->diagramSceneController();
if (id == INHERITANCE) {
auto baseClass = dynamic_cast<DClass *>(targetItem->object());
if (baseClass) {
auto derivedClass = dynamic_cast<DClass *>(object());
QMT_ASSERT(derivedClass, return);
diagramSceneController->createInheritance(derivedClass, baseClass, intermediatePoints, diagramSceneModel()->diagram());
}
return;
} else if (id == ASSOCIATION) {
auto associatedClass = dynamic_cast<DClass *>(targetItem->object());
if (associatedClass) {
auto derivedClass = dynamic_cast<DClass *>(object());
QMT_ASSERT(derivedClass, return);
diagramSceneController->createAssociation(derivedClass, associatedClass, intermediatePoints, diagramSceneModel()->diagram());
}
return;
} else {
StereotypeController *stereotypeController = diagramSceneModel()->stereotypeController();
CustomRelation customRelation = stereotypeController->findCustomRelation(id);
if (!customRelation.isNull()) {
switch (customRelation.element()) {
case CustomRelation::Element::Inheritance:
{
auto baseClass = dynamic_cast<DClass *>(targetItem->object());
if (baseClass) {
auto derivedClass = dynamic_cast<DClass *>(object());
QMT_ASSERT(derivedClass, return);
diagramSceneController->createInheritance(derivedClass, baseClass, intermediatePoints, diagramSceneModel()->diagram());
}
return;
}
} else if (id == QLatin1String("dependency")) {
auto dependantObject = dynamic_cast<DObject *>(targetElement);
if (dependantObject)
diagramSceneModel()->diagramSceneController()->createDependency(object(), dependantObject, intermediatePoints, diagramSceneModel()->diagram());
} else if (id == QLatin1String("association")) {
auto assoziatedClass = dynamic_cast<DClass *>(targetElement);
if (assoziatedClass) {
auto derivedClass = dynamic_cast<DClass *>(object());
QMT_ASSERT(derivedClass, return);
diagramSceneModel()->diagramSceneController()->createAssociation(derivedClass, assoziatedClass, intermediatePoints, diagramSceneModel()->diagram());
case CustomRelation::Element::Association:
{
auto assoziatedClass = dynamic_cast<DClass *>(targetItem->object());
if (assoziatedClass) {
auto derivedClass = dynamic_cast<DClass *>(object());
QMT_ASSERT(derivedClass, return);
diagramSceneController->createAssociation(
derivedClass, assoziatedClass, intermediatePoints, diagramSceneModel()->diagram(),
[=] (MAssociation *mAssociation, DAssociation *dAssociation) {
if (mAssociation && dAssociation) {
static const QHash<CustomRelation::Relationship, MAssociationEnd::Kind> relationship2KindMap = {
{ CustomRelation::Relationship::Association, MAssociationEnd::Association },
{ CustomRelation::Relationship::Aggregation, MAssociationEnd::Aggregation },
{ CustomRelation::Relationship::Composition, MAssociationEnd::Composition } };
diagramSceneController->modelController()->startUpdateRelation(mAssociation);
mAssociation->setStereotypes(customRelation.stereotypes().toList());
mAssociation->setName(customRelation.name());
MAssociationEnd endA;
endA.setCardinality(customRelation.endA().cardinality());
endA.setKind(relationship2KindMap.value(customRelation.endA().relationship()));
endA.setName(customRelation.endA().role());
endA.setNavigable(customRelation.endA().navigable());
mAssociation->setEndA(endA);
MAssociationEnd endB;
endB.setCardinality(customRelation.endB().cardinality());
endB.setKind(relationship2KindMap.value(customRelation.endB().relationship()));
endB.setName(customRelation.endB().role());
endB.setNavigable(customRelation.endB().navigable());
mAssociation->setEndB(endB);
diagramSceneController->modelController()->finishUpdateRelation(mAssociation, false);
}
});
}
return;
}
case CustomRelation::Element::Dependency:
case CustomRelation::Element::Relation:
// fall thru
break;
}
}
}
ObjectItem::relationDrawn(id, targetItem, intermediatePoints);
}
bool ClassItem::extendContextMenu(QMenu *menu)
@@ -338,11 +397,64 @@ void ClassItem::setFromDisplayName(const QString &displayName)
}
}
void ClassItem::updateRelationStarterTools(RelationStarter *relationStarter)
void ClassItem::addRelationStarterTool(const QString &id)
{
relationStarter->addArrow("dependency", ArrowItem::ShaftDashed, ArrowItem::HeadOpen);
relationStarter->addArrow("inheritance", ArrowItem::ShaftSolid, ArrowItem::HeadTriangle);
relationStarter->addArrow("association", ArrowItem::ShaftSolid, ArrowItem::HeadFilledTriangle);
if (id == INHERITANCE)
relationStarter()->addArrow(INHERITANCE, ArrowItem::ShaftSolid,
ArrowItem::HeadNone, ArrowItem::HeadTriangle,
tr("Inheritance"));
else if (id == ASSOCIATION)
relationStarter()->addArrow(ASSOCIATION, ArrowItem::ShaftSolid,
ArrowItem::HeadNone, ArrowItem::HeadFilledTriangle,
tr("Association"));
else
ObjectItem::addRelationStarterTool(id);
}
void ClassItem::addRelationStarterTool(const CustomRelation &customRelation)
{
ArrowItem::Shaft shaft = ArrowItem::ShaftSolid;
ArrowItem::Head headStart = ArrowItem::HeadNone;
ArrowItem::Head headEnd = ArrowItem::HeadNone;
switch (customRelation.element()) {
case CustomRelation::Element::Inheritance:
shaft = ArrowItem::ShaftSolid;
headEnd = ArrowItem::HeadTriangle;
break;
case CustomRelation::Element::Association:
switch (customRelation.endA().relationship()) {
case CustomRelation::Relationship::Association:
if (customRelation.endA().navigable() && customRelation.endB().navigable()) {
headStart = ArrowItem::HeadNone;
headEnd = ArrowItem::HeadNone;
} else if (customRelation.endA().navigable()) {
headStart = ArrowItem::HeadFilledTriangle;
} else {
headEnd = ArrowItem::HeadFilledTriangle;
}
break;
case CustomRelation::Relationship::Aggregation:
headStart = ArrowItem::HeadDiamond;
break;
case CustomRelation::Relationship::Composition:
headStart = ArrowItem::HeadFilledDiamond;
break;
}
break;
case CustomRelation::Element::Dependency:
case CustomRelation::Element::Relation:
ObjectItem::addRelationStarterTool(customRelation);
return;
}
relationStarter()->addArrow(customRelation.id(), shaft, headStart, headEnd,
customRelation.title());
}
void ClassItem::addStandardRelationStarterTools()
{
ObjectItem::addStandardRelationStarterTools();
addRelationStarterTool(INHERITANCE);
addRelationStarterTool(ASSOCIATION);
}
DClass::TemplateDisplay ClassItem::templateDisplay() const

View File

@@ -59,7 +59,7 @@ public:
QSizeF minimumSize() const override;
void relationDrawn(const QString &id, const QPointF &toScenePos,
void relationDrawn(const QString &id, ObjectItem *targetElement,
const QList<QPointF> &intermediatePoints) override;
protected:
@@ -67,7 +67,9 @@ protected:
bool handleSelectedContextMenuAction(const QString &id) override;
QString buildDisplayName() const override;
void setFromDisplayName(const QString &displayName) override;
void updateRelationStarterTools(RelationStarter *relationStarter) override;
void addRelationStarterTool(const QString &id) override;
void addRelationStarterTool(const CustomRelation &customRelation) override;
void addStandardRelationStarterTools() override;
private:
DClass::TemplateDisplay templateDisplay() const;

View File

@@ -32,7 +32,6 @@
#include "qmt/diagram_scene/parts/contextlabelitem.h"
#include "qmt/diagram_scene/parts/customiconitem.h"
#include "qmt/diagram_scene/parts/editabletextitem.h"
#include "qmt/diagram_scene/parts/relationstarter.h"
#include "qmt/diagram_scene/parts/stereotypesitem.h"
#include "qmt/infrastructure/geometryutilities.h"
#include "qmt/infrastructure/qmtassert.h"
@@ -61,7 +60,7 @@ static const qreal BODY_VERT_BORDER = 4.0;
static const qreal BODY_HORIZ_BORDER = 4.0;
ComponentItem::ComponentItem(DComponent *component, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
: ObjectItem(component, diagramSceneModel, parent)
: ObjectItem(QStringLiteral("component"), component, diagramSceneModel, parent)
{
}
@@ -154,22 +153,7 @@ void ComponentItem::update()
}
updateSelectionMarker(m_customIcon);
// relation starters
if (isFocusSelected()) {
if (!m_relationStarter && scene()) {
m_relationStarter = new RelationStarter(this, diagramSceneModel(), 0);
scene()->addItem(m_relationStarter);
m_relationStarter->setZValue(RELATION_STARTER_ZVALUE);
m_relationStarter->addArrow(QStringLiteral("dependency"), ArrowItem::ShaftDashed, ArrowItem::HeadOpen);
}
} else if (m_relationStarter) {
if (m_relationStarter->scene())
m_relationStarter->scene()->removeItem(m_relationStarter);
delete m_relationStarter;
m_relationStarter = 0;
}
updateRelationStarter();
updateAlignmentButtons();
updateGeometry();
}
@@ -344,10 +328,7 @@ void ComponentItem::updateGeometry()
}
updateSelectionMarkerGeometry(rect);
if (m_relationStarter)
m_relationStarter->setPos(mapToScene(QPointF(right + 8.0, top)));
updateRelationStarterGeometry(rect);
updateAlignmentButtonsGeometry(rect);
updateDepth();
}

View File

@@ -68,7 +68,6 @@ private:
QGraphicsRectItem *m_upperRect = 0;
QGraphicsRectItem *m_lowerRect = 0;
ContextLabelItem *m_contextLabel = 0;
RelationStarter *m_relationStarter = 0;
};
} // namespace qmt

View File

@@ -54,7 +54,7 @@ static const qreal BODY_HORIZ_BORDER = 4.0;
static const qreal BODY_VERT_BORDER = 4.0;
DiagramItem::DiagramItem(DDiagram *diagram, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
: ObjectItem(diagram, diagramSceneModel, parent)
: ObjectItem(QStringLiteral("diagram"), diagram, diagramSceneModel, parent)
{
}

View File

@@ -32,7 +32,6 @@
#include "qmt/diagram_scene/parts/contextlabelitem.h"
#include "qmt/diagram_scene/parts/customiconitem.h"
#include "qmt/diagram_scene/parts/editabletextitem.h"
#include "qmt/diagram_scene/parts/relationstarter.h"
#include "qmt/diagram_scene/parts/stereotypesitem.h"
#include "qmt/infrastructure/geometryutilities.h"
#include "qmt/infrastructure/qmtassert.h"
@@ -56,7 +55,7 @@ static const qreal BODY_VERT_BORDER = 4.0;
static const qreal BODY_HORIZ_BORDER = 4.0;
ItemItem::ItemItem(DItem *item, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
: ObjectItem(item, diagramSceneModel, parent)
: ObjectItem(QStringLiteral("item"), item, diagramSceneModel, parent)
{
}
@@ -124,22 +123,7 @@ void ItemItem::update()
}
updateSelectionMarker(m_customIcon);
// relation starters
if (isFocusSelected()) {
if (!m_relationStarter && scene()) {
m_relationStarter = new RelationStarter(this, diagramSceneModel(), 0);
scene()->addItem(m_relationStarter);
m_relationStarter->setZValue(RELATION_STARTER_ZVALUE);
m_relationStarter->addArrow(QStringLiteral("dependency"), ArrowItem::ShaftDashed, ArrowItem::HeadOpen);
}
} else if (m_relationStarter) {
if (m_relationStarter->scene())
m_relationStarter->scene()->removeItem(m_relationStarter);
delete m_relationStarter;
m_relationStarter = 0;
}
updateRelationStarter();
updateAlignmentButtons();
updateGeometry();
}
@@ -277,10 +261,7 @@ void ItemItem::updateGeometry()
}
updateSelectionMarkerGeometry(rect);
if (m_relationStarter)
m_relationStarter->setPos(mapToScene(QPointF(right + 8.0, top)));
updateRelationStarterGeometry(rect);
updateAlignmentButtonsGeometry(rect);
updateDepth();
}

View File

@@ -64,7 +64,6 @@ private:
CustomIconItem *m_customIcon = 0;
QGraphicsRectItem *m_shape = 0;
ContextLabelItem *m_contextLabel = 0;
RelationStarter *m_relationStarter = 0;
};
} // namespace qmt

View File

@@ -42,7 +42,9 @@
#include "qmt/model/mdiagram.h"
#include "qmt/model/mobject.h"
#include "qmt/model_controller/modelcontroller.h"
#include "qmt/stereotype/customrelation.h"
#include "qmt/stereotype/stereotypecontroller.h"
#include "qmt/stereotype/toolbar.h"
#include "qmt/style/style.h"
#include "qmt/style/stylecontroller.h"
#include "qmt/style/styledobject.h"
@@ -58,8 +60,12 @@
namespace qmt {
ObjectItem::ObjectItem(DObject *object, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
static const char DEPENDENCY[] = "dependency";
ObjectItem::ObjectItem(const QString &elementType, DObject *object, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
: QGraphicsItem(parent),
m_elementType(elementType),
m_object(object),
m_diagramSceneModel(diagramSceneModel)
{
@@ -309,12 +315,58 @@ QPointF ObjectItem::relationStartPos() const
void ObjectItem::relationDrawn(const QString &id, const QPointF &toScenePos, const QList<QPointF> &intermediatePoints)
{
DElement *targetElement = diagramSceneModel()->findTopmostElement(toScenePos);
if (targetElement) {
if (id == QLatin1String("dependency")) {
auto dependantObject = dynamic_cast<DObject *>(targetElement);
if (dependantObject)
diagramSceneModel()->diagramSceneController()->createDependency(object(), dependantObject, intermediatePoints, diagramSceneModel()->diagram());
ObjectItem *targetItem = diagramSceneModel()->findTopmostObjectItem(toScenePos);
if (targetItem)
relationDrawn(id, targetItem, intermediatePoints);
}
void ObjectItem::relationDrawn(const QString &id, ObjectItem *targetItem, const QList<QPointF> &intermediatePoints)
{
DiagramSceneController *diagramSceneController = diagramSceneModel()->diagramSceneController();
if (id == DEPENDENCY) {
DObject *dependantObject = targetItem->object();
if (dependantObject)
diagramSceneController->createDependency(object(), dependantObject, intermediatePoints,
diagramSceneModel()->diagram());
} else {
StereotypeController *stereotypeController = diagramSceneModel()->stereotypeController();
CustomRelation customRelation = stereotypeController->findCustomRelation(id);
if (!customRelation.isNull()) {
switch (customRelation.element()) {
case CustomRelation::Element::Dependency:
{
DObject *dependantObject = targetItem->object();
if (dependantObject)
diagramSceneController->createDependency(object(), dependantObject, intermediatePoints,
diagramSceneModel()->diagram());
break;
}
case CustomRelation::Element::Relation:
{
DObject *relatedObject = targetItem->object();
if (relatedObject) {
// check if element is allowed as target
QList<QString> endItems = customRelation.endB().endItems();
if (endItems.isEmpty())
endItems = customRelation.endItems();
QString elementType;
if (!targetItem->stereotypeIconId().isEmpty())
elementType = targetItem->stereotypeIconId();
else if (!targetItem->shapeIconId().isEmpty())
elementType = targetItem->shapeIconId();
else
elementType = targetItem->elementType();
if (!endItems.contains(elementType)) {
return;
}
// create relation
}
break;
}
default:
// ignore other elements
break;
}
}
}
}
@@ -594,7 +646,29 @@ void ObjectItem::updateRelationStarter()
m_relationStarter = new RelationStarter(this, diagramSceneModel(), 0);
scene()->addItem(m_relationStarter);
m_relationStarter->setZValue(RELATION_STARTER_ZVALUE);
updateRelationStarterTools(m_relationStarter);
QString elementType;
if (!m_stereotypeIconId.isEmpty())
elementType = m_stereotypeIconId;
else if (!m_shapeIconId.isEmpty())
elementType = m_shapeIconId;
else
elementType = m_elementType;
StereotypeController *stereotypeController = diagramSceneModel()->stereotypeController();
QList<Toolbar> toolbars = stereotypeController->findToolbars(elementType);
if (!toolbars.isEmpty()) {
foreach (const Toolbar &toolbar, toolbars) {
foreach (const Toolbar::Tool &tool, toolbar.tools()) {
CustomRelation customRelation =
stereotypeController->findCustomRelation(tool.m_elementType);
if (!customRelation.isNull())
addRelationStarterTool(customRelation);
else
addRelationStarterTool(tool.m_elementType);
}
}
} else {
addStandardRelationStarterTools();
}
}
} else if (m_relationStarter) {
scene()->removeItem(m_relationStarter);
@@ -604,9 +678,71 @@ void ObjectItem::updateRelationStarter()
}
void ObjectItem::updateRelationStarterTools(RelationStarter *relationStarter)
void ObjectItem::addRelationStarterTool(const QString &id)
{
relationStarter->addArrow(QLatin1String("dependency"), ArrowItem::ShaftDashed, ArrowItem::HeadOpen);
if (id == DEPENDENCY)
m_relationStarter->addArrow(DEPENDENCY, ArrowItem::ShaftDashed,
ArrowItem::HeadNone, ArrowItem::HeadOpen,
tr("Dependency"));
}
void ObjectItem::addRelationStarterTool(const CustomRelation &customRelation)
{
ArrowItem::Shaft shaft = ArrowItem::ShaftSolid;
ArrowItem::Head headStart = ArrowItem::HeadNone;
ArrowItem::Head headEnd = ArrowItem::HeadNone;
switch (customRelation.element()) {
case CustomRelation::Element::Dependency:
shaft = ArrowItem::ShaftDashed;
switch (customRelation.direction()) {
case CustomRelation::Direction::AtoB:
headEnd = ArrowItem::HeadOpen;
break;
case CustomRelation::Direction::BToA:
headStart = ArrowItem::HeadOpen;
break;
case CustomRelation::Direction::Bi:
headStart = ArrowItem::HeadOpen;
headEnd = ArrowItem::HeadOpen;
break;
}
break;
case CustomRelation::Element::Relation:
{
// TODO support custom shapes
static const QHash<CustomRelation::ShaftPattern, ArrowItem::Shaft> shaft2shaft = {
{ CustomRelation::ShaftPattern::Solid, ArrowItem::ShaftSolid },
{ CustomRelation::ShaftPattern::Dash, ArrowItem::ShaftDashed },
{ CustomRelation::ShaftPattern::Dot, ArrowItem::ShaftDot },
{ CustomRelation::ShaftPattern::DashDot, ArrowItem::ShaftDashDot },
{ CustomRelation::ShaftPattern::DashDotDot, ArrowItem::ShaftDashDotDot },
};
static const QHash<CustomRelation::Head, ArrowItem::Head> head2head = {
{ CustomRelation::Head::None, ArrowItem::HeadNone },
{ CustomRelation::Head::Shape, ArrowItem::HeadNone },
{ CustomRelation::Head::Arrow, ArrowItem::HeadOpen },
{ CustomRelation::Head::Triangle, ArrowItem::HeadTriangle },
{ CustomRelation::Head::FilledTriangle, ArrowItem::HeadFilledTriangle },
{ CustomRelation::Head::Diamond, ArrowItem::HeadDiamond },
{ CustomRelation::Head::FilledDiamond, ArrowItem::HeadFilledDiamond },
};
shaft = shaft2shaft.value(customRelation.shaftPattern());
headStart = head2head.value(customRelation.endA().head());
headEnd = head2head.value(customRelation.endB().head());
// TODO use color?
break;
}
default:
return;
}
m_relationStarter->addArrow(customRelation.id(), shaft, headStart, headEnd,
customRelation.title());
}
void ObjectItem::addStandardRelationStarterTools()
{
addRelationStarterTool(DEPENDENCY);
}
void ObjectItem::updateRelationStarterGeometry(const QRectF &objectRect)

View File

@@ -46,10 +46,12 @@ QT_END_NAMESPACE
namespace qmt {
class DElement;
class DObject;
class DiagramSceneModel;
class StereotypesItem;
class CustomIconItem;
class CustomRelation;
class EditableTextItem;
class RectangularSelectionItem;
class RelationStarter;
@@ -84,9 +86,10 @@ protected:
};
public:
ObjectItem(DObject *object, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent = 0);
ObjectItem(const QString &elementType, DObject *object, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent = 0);
~ObjectItem() override;
QString elementType() const { return m_elementType; }
DObject *object() const { return m_object; }
DiagramSceneModel *diagramSceneModel() const { return m_diagramSceneModel; }
@@ -119,6 +122,8 @@ public:
QPointF relationStartPos() const override;
void relationDrawn(const QString &id, const QPointF &toScenePos,
const QList<QPointF> &intermediatePoints) override;
virtual void relationDrawn(const QString &id, ObjectItem *targetElement,
const QList<QPointF> &intermediatePoints);
void align(AlignType alignType, const QString &identifier) override;
@@ -147,7 +152,10 @@ protected:
void updateSelectionMarker(ResizeFlags resizeFlags);
void updateSelectionMarkerGeometry(const QRectF &objectRect);
void updateRelationStarter();
virtual void updateRelationStarterTools(RelationStarter *relationStarter);
RelationStarter *relationStarter() const { return m_relationStarter; }
virtual void addRelationStarterTool(const QString &id);
virtual void addRelationStarterTool(const CustomRelation &customRelation);
virtual void addStandardRelationStarterTools();
void updateRelationStarterGeometry(const QRectF &objectRect);
void updateAlignmentButtons();
void updateAlignmentButtonsGeometry(const QRectF &objectRect);
@@ -168,6 +176,7 @@ protected:
private:
QSizeF minimumSize(const QSet<QGraphicsItem *> &items) const;
QString m_elementType;
DObject *m_object = 0;
DiagramSceneModel *m_diagramSceneModel = 0;
bool m_isSecondarySelected = false;

View File

@@ -32,7 +32,6 @@
#include "qmt/diagram_scene/parts/contextlabelitem.h"
#include "qmt/diagram_scene/parts/customiconitem.h"
#include "qmt/diagram_scene/parts/editabletextitem.h"
#include "qmt/diagram_scene/parts/relationstarter.h"
#include "qmt/diagram_scene/parts/stereotypesitem.h"
#include "qmt/infrastructure/geometryutilities.h"
#include "qmt/stereotype/stereotypecontroller.h"
@@ -72,7 +71,7 @@ public:
};
PackageItem::PackageItem(DPackage *package, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
: ObjectItem(package, diagramSceneModel, parent)
: ObjectItem(QStringLiteral("package"), package, diagramSceneModel, parent)
{
}
@@ -135,21 +134,7 @@ void PackageItem::update()
}
updateSelectionMarker(m_customIcon);
// relation starters
if (isFocusSelected()) {
if (!m_relationStarter) {
m_relationStarter = new RelationStarter(this, diagramSceneModel(), 0);
scene()->addItem(m_relationStarter);
m_relationStarter->setZValue(RELATION_STARTER_ZVALUE);
m_relationStarter->addArrow(QStringLiteral("dependency"), ArrowItem::ShaftDashed, ArrowItem::HeadOpen);
}
} else if (m_relationStarter) {
scene()->removeItem(m_relationStarter);
delete m_relationStarter;
m_relationStarter = 0;
}
updateRelationStarter();
updateAlignmentButtons();
updateGeometry();
}
@@ -326,10 +311,7 @@ void PackageItem::updateGeometry()
}
updateSelectionMarkerGeometry(rect);
if (m_relationStarter)
m_relationStarter->setPos(mapToScene(QPointF(right + 8.0, top)));
updateRelationStarterGeometry(rect);
updateAlignmentButtonsGeometry(rect);
updateDepth();
}

View File

@@ -65,7 +65,6 @@ private:
CustomIconItem *m_customIcon = 0;
QGraphicsPolygonItem *m_shape = 0;
ContextLabelItem *m_contextLabel = 0;
RelationStarter *m_relationStarter = 0;
};
} // namespace qmt

View File

@@ -38,6 +38,8 @@
#include <QPainter>
#include <QPen>
//#define DEBUG_PAINT_SHAPE
namespace qmt {
class ArrowItem::GraphicsHeadItem : public QGraphicsItem
@@ -110,6 +112,7 @@ public:
double length = 0.0;
switch (m_head) {
case ArrowItem::HeadNone:
case ArrowItem::HeadCustom:
break;
case ArrowItem::HeadOpen:
case ArrowItem::HeadTriangle:
@@ -146,6 +149,7 @@ public:
bool hasDiamond = false;
switch (m_head) {
case ArrowItem::HeadNone:
case ArrowItem::HeadCustom:
break;
case ArrowItem::HeadOpen:
case ArrowItem::HeadTriangle:
@@ -236,7 +240,6 @@ public:
ArrowItem::ArrowItem(QGraphicsItem *parent)
: QGraphicsItem(parent),
m_shaft(ShaftSolid),
m_shaftItem(new GraphicsShaftItem(this))
{
}
@@ -248,9 +251,7 @@ ArrowItem::ArrowItem(const ArrowItem &rhs, QGraphicsItem *parent)
m_arrowSize(rhs.m_arrowSize),
m_diamondSize(rhs.m_diamondSize),
m_startHead(rhs.m_startHead),
m_startHeadItem(nullptr),
m_endHead(rhs.m_endHead),
m_endHeadItem(nullptr)
m_endHead(rhs.m_endHead)
{
}
@@ -278,14 +279,36 @@ void ArrowItem::setDiamondSize(double diamondSize)
void ArrowItem::setStartHead(ArrowItem::Head head)
{
if (m_startHead != head)
m_startHead = head;
m_startHead = head;
}
void ArrowItem::setStartHead(QGraphicsItem *startHeadItem)
{
deleteHead(&m_startHeadItem);
if (!startHeadItem) {
m_startHead = HeadNone;
} else {
QTC_ASSERT(startHeadItem->parentItem() == this, return);
m_startHead = HeadCustom;
m_startHeadItem = startHeadItem;
}
}
void ArrowItem::setEndHead(ArrowItem::Head head)
{
if (m_endHead != head)
m_endHead = head;
m_endHead = head;
}
void ArrowItem::setEndHead(QGraphicsItem *endHeadItem)
{
deleteHead(&m_endHeadItem);
if (!endHeadItem) {
m_endHead = HeadNone;
} else {
QTC_ASSERT(endHeadItem->parentItem() == this, return);
m_endHead = HeadCustom;
m_endHeadItem = endHeadItem;
}
}
void ArrowItem::setPoints(const QList<QPointF> &points)
@@ -359,14 +382,14 @@ QLineF ArrowItem::lastLineSegment() const
double ArrowItem::startHeadLength() const
{
if (m_startHeadItem)
return m_startHeadItem->calcHeadLength();
return calcHeadLength(m_startHeadItem);
return 0.0;
}
double ArrowItem::endHeadLength() const
{
if (m_endHeadItem)
return m_endHeadItem->calcHeadLength();
return calcHeadLength(m_endHeadItem);
return 0.0;
}
@@ -383,43 +406,79 @@ void ArrowItem::updateShaft(const Style *style)
QMT_ASSERT(m_shaftItem, return);
QPen pen(style->linePen());
if (m_shaft == ShaftDashed)
pen.setDashPattern(QVector<qreal>() << (4.0 / pen.widthF()) << (4.0 / pen.widthF()));
switch (m_shaft) {
case ShaftSolid:
break;
case ShaftDashed:
pen.setDashPattern(QVector<qreal>()
<< (4.0 / pen.widthF()) << (4.0 / pen.widthF()));
break;
case ShaftDot:
pen.setDashPattern(QVector<qreal>()
<< (2.0 / pen.widthF()) << (2.0 / pen.widthF()));
break;
case ShaftDashDot:
pen.setDashPattern(QVector<qreal>()
<< (4.0 / pen.widthF()) << (2.0 / pen.widthF())
<< (2.0 / pen.widthF()) << (2.0 / pen.widthF()));
break;
case ShaftDashDotDot:
pen.setDashPattern(QVector<qreal>()
<< (4.0 / pen.widthF()) << (2.0 / pen.widthF())
<< (2.0 / pen.widthF()) << (2.0 / pen.widthF())
<< (2.0 / pen.widthF()) << (2.0 / pen.widthF()));
break;
}
m_shaftItem->setPen(pen);
}
void ArrowItem::updateHead(GraphicsHeadItem **headItem, Head head, const Style *style)
void ArrowItem::deleteHead(QGraphicsItem **headItem)
{
if (head == HeadNone) {
if (*headItem) {
if ((*headItem)->scene())
(*headItem)->scene()->removeItem(*headItem);
delete *headItem;
*headItem = 0;
}
return;
if (*headItem) {
if ((*headItem)->scene())
(*headItem)->scene()->removeItem(*headItem);
delete *headItem;
*headItem = 0;
}
if (!*headItem)
*headItem = new GraphicsHeadItem(this);
(*headItem)->setArrowSize(m_arrowSize);
(*headItem)->setDiamondSize(m_diamondSize);
(*headItem)->setHead(head);
(*headItem)->update(style);
}
void ArrowItem::updateHeadGeometry(GraphicsHeadItem **headItem, const QPointF &pos, const QPointF &otherPos)
void ArrowItem::updateHead(QGraphicsItem **headItem, Head head, const Style *style)
{
if (!*headItem)
if (head == HeadNone) {
deleteHead(headItem);
} else if (head == HeadCustom) {
// nothing to do
} else {
QTC_ASSERT(*headItem == 0 || dynamic_cast<GraphicsHeadItem *>(*headItem) != 0, return);
GraphicsHeadItem *item;
if (!*headItem) {
item = new GraphicsHeadItem(this);
*headItem = item;
} else {
item = dynamic_cast<GraphicsHeadItem *>(*headItem);
if (!item)
return;
}
item->setArrowSize(m_arrowSize);
item->setDiamondSize(m_diamondSize);
item->setHead(head);
item->update(style);
}
}
void ArrowItem::updateHeadGeometry(QGraphicsItem *headItem, const QPointF &pos, const QPointF &otherPos)
{
if (!headItem)
return;
(*headItem)->setPos(pos);
headItem->setPos(pos);
QVector2D directionVector(pos - otherPos);
directionVector.normalize();
double angle = qAcos(directionVector.x()) * 180.0 / 3.1415926535;
if (directionVector.y() > 0.0)
angle = -angle;
(*headItem)->setRotation(-angle);
headItem->setRotation(-angle);
}
void ArrowItem::updateGeometry()
@@ -434,8 +493,8 @@ void ArrowItem::updateGeometry()
if (m_startHeadItem) {
QVector2D startDirectionVector(m_points.at(1) - m_points.at(0));
startDirectionVector.normalize();
startDirectionVector *= m_startHeadItem->calcHeadLength();
path.moveTo(m_points.at(0) + startDirectionVector.toPointF());
startDirectionVector *= calcHeadLength(m_startHeadItem);
path.moveTo(m_points[0] + startDirectionVector.toPointF());
} else {
path.moveTo(m_points.at(0));
}
@@ -446,16 +505,24 @@ void ArrowItem::updateGeometry()
if (m_endHeadItem) {
QVector2D endDirectionVector(m_points.at(m_points.size() - 1) - m_points.at(m_points.size() - 2));
endDirectionVector.normalize();
endDirectionVector *= m_endHeadItem->calcHeadLength();
path.lineTo(m_points.at(m_points.size() - 1) - endDirectionVector.toPointF());
endDirectionVector *= calcHeadLength(m_endHeadItem);
path.lineTo(m_points[m_points.size() - 1] - endDirectionVector.toPointF());
} else {
path.lineTo(m_points.at(m_points.size() - 1));
}
m_shaftItem->setPath(path);
updateHeadGeometry(&m_startHeadItem, m_points.at(0), m_points.at(1));
updateHeadGeometry(&m_endHeadItem, m_points.at(m_points.size() - 1), m_points.at(m_points.size() - 2));
updateHeadGeometry(m_startHeadItem, m_points.at(0), m_points.at(1));
updateHeadGeometry(m_endHeadItem, m_points.at(m_points.size() - 1), m_points.at(m_points.size() - 2));
}
double ArrowItem::calcHeadLength(QGraphicsItem *headItem) const
{
// TODO use an interface
if (GraphicsHeadItem *item = dynamic_cast<GraphicsHeadItem *>(headItem))
return item->calcHeadLength();
return 100.0;
}
} // namespace qmt

View File

@@ -43,11 +43,15 @@ class ArrowItem : public QGraphicsItem
public:
enum Shaft {
ShaftSolid,
ShaftDashed
ShaftDashed,
ShaftDot,
ShaftDashDot,
ShaftDashDotDot
};
enum Head {
HeadNone,
HeadCustom,
HeadOpen,
HeadTriangle,
HeadFilledTriangle,
@@ -65,7 +69,9 @@ public:
void setArrowSize(double arrowSize);
void setDiamondSize(double diamondSize);
void setStartHead(Head head);
void setStartHead(QGraphicsItem *startHeadItem);
void setEndHead(Head head);
void setEndHead(QGraphicsItem *endHeadItem);
void setPoints(const QList<QPointF> &points);
QRectF boundingRect() const override;
@@ -82,19 +88,21 @@ public:
private:
void updateShaft(const Style *style);
void updateHead(GraphicsHeadItem **headItem, Head head, const Style *style);
void updateHeadGeometry(GraphicsHeadItem **headItem, const QPointF &pos,
void deleteHead(QGraphicsItem **headItem);
void updateHead(QGraphicsItem **headItem, Head head, const Style *style);
void updateHeadGeometry(QGraphicsItem *headItem, const QPointF &pos,
const QPointF &otherPos);
void updateGeometry();
double calcHeadLength(QGraphicsItem *headItem) const;
Shaft m_shaft = ShaftSolid;
GraphicsShaftItem *m_shaftItem = nullptr;
double m_arrowSize = 10.0;
double m_diamondSize = 15.0;
Head m_startHead = HeadNone;
GraphicsHeadItem *m_startHeadItem = nullptr;
QGraphicsItem *m_startHeadItem = nullptr;
Head m_endHead = HeadNone;
GraphicsHeadItem *m_endHeadItem = nullptr;
QGraphicsItem *m_endHeadItem = nullptr;
QList<QPointF> m_points;
};

View File

@@ -70,16 +70,19 @@ void RelationStarter::paint(QPainter *painter, const QStyleOptionGraphicsItem *o
}
void RelationStarter::addArrow(const QString &id, ArrowItem::Shaft shaft,
ArrowItem::Head endHead, ArrowItem::Head startHead)
ArrowItem::Head startHead, ArrowItem::Head endHead,
const QString &toolTip)
{
QMT_CHECK(!id.isEmpty());
prepareGeometryChange();
auto arrow = new ArrowItem(this);
arrow->setArrowSize(10.0);
arrow->setDiamondSize(15.0);
arrow->setDiamondSize(8.0);
arrow->setShaft(shaft);
arrow->setStartHead(startHead);
arrow->setEndHead(endHead);
if (!toolTip.isEmpty())
arrow->setToolTip(toolTip);
arrow->setPoints(QList<QPointF>() << QPointF(0.0, 10.0) << QPointF(15.0, 0.0));
arrow->setPos(6.0, m_arrows.size() * 20.0 + 8.0);
arrow->update(m_diagramSceneModel->styleController()->relationStarterStyle());
@@ -99,6 +102,9 @@ void RelationStarter::mousePressEvent(QGraphicsSceneMouseEvent *event)
m_currentPreviewArrowId = m_arrowIds.value(item);
QMT_CHECK(!m_currentPreviewArrowId.isEmpty());
m_currentPreviewArrow = new ArrowItem(*item);
// TODO use constants for sizes (in relationitem.h also)
m_currentPreviewArrow->setArrowSize(12.0);
m_currentPreviewArrow->setDiamondSize(12.0);
m_currentPreviewArrow->setPoints(QList<QPointF>() << m_owner->relationStartPos() << mapToScene(event->pos()));
m_currentPreviewArrow->update(m_diagramSceneModel->styleController()->relationStarterStyle());
m_currentPreviewArrow->setZValue(PREVIEW_RELATION_ZVALUE);

View File

@@ -47,8 +47,9 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget = 0) override;
void addArrow(const QString &id, ArrowItem::Shaft shaft, ArrowItem::Head endHead,
ArrowItem::Head startHead = ArrowItem::HeadNone);
void addArrow(const QString &id, ArrowItem::Shaft shaft, ArrowItem::Head startHead,
ArrowItem::Head endHead,
const QString &toolTip = QString());
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;

View File

@@ -36,6 +36,7 @@
#include "qmt/diagram/dpackage.h"
#include "qmt/diagram/ditem.h"
#include "qmt/diagram/drelation.h"
#include "qmt/diagram/dassociation.h"
#include "qmt/diagram_ui/diagram_mime_types.h"
#include "qmt/model_controller/modelcontroller.h"
#include "qmt/model_controller/mselection.h"
@@ -221,7 +222,8 @@ void DiagramSceneController::createInheritance(DClass *derivedClass, DClass *bas
}
void DiagramSceneController::createAssociation(DClass *endAClass, DClass *endBClass,
const QList<QPointF> &intermediatePoints, MDiagram *diagram)
const QList<QPointF> &intermediatePoints, MDiagram *diagram,
std::function<void (MAssociation*, DAssociation*)> custom)
{
m_diagramController->undoController()->beginMergeSequence(tr("Create Association"));
@@ -243,6 +245,11 @@ void DiagramSceneController::createAssociation(DClass *endAClass, DClass *endBCl
m_modelController->addRelation(endAModelObject, modelAssociation);
DRelation *relation = addRelation(modelAssociation, intermediatePoints, diagram);
DAssociation *diagramAssociation = dynamic_cast<DAssociation *>(relation);
QMT_CHECK(diagramAssociation);
if (custom)
custom(modelAssociation, diagramAssociation);
m_diagramController->undoController()->endMergeSequence();

View File

@@ -28,6 +28,8 @@
#include <QObject>
#include "qmt/infrastructure/qmt_global.h"
#include <functional>
QT_BEGIN_NAMESPACE
class QPointF;
QT_END_NAMESPACE
@@ -41,10 +43,12 @@ class MObject;
class MPackage;
class MDiagram;
class MRelation;
class MAssociation;
class DElement;
class DObject;
class DClass;
class DRelation;
class DAssociation;
class DSelection;
class IElementTasks;
class ISceneInspector;
@@ -80,7 +84,8 @@ public:
void createInheritance(DClass *derivedClass, DClass *baseClass,
const QList<QPointF> &intermediatePoints, MDiagram *diagram);
void createAssociation(DClass *endAClass, DClass *endBClass,
const QList<QPointF> &intermediatePoints, MDiagram *diagram);
const QList<QPointF> &intermediatePoints, MDiagram *diagram,
std::function<void (MAssociation*, DAssociation*)> custom = 0);
bool relocateRelationEndA(DRelation *relation, DObject *targetObject);
bool relocateRelationEndB(DRelation *relation, DObject *targetObject);