diff options
author | Jochen Becher <jochen_becher@gmx.de> | 2024-04-11 16:52:18 +0200 |
---|---|---|
committer | Jochen Becher <jochen_becher@gmx.de> | 2024-04-16 08:19:38 +0000 |
commit | 98bf52c2d693fd03972d7f3e189ab3d99f21b9f5 (patch) | |
tree | e72ad58d1dbd5040dd40fe1f582bec5e4d25a890 | |
parent | b74f871b5be0475aa7b0a173cc825b1746de5ab1 (diff) |
ModelEditor: Support linked files and custom images
Change-Id: I1a5f8b19a4890e0bcbd5a2cf70fdee2cadf62a98
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
35 files changed, 893 insertions, 397 deletions
diff --git a/src/libs/modelinglib/qmt/diagram/dobject.cpp b/src/libs/modelinglib/qmt/diagram/dobject.cpp index 4e0e5cb642..68e954b857 100644 --- a/src/libs/modelinglib/qmt/diagram/dobject.cpp +++ b/src/libs/modelinglib/qmt/diagram/dobject.cpp @@ -25,7 +25,10 @@ DObject::DObject(const DObject &rhs) m_visualSecondaryRole(rhs.m_visualSecondaryRole), m_stereotypeDisplay(rhs.m_stereotypeDisplay), m_isAutoSized(rhs.m_isAutoSized), - m_isVisualEmphasized(rhs.m_isVisualEmphasized) + m_isVisualEmphasized(rhs.m_isVisualEmphasized), + m_hasLinkedFile(rhs.m_hasLinkedFile), + m_imagePath(rhs.m_imagePath), + m_image(rhs.m_image) { } @@ -49,6 +52,9 @@ DObject &DObject::operator =(const DObject &rhs) m_stereotypeDisplay = rhs.m_stereotypeDisplay; m_isAutoSized = rhs.m_isAutoSized; m_isVisualEmphasized = rhs.m_isVisualEmphasized; + m_hasLinkedFile = rhs.m_hasLinkedFile; + m_imagePath = rhs.m_imagePath; + m_image = rhs.m_image; } return *this; } @@ -113,6 +119,26 @@ void DObject::setVisualEmphasized(bool visualEmphasized) m_isVisualEmphasized = visualEmphasized; } +void DObject::setLinkedFile(bool linkedFile) +{ + m_hasLinkedFile = linkedFile; +} + +bool DObject::hasImage() const +{ + return !m_image.isNull(); +} + +void DObject::setImagePath(const QString &path) +{ + m_imagePath = path; +} + +void DObject::setImage(const QImage &image) +{ + m_image = image; +} + void DObject::accept(DVisitor *visitor) { visitor->visitDObject(this); diff --git a/src/libs/modelinglib/qmt/diagram/dobject.h b/src/libs/modelinglib/qmt/diagram/dobject.h index c8cf2b0e4d..bf43edea12 100644 --- a/src/libs/modelinglib/qmt/diagram/dobject.h +++ b/src/libs/modelinglib/qmt/diagram/dobject.h @@ -7,6 +7,7 @@ #include "qmt/infrastructure/uid.h" +#include <QImage> #include <QList> #include <QPointF> #include <QRectF> @@ -78,6 +79,13 @@ public: void setAutoSized(bool autoSized); bool isVisualEmphasized() const { return m_isVisualEmphasized; } void setVisualEmphasized(bool visualEmphasized); + bool hasLinkedFile() const { return m_hasLinkedFile; } + void setLinkedFile(bool linkedFile); + QString imagePath() const { return m_imagePath; } + void setImagePath(const QString &path); + bool hasImage() const; + QImage image() const { return m_image; } + void setImage(const QImage &image); void accept(DVisitor *visitor) override; void accept(DConstVisitor *visitor) const override; @@ -95,6 +103,9 @@ private: StereotypeDisplay m_stereotypeDisplay = StereotypeSmart; bool m_isAutoSized = true; bool m_isVisualEmphasized = false; + bool m_hasLinkedFile = false; + QString m_imagePath; + QImage m_image; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp b/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp index 205c8fbb4d..5e4c072588 100644 --- a/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp +++ b/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp @@ -49,7 +49,10 @@ void DFlatAssignmentVisitor::visitDObject(const DObject *object) target->setVisualPrimaryRole(object->visualPrimaryRole()); target->setVisualSecondaryRole(object->visualSecondaryRole()); target->setVisualEmphasized(object->isVisualEmphasized()); + target->setLinkedFile(object->hasLinkedFile()); target->setStereotypeDisplay(object->stereotypeDisplay()); + target->setImagePath(object->imagePath()); + target->setImage(object->image()); } void DFlatAssignmentVisitor::visitDPackage(const DPackage *package) diff --git a/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp b/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp index 0866e6d9f5..2dd079e1bc 100644 --- a/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp +++ b/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp @@ -65,6 +65,9 @@ void DUpdateVisitor::visitMObject(const MObject *object) } if (isUpdating(object->name() != dobject->name())) dobject->setName(object->name()); + bool hasLinkedFile = !object->linkedFileName().isEmpty(); + if (isUpdating(hasLinkedFile != dobject->hasLinkedFile())) + dobject->setLinkedFile(hasLinkedFile); // TODO unlikely that this is called for all objects if hierarchy is modified // PERFORM remove loop int depth = 1; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp index 14ad25b2c1..23a9c524a2 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp @@ -85,29 +85,16 @@ void ClassItem::update() m_methodsText.clear(); } - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsRectItem(this); m_shape->setBrush(style->fillBrush()); m_shape->setPen(style->outerLinePen()); m_shape->setZValue(SHAPE_ZVALUE); - } else if (m_shape){ + } else if (m_shape) { m_shape->scene()->removeItem(m_shape); delete m_shape; m_shape = nullptr; @@ -259,7 +246,7 @@ void ClassItem::update() m_templateParameterBox = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -267,8 +254,8 @@ void ClassItem::update() bool ClassItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -508,7 +495,7 @@ DClass::TemplateDisplay ClassItem::templateDisplay() const DClass::TemplateDisplay templateDisplay = diagramClass->templateDisplay(); if (templateDisplay == DClass::TemplateSmart) { - if (m_customIcon) + if (customIconItem()) templateDisplay = DClass::TemplateName; else templateDisplay = DClass::TemplateBox; @@ -521,8 +508,8 @@ QSizeF ClassItem::calcMinimumGeometry() const double width = 0.0; double height = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) @@ -589,12 +576,7 @@ void ClassItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - if (!m_customIcon) { - if (width < MINIMUM_AUTO_WIDTH) - width = MINIMUM_AUTO_WIDTH; - if (height < MINIMUM_AUTO_HEIGHT) - height = MINIMUM_AUTO_HEIGHT; - } + correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -619,15 +601,15 @@ void ClassItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_shape) m_shape->setRect(rect); - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; @@ -679,7 +661,7 @@ void ClassItem::updateGeometry() y += nameItem()->boundingRect().height(); } if (m_contextLabel) { - if (m_customIcon) + if (customIconItem()) m_contextLabel->resetMaxWidth(); else m_contextLabel->setMaxWidth(width - 2 * BODY_HORIZ_BORDER); @@ -692,7 +674,7 @@ void ClassItem::updateGeometry() y += 8.0; } if (m_attributes) { - if (m_customIcon) + if (customIconItem()) m_attributes->setPos(-m_attributes->boundingRect().width() / 2.0, y); else m_attributes->setPos(left + BODY_HORIZ_BORDER, y); @@ -704,7 +686,7 @@ void ClassItem::updateGeometry() y += 8.0; } if (m_methods) { - if (m_customIcon) + if (customIconItem()) m_methods->setPos(-m_methods->boundingRect().width() / 2.0, y); else m_methods->setPos(left + BODY_HORIZ_BORDER, y); @@ -765,21 +747,22 @@ void ClassItem::updateMembers(const Style *style) break; } + bool needBr = false; if (text && !text->isEmpty()) - *text += "<br/>"; + needBr = true; - bool addNewline = false; - bool addSpace = false; if (currentVisibility) *currentVisibility = member.visibility(); if (currentGroup && member.group() != *currentGroup) { - *text += QString("[%1]").arg(member.group()); - addNewline = true; + needBr = false; + *text += QString("<p style=\"padding:0;margin-top:6;margin-bottom:0;\"><b>[%1]</b></p>").arg(member.group()); *currentGroup = member.group(); } - if (addNewline) + + if (needBr) *text += "<br/>"; + bool addSpace = false; bool haveSignal = false; bool haveSlot = false; if (member.visibility() != MClassMember::VisibilityUndefined) { diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h index 83ce0a9a5a..cc92ab1ff5 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h @@ -53,7 +53,6 @@ private: void updateGeometry(); void updateMembers(const Style *style); - CustomIconItem *m_customIcon = nullptr; QGraphicsRectItem *m_shape = nullptr; QGraphicsSimpleTextItem *m_baseClasses = nullptr; QGraphicsSimpleTextItem *m_namespace = nullptr; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp index 6a54a001e5..e7fffcbf9a 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp @@ -53,24 +53,11 @@ void ComponentItem::update() const Style *style = adaptedStyle(stereotypeIconId()); - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape bool deleteRects = false; - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsRectItem(this); m_shape->setBrush(style->fillBrush()); @@ -130,7 +117,7 @@ void ComponentItem::update() m_contextLabel = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -138,8 +125,8 @@ void ComponentItem::update() bool ComponentItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -207,14 +194,16 @@ QSizeF ComponentItem::calcMinimumGeometry() const { double width = 0.0; double height = 0.0; + double customMinHeight = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) return sz; width = sz.width(); + customMinHeight = sz.height(); } height += BODY_VERT_BORDER; @@ -234,7 +223,7 @@ QSizeF ComponentItem::calcMinimumGeometry() const height += m_contextLabel->height(); height += BODY_VERT_BORDER; - if (!hasPlainShape()) { + if (!customIconItem() && !hasPlainShape()) { width = RECT_WIDTH * 0.5 + BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER + RECT_WIDTH * 0.5; double minHeight = UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT + LOWER_RECT_MIN_Y; if (height < minHeight) @@ -243,6 +232,9 @@ QSizeF ComponentItem::calcMinimumGeometry() const width = BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER; } + if (height < customMinHeight) + height = customMinHeight; + return GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT); } @@ -259,7 +251,7 @@ void ComponentItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - // nothing + correctAutoSize(customIconItem(), width, height, 0, 0); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -283,9 +275,9 @@ void ComponentItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_shape) @@ -303,7 +295,7 @@ void ComponentItem::updateGeometry() m_lowerRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE); } - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; @@ -345,7 +337,7 @@ void ComponentItem::updateGeometry() y += nameItem()->boundingRect().height(); } if (m_contextLabel) { - if (m_customIcon) { + if (customIconItem()) { m_contextLabel->resetMaxWidth(); } else { double maxContextWidth = width - 2 * BODY_HORIZ_BORDER - (hasPlainShape() ? 0 : RECT_WIDTH); diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h index f00fbf30eb..f4910f53ce 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h @@ -41,7 +41,6 @@ private: QSizeF calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsRectItem *m_shape = nullptr; QGraphicsRectItem *m_upperRect = nullptr; QGraphicsRectItem *m_lowerRect = nullptr; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp index c37c38b3bb..951d6f3b1a 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp @@ -47,23 +47,10 @@ void DiagramItem::update() const Style *style = adaptedStyle(stereotypeIconId()); - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_body) m_body = new QGraphicsPolygonItem(this); m_body->setBrush(style->fillBrush()); @@ -93,15 +80,15 @@ void DiagramItem::update() // diagram name updateNameItem(style); - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateAlignmentButtons(); updateGeometry(); } bool DiagramItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -131,8 +118,8 @@ QSizeF DiagramItem::calcMinimumGeometry() const double width = MINIMUM_WIDTH; double height = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) @@ -175,12 +162,7 @@ void DiagramItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - if (!m_customIcon) { - if (width < MINIMUM_AUTO_WIDTH) - width = MINIMUM_AUTO_WIDTH; - if (height < MINIMUM_AUTO_HEIGHT) - height = MINIMUM_AUTO_HEIGHT; - } + correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -203,9 +185,9 @@ void DiagramItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_body) { @@ -227,7 +209,7 @@ void DiagramItem::updateGeometry() m_fold->setPolygon(foldPolygon); } - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h index 81012b04a1..88d9f9a7a8 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h @@ -29,7 +29,6 @@ private: QSizeF calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsPolygonItem *m_body = nullptr; QGraphicsPolygonItem *m_fold = nullptr; }; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp index 97a1b337fc..9855889b08 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp @@ -52,22 +52,10 @@ void ItemItem::update() const Style *style = adaptedStyle(shapeIconId()); - if (!shapeIconId().isEmpty()) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(shapeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsRectItem(this); m_shape->setBrush(style->fillBrush()); @@ -100,7 +88,7 @@ void ItemItem::update() m_contextLabel = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -108,8 +96,8 @@ void ItemItem::update() bool ItemItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -152,14 +140,16 @@ QSizeF ItemItem::calcMinimumGeometry() const { double width = 0.0; double height = 0.0; + double customMinHeight = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) return sz; width = sz.width(); + customMinHeight = sz.height(); } height += BODY_VERT_BORDER; @@ -181,6 +171,9 @@ QSizeF ItemItem::calcMinimumGeometry() const width = BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER; + if (height < customMinHeight) + height = customMinHeight; + return GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT); } @@ -197,7 +190,7 @@ void ItemItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - // nothing + correctAutoSize(customIconItem(), width, height, 0, 0); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -221,15 +214,15 @@ void ItemItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_shape) m_shape->setRect(rect); - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; @@ -271,7 +264,7 @@ void ItemItem::updateGeometry() y += nameItem()->boundingRect().height(); } if (m_contextLabel) { - if (m_customIcon) { + if (customIconItem()) { m_contextLabel->resetMaxWidth(); } else { double maxContextWidth = width - 2 * BODY_HORIZ_BORDER; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h index 327b3799ac..de091a2bc7 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h @@ -39,7 +39,6 @@ private: QSizeF calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsRectItem *m_shape = nullptr; ContextLabelItem *m_contextLabel = nullptr; }; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp index 0fe32080cf..5d747a6391 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp @@ -498,6 +498,7 @@ void ObjectItem::updateStereotypes(const QString &stereotypeIconId, StereotypeIc m_stereotypeIcon = new CustomIconItem(m_diagramSceneModel, this); m_stereotypeIcon->setStereotypeIconId(stereotypeIconId); m_stereotypeIcon->setBaseSize(QSizeF(m_stereotypeIcon->shapeWidth(), m_stereotypeIcon->shapeHeight())); + m_stereotypeIcon->setActualSize(QSizeF(m_stereotypeIcon->shapeWidth(), m_stereotypeIcon->shapeHeight())); m_stereotypeIcon->setBrush(style->fillBrush()); m_stereotypeIcon->setPen(style->innerLinePen()); } else if (m_stereotypeIcon) { @@ -518,45 +519,92 @@ void ObjectItem::updateStereotypes(const QString &stereotypeIconId, StereotypeIc } } -QSizeF ObjectItem::stereotypeIconMinimumSize(const StereotypeIcon &stereotypeIcon, +QSizeF ObjectItem::customIconItemMinimumSize(const CustomIconItem *customIconItem, qreal minimumWidth, qreal minimumHeight) const { Q_UNUSED(minimumWidth) qreal width = 0.0; qreal height = 0.0; - if (stereotypeIcon.hasMinWidth() && !stereotypeIcon.hasMinHeight()) { - width = stereotypeIcon.minWidth(); - if (stereotypeIcon.sizeLock() == StereotypeIcon::LockHeight || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) - height = stereotypeIcon.minHeight(); - else - height = width * stereotypeIcon.height() / stereotypeIcon.width(); - } else if (!stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { - height = stereotypeIcon.minHeight(); - if (stereotypeIcon.sizeLock() == StereotypeIcon::LockWidth || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) - width = stereotypeIcon.minWidth(); - else - width = height * stereotypeIcon.width() / stereotypeIcon.height(); - } else if (stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { - if (stereotypeIcon.sizeLock() == StereotypeIcon::LockRatio) { + if (customIconItem->hasImage()) { + const QImage &image = customIconItem->image(); + width = minimumWidth; + height = image.height() * width / image.width(); + if (height < minimumHeight) { + height = minimumHeight; + width = image.width() * height / image.height(); + } + } else { + const StereotypeIcon &stereotypeIcon = customIconItem->stereotypeIcon(); + if (stereotypeIcon.hasMinWidth() && !stereotypeIcon.hasMinHeight()) { width = stereotypeIcon.minWidth(); - height = width * stereotypeIcon.height() / stereotypeIcon.width(); - if (height < stereotypeIcon.minHeight()) { + if (stereotypeIcon.sizeLock() == StereotypeIcon::LockHeight || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) height = stereotypeIcon.minHeight(); + else + height = width * stereotypeIcon.height() / stereotypeIcon.width(); + } else if (!stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { + height = stereotypeIcon.minHeight(); + if (stereotypeIcon.sizeLock() == StereotypeIcon::LockWidth || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) + width = stereotypeIcon.minWidth(); + else width = height * stereotypeIcon.width() / stereotypeIcon.height(); - QMT_CHECK(width <= stereotypeIcon.minWidth()); + } else if (stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { + if (stereotypeIcon.sizeLock() == StereotypeIcon::LockRatio) { + width = stereotypeIcon.minWidth(); + height = width * stereotypeIcon.height() / stereotypeIcon.width(); + if (height < stereotypeIcon.minHeight()) { + height = stereotypeIcon.minHeight(); + width = height * stereotypeIcon.width() / stereotypeIcon.height(); + QMT_CHECK(width <= stereotypeIcon.minWidth()); + } + } else { + width = stereotypeIcon.minWidth(); + height = stereotypeIcon.minHeight(); } } else { - width = stereotypeIcon.minWidth(); - height = stereotypeIcon.minHeight(); + height = minimumHeight; + width = height * stereotypeIcon.width() / stereotypeIcon.height(); } - } else { - height = minimumHeight; - width = height * stereotypeIcon.width() / stereotypeIcon.height(); } return QSizeF(width, height); } +void ObjectItem::correctAutoSize(const CustomIconItem* customIconItem, qreal &width, qreal &height, qreal minimumWidth, qreal minimumHeight) const +{ + if (customIconItem) { + if (customIconItem->hasImage()) { + width = customIconItem->image().width(); + height = customIconItem->image().height(); + } + } else { + if (width < minimumWidth) + width = minimumWidth; + if (height < minimumHeight) + height = minimumHeight; + } +} + +void ObjectItem::updateCustomIcon(const Style *style) +{ + if (object()->hasImage()) { + if (!m_customIcon) + m_customIcon = new CustomIconItem(diagramSceneModel(), this); + m_customIcon->setImage(object()->image()); + m_customIcon->setZValue(SHAPE_ZVALUE); + } else if (!shapeIconId().isEmpty()) { + if (!m_customIcon) + m_customIcon = new CustomIconItem(diagramSceneModel(), this); + m_customIcon->setStereotypeIconId(shapeIconId()); + m_customIcon->setBrush(style->fillBrush()); + m_customIcon->setPen(style->outerLinePen()); + m_customIcon->setZValue(SHAPE_ZVALUE); + } else if (m_customIcon) { + m_customIcon->scene()->removeItem(m_customIcon); + delete m_customIcon; + m_customIcon = nullptr; + } +} + bool ObjectItem::suppressTextDisplay() const { return m_shapeIcon.textAlignment() == StereotypeIcon::TextalignNone; @@ -564,6 +612,7 @@ bool ObjectItem::suppressTextDisplay() const void ObjectItem::updateNameItem(const Style *style) { + QString display_name = buildDisplayName(); if (!suppressTextDisplay()) { if (!m_nameItem) { m_nameItem = new EditableTextItem(this); @@ -582,16 +631,18 @@ void ObjectItem::updateNameItem(const Style *style) QObject::connect(m_nameItem, &EditableTextItem::returnKeyPressed, m_nameItem, [this] { this->m_nameItem->clearFocus(); }); } - if (style->headerFont() != m_nameItem->font()) - m_nameItem->setFont(style->headerFont()); + QFont font = style->headerFont(); + if (object()->hasLinkedFile()) + font.setUnderline(true); + if (font != m_nameItem->font()) + m_nameItem->setFont(font); if (style->textBrush().color() != m_nameItem->defaultTextColor()) m_nameItem->setDefaultTextColor(style->textBrush().color()); if (!m_nameItem->hasFocus()) { - QString name = buildDisplayName(); - if (name != m_nameItem->toPlainText()) - m_nameItem->setPlainText(name); + if (display_name != m_nameItem->toPlainText()) + m_nameItem->setPlainText(display_name); } - } else if (m_nameItem ){ + } else if (m_nameItem) { m_nameItem->scene()->removeItem(m_nameItem); delete m_nameItem; m_nameItem = nullptr; @@ -621,30 +672,45 @@ void ObjectItem::setObjectName(const QString &objectName) void ObjectItem::updateDepth() { - setZValue(m_object->depth()); + int depth_delta = 0; + switch (m_shapeIcon.depthLayer()) { + case StereotypeIcon::DepthBehindItems: + depth_delta = -1; + break; + case StereotypeIcon::DepthAmongItems: + depth_delta = 0; + break; + case StereotypeIcon::DepthBeforeItems: + depth_delta = 1; + break; + } + setZValue(m_object->depth() + depth_delta); } -void ObjectItem::updateSelectionMarker(CustomIconItem *customIconItem) +void ObjectItem::updateSelectionMarker(const CustomIconItem *customIconItem) { if (customIconItem) { - StereotypeIcon stereotypeIcon = customIconItem->stereotypeIcon(); ResizeFlags resizeFlags = ResizeUnlocked; - switch (stereotypeIcon.sizeLock()) { - case StereotypeIcon::LockNone: - resizeFlags = ResizeUnlocked; - break; - case StereotypeIcon::LockWidth: - resizeFlags = ResizeLockedWidth; - break; - case StereotypeIcon::LockHeight: - resizeFlags = ResizeLockedHeight; - break; - case StereotypeIcon::LockSize: - resizeFlags = ResizeLockedSize; - break; - case StereotypeIcon::LockRatio: + if (customIconItem->hasImage()) { resizeFlags = ResizeLockedRatio; - break; + } else { + switch (customIconItem->stereotypeIcon().sizeLock()) { + case StereotypeIcon::LockNone: + resizeFlags = ResizeUnlocked; + break; + case StereotypeIcon::LockWidth: + resizeFlags = ResizeLockedWidth; + break; + case StereotypeIcon::LockHeight: + resizeFlags = ResizeLockedHeight; + break; + case StereotypeIcon::LockSize: + resizeFlags = ResizeLockedSize; + break; + case StereotypeIcon::LockRatio: + resizeFlags = ResizeLockedRatio; + break; + } } updateSelectionMarker(resizeFlags); } else { @@ -990,6 +1056,10 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) addSeparator = true; if (element_tasks->extendContextMenu(object(), diagramSceneModel()->diagram(), &menu)) addSeparator = true; + if (element_tasks->hasLinkedFile(m_object, m_diagramSceneModel->diagram())) { + menu.addAction(new ContextMenuAction(Tr::tr("Open Linked File"), "openLinkedFile", &menu)); + addSeparator = true; + } if (addSeparator) menu.addSeparator(); menu.addAction(new ContextMenuAction(Tr::tr("Remove"), "remove", @@ -1000,12 +1070,12 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) QMenu alignMenu; alignMenu.setTitle(Tr::tr("Align Objects")); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Left"), "alignLeft", &alignMenu)); - alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Vertically"), "centerVertically", + alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Horizontally"), "centerHorizontally", &alignMenu)); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Right"), "alignRight", &alignMenu)); alignMenu.addSeparator(); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Top"), "alignTop", &alignMenu)); - alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Horizontally"), "centerHorizontally", + alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Vertically"), "centerVertically", &alignMenu)); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Bottom"), "alignBottom", &alignMenu)); alignMenu.addSeparator(); @@ -1035,6 +1105,8 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) m_diagramSceneModel->diagramSceneController()->elementTasks()->openDiagram(m_object, m_diagramSceneModel->diagram()); } else if (action->id() == "createDiagram") { m_diagramSceneModel->diagramSceneController()->elementTasks()->createAndOpenDiagram(m_object, m_diagramSceneModel->diagram()); + } else if (action->id() == "openLinkedFile") { + m_diagramSceneModel->diagramSceneController()->elementTasks()->openLinkedFile(m_object, m_diagramSceneModel->diagram()); } else if (action->id() == "remove") { DSelection selection = m_diagramSceneModel->selectedElements(); if (selection.isEmpty()) diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h index e7acd5705c..254ef215ca 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h @@ -120,8 +120,12 @@ protected: StereotypeIcon::Display stereotypeDisplay, const Style *style); StereotypesItem *stereotypesItem() const { return m_stereotypesItem; } CustomIconItem *stereotypeIconItem() const { return m_stereotypeIcon; } - QSizeF stereotypeIconMinimumSize(const StereotypeIcon &stereotypeIcon, qreal minimumWidth, + CustomIconItem *customIconItem() const { return m_customIcon; } + QSizeF customIconItemMinimumSize(const CustomIconItem* customIconItem, qreal minimumWidth, qreal minimumHeight) const; + void correctAutoSize(const CustomIconItem *customIconItem, qreal& width, qreal& height, + qreal minimumWidth, qreal minimumHeight) const; + void updateCustomIcon(const Style* style); bool suppressTextDisplay() const; void updateNameItem(const Style *style); EditableTextItem *nameItem() const { return m_nameItem; } @@ -130,7 +134,7 @@ protected: void setObjectName(const QString &objectName); void updateDepth(); - void updateSelectionMarker(CustomIconItem *customIconItem); + void updateSelectionMarker(const CustomIconItem* customIconItem); void updateSelectionMarker(ResizeFlags resizeFlags); void updateSelectionMarkerGeometry(const QRectF &objectRect); void updateRelationStarter(); @@ -169,6 +173,7 @@ private: StereotypeIcon::Display m_stereotypeIconDisplay = StereotypeIcon::DisplayLabel; StereotypesItem *m_stereotypesItem = nullptr; CustomIconItem *m_stereotypeIcon = nullptr; + CustomIconItem *m_customIcon = nullptr; EditableTextItem *m_nameItem = nullptr; RectangularSelectionItem *m_selectionMarker = nullptr; RelationStarter *m_relationStarter = nullptr; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp index b473d17ec8..03e6d8d0f5 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp @@ -64,23 +64,10 @@ void PackageItem::update() const Style *style = adaptedStyle(stereotypeIconId()); - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsPolygonItem(this); m_shape->setBrush(style->fillBrush()); @@ -111,7 +98,7 @@ void PackageItem::update() m_contextLabel = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -119,8 +106,8 @@ void PackageItem::update() bool PackageItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -169,8 +156,8 @@ PackageItem::ShapeGeometry PackageItem::calcMinimumGeometry() const double width = 0.0; double height = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) @@ -244,12 +231,7 @@ void PackageItem::updateGeometry() // calc width and height if (object()->isAutoSized()) { - if (!m_customIcon) { - if (width < MINIMUM_AUTO_WIDTH) - width = MINIMUM_AUTO_WIDTH; - if (height < MINIMUM_AUTO_HEIGHT) - height = MINIMUM_AUTO_HEIGHT; - } + correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -273,9 +255,9 @@ void PackageItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h index 19c2fe54ab..f87af2fa35 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h @@ -40,7 +40,6 @@ private: ShapeGeometry calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsPolygonItem *m_shape = nullptr; ContextLabelItem *m_contextLabel = nullptr; }; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp index ad668f6bee..9313327c87 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp @@ -57,7 +57,9 @@ void StereotypeDisplayVisitor::visitDObject(const DObject *object) DObject::StereotypeDisplay stereotypeDisplay = object->stereotypeDisplay(); m_stereotypeIconId = m_stereotypeController->findStereotypeIconId(m_stereotypeIconElement, object->stereotypes()); - if (m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeIcon) { + if (object->hasImage() && stereotypeDisplay == DObject::StereotypeSmart) { + stereotypeDisplay = DObject::StereotypeLabel; + } else if (m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeIcon) { stereotypeDisplay = DObject::StereotypeLabel; } else if (!m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeSmart) { StereotypeIcon stereotypeIcon = m_stereotypeController->findStereotypeIcon(m_stereotypeIconId); diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp index 066f9b7875..36e4c4f789 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp @@ -57,55 +57,97 @@ void CustomIconItem::setPen(const QPen &pen) m_pen = pen; } +void CustomIconItem::setImage(const QImage &image) +{ + m_image = image; +} + double CustomIconItem::shapeWidth() const { - return m_stereotypeIcon.width(); + if (!m_image.isNull()) + return m_image.width(); + return m_stereotypeIcon.hasIconWidth() ? m_stereotypeIcon.iconWidth() + : m_stereotypeIcon.width(); } double CustomIconItem::shapeHeight() const { - return m_stereotypeIcon.height(); + if (!m_image.isNull()) + return m_image.height(); + return m_stereotypeIcon.hasIconHeight() ? m_stereotypeIcon.iconHeight() + : m_stereotypeIcon.height(); } QRectF CustomIconItem::boundingRect() const { - ShapeSizeVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); + if (!m_image.isNull()) + return QRectF(QPointF(0.0, 0.0), m_actualSize) | childrenBoundingRect(); + ShapeSizeVisitor visitor(QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); m_stereotypeIcon.iconShape().visitShapes(&visitor); return visitor.boundingRect() | childrenBoundingRect(); } -void CustomIconItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +void CustomIconItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) { Q_UNUSED(option) Q_UNUSED(widget) painter->save(); - painter->setBrush(m_brush); - painter->setPen(m_pen); + if (!m_image.isNull()) { + painter->drawImage(QRectF(QPointF(0.0, 0.0), m_actualSize), m_image); + } else { + painter->setBrush(m_brush); + painter->setPen(m_pen); #ifdef DEBUG_OUTLINE - ShapePolygonVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - IconShape shape = m_stereotypeIcon.outlineShape(); - if (shape.isEmpty()) - shape = m_stereotypeIcon.iconShape(); - shape.visitShapes(&visitor); - painter->drawPath(visitor.path()); - ShapePaintVisitor visitor1(painter, QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - m_stereotypeIcon.iconShape().visitShapes(&visitor1); + ShapePolygonVisitor visitor(QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + IconShape shape = m_stereotypeIcon.outlineShape(); + if (shape.isEmpty()) + shape = m_stereotypeIcon.iconShape(); + shape.visitShapes(&visitor); + painter->drawPath(visitor.path()); + ShapePaintVisitor visitor1(painter, + QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + m_stereotypeIcon.iconShape().visitShapes(&visitor1); #else - ShapePaintVisitor visitor(painter, QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - m_stereotypeIcon.iconShape().visitShapes(&visitor); + ShapePaintVisitor visitor(painter, + QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + m_stereotypeIcon.iconShape().visitShapes(&visitor); #endif + } painter->restore(); } QList<QPolygonF> CustomIconItem::outline() const { - ShapePolygonVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - IconShape shape = m_stereotypeIcon.outlineShape(); - if (shape.isEmpty()) - shape = m_stereotypeIcon.iconShape(); - shape.visitShapes(&visitor); - return visitor.toPolygons(); + if (!m_image.isNull()) { + QList<QPolygonF> polygons; + polygons.append(QPolygonF(QRectF(QPointF(0.0, 0.0), m_actualSize))); + return polygons; + } else { + ShapePolygonVisitor visitor(QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + IconShape shape = m_stereotypeIcon.outlineShape(); + if (shape.isEmpty()) + shape = m_stereotypeIcon.iconShape(); + shape.visitShapes(&visitor); + return visitor.toPolygons(); + } } } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h index 7f85e87516..f61db552ac 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h @@ -9,6 +9,7 @@ #include "qmt/stereotype/stereotypeicon.h" #include <QBrush> +#include <QImage> #include <QPen> namespace qmt { @@ -26,6 +27,9 @@ public: void setActualSize(const QSizeF &actualSize); void setBrush(const QBrush &brush); void setPen(const QPen &pen); + QImage image() const { return m_image; } + bool hasImage() const { return !m_image.isNull(); } + void setImage(const QImage &image); StereotypeIcon stereotypeIcon() const { return m_stereotypeIcon; } double shapeWidth() const; double shapeHeight() const; @@ -43,6 +47,7 @@ private: QSizeF m_actualSize; QBrush m_brush; QPen m_pen; + QImage m_image; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/model/mobject.cpp b/src/libs/modelinglib/qmt/model/mobject.cpp index 734be13567..1e6bf8d0b3 100644 --- a/src/libs/modelinglib/qmt/model/mobject.cpp +++ b/src/libs/modelinglib/qmt/model/mobject.cpp @@ -20,6 +20,7 @@ MObject::MObject() MObject::MObject(const MObject &rhs) : MElement(rhs), m_name(rhs.m_name), + m_linkedfilename(rhs.m_linkedfilename), m_children(true), m_relations(true) { @@ -34,6 +35,7 @@ MObject &MObject::operator =(const MObject &rhs) if (this != &rhs) { MElement::operator=(rhs); m_name = rhs.m_name; + m_linkedfilename = rhs.m_linkedfilename; // no deep copy; list of children remains unchanged } return *this; @@ -44,6 +46,11 @@ void MObject::setName(const QString &name) m_name = name; } +void MObject::setLinkedFileName(const QString &linkedfilename) +{ + m_linkedfilename = linkedfilename; +} + void MObject::setChildren(const Handles<MObject> &children) { m_children = children; diff --git a/src/libs/modelinglib/qmt/model/mobject.h b/src/libs/modelinglib/qmt/model/mobject.h index 1dc95887d9..33a475ed6e 100644 --- a/src/libs/modelinglib/qmt/model/mobject.h +++ b/src/libs/modelinglib/qmt/model/mobject.h @@ -23,6 +23,8 @@ public: QString name() const { return m_name; } void setName(const QString &name); + QString linkedFileName() const { return m_linkedfilename; } + void setLinkedFileName(const QString &linkedfilename); const Handles<MObject> &children() const { return m_children; } void setChildren(const Handles<MObject> &children); @@ -48,6 +50,7 @@ public: private: QString m_name; + QString m_linkedfilename; Handles<MObject> m_children; Handles<MRelation> m_relations; }; diff --git a/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp b/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp index deb458e4bc..66cfb4576f 100644 --- a/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp +++ b/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp @@ -35,6 +35,7 @@ void MFlatAssignmentVisitor::visitMObject(const MObject *object) auto targetObject = dynamic_cast<MObject *>(m_target); QMT_ASSERT(targetObject, return); targetObject->setName(object->name()); + targetObject->setLinkedFileName(object->linkedFileName()); } void MFlatAssignmentVisitor::visitMPackage(const MPackage *package) diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp index 28566d07ce..11df24188d 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp +++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp @@ -454,6 +454,7 @@ void PropertiesView::MView::visitMPackage(const MPackage *package) else setTitle<MPackage>(m_modelElements, Tr::tr("Package"), Tr::tr("Packages")); visitMObject(package); + visitMObjectBehind(package); } void PropertiesView::MView::visitMClass(const MClass *klass) @@ -528,12 +529,14 @@ void PropertiesView::MView::visitMClass(const MClass *klass) } if (m_classMembersEdit->isEnabled() != isSingleSelection) m_classMembersEdit->setEnabled(isSingleSelection); + visitMObjectBehind(klass); } void PropertiesView::MView::visitMComponent(const MComponent *component) { setTitle<MComponent>(m_modelElements, Tr::tr("Component"), Tr::tr("Components")); visitMObject(component); + visitMObjectBehind(component); } void PropertiesView::MView::visitMDiagram(const MDiagram *diagram) @@ -553,6 +556,7 @@ void PropertiesView::MView::visitMCanvasDiagram(const MCanvasDiagram *diagram) { setTitle<MCanvasDiagram>(m_modelElements, Tr::tr("Canvas Diagram"), Tr::tr("Canvas Diagrams")); visitMDiagram(diagram); + visitMDiagramBehind(diagram); } void PropertiesView::MView::visitMItem(const MItem *item) @@ -577,6 +581,7 @@ void PropertiesView::MView::visitMItem(const MItem *item) if (m_itemVarietyEdit->isEnabled() != isSingleSelection) m_itemVarietyEdit->setEnabled(isSingleSelection); } + visitMObjectBehind(item); } void PropertiesView::MView::visitMRelation(const MRelation *relation) @@ -906,6 +911,7 @@ void PropertiesView::MView::visitDElement(const DElement *element) void PropertiesView::MView::visitDObject(const DObject *object) { visitDElement(object); + visitDObjectBefore(object); #ifdef SHOW_DEBUG_PROPERTIES if (!m_posRectLabel) { m_posRectLabel = new QLabel(m_topWidget); @@ -1247,6 +1253,26 @@ void PropertiesView::MView::visitDSwimlane(const DSwimlane *swimlane) visitDElement(swimlane); } +void PropertiesView::MView::visitMElementBehind(const MElement *element) +{ + Q_UNUSED(element) +} + +void PropertiesView::MView::visitMObjectBehind(const MObject *object) +{ + visitMElementBehind(object); +} + +void PropertiesView::MView::visitMDiagramBehind(const MDiagram *diagram) +{ + visitMObjectBehind(diagram); +} + +void PropertiesView::MView::visitDObjectBefore(const DObject *object) +{ + Q_UNUSED(object); +} + void PropertiesView::MView::onStereotypesChanged(const QString &stereotypes) { QList<QString> set = m_stereotypesController->fromString(stereotypes); @@ -1744,112 +1770,4 @@ void PropertiesView::MView::setRelationPrimaryRolePalette(StyleEngine::ElementTy m_relationVisualPrimaryRoleSelector->setLinePen(index, style->linePen()); } -template<class T, class V> -QList<T *> PropertiesView::MView::filter(const QList<V *> &elements) -{ - QList<T *> filtered; - for (V *element : elements) { - auto t = dynamic_cast<T *>(element); - if (t) - filtered.append(t); - } - return filtered; -} - -template<class T, class V, class BASE> -bool PropertiesView::MView::haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value) -{ - const QList<T *> elements = filter<T>(baseElements); - QMT_CHECK(!elements.isEmpty()); - V candidate = V(); // avoid warning of reading uninitialized variable - bool haveCandidate = false; - for (T *element : elements) { - if (!haveCandidate) { - candidate = ((*element).*getter)(); - haveCandidate = true; - } else { - if (candidate != ((*element).*getter)()) - return false; - } - } - QMT_CHECK(haveCandidate); - if (!haveCandidate) - return false; - if (value) - *value = candidate; - return true; -} - -template<class T, class V, class BASE> -void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, V (T::*getter)() const, void (T::*setter)(const V &)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - if (value != ((*element).*getter)()) { - m_propertiesView->beginUpdate(element); - ((*element).*setter)(value); - m_propertiesView->endUpdate(element, false); - } - } - } -} - -template<class T, class V, class BASE> -void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, V (T::*getter)() const, void (T::*setter)(V)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - if (value != ((*element).*getter)()) { - m_propertiesView->beginUpdate(element); - ((*element).*setter)(value); - m_propertiesView->endUpdate(element, false); - } - } - } -} - -template<class T, class E, class V, class BASE> -void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, E (T::*getter)() const, - void (T::*setter)(const E &), - V (E::*vGetter)() const, void (E::*vSetter)(const V &)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - E embedded = ((*element).*getter)(); - if (value != (embedded.*vGetter)()) { - m_propertiesView->beginUpdate(element); - (embedded.*vSetter)(value); - ((*element).*setter)(embedded); - m_propertiesView->endUpdate(element, false); - } - } - } -} - -template<class T, class E, class V, class BASE> -void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, E (T::*getter)() const, - void (T::*setter)(const E &), - V (E::*vGetter)() const, void (E::*vSetter)(V)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - E embedded = ((*element).*getter)(); - if (value != (embedded.*vGetter)()) { - m_propertiesView->beginUpdate(element); - (embedded.*vSetter)(value); - ((*element).*setter)(embedded); - m_propertiesView->endUpdate(element, false); - } - } - } -} - } // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h index 026f4f6857..8fa9f0d137 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h +++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h @@ -78,6 +78,11 @@ public: void edit(); protected: + virtual void visitMElementBehind(const MElement *element); + virtual void visitMObjectBehind(const MObject *object); + virtual void visitMDiagramBehind(const MDiagram *diagram); + virtual void visitDObjectBefore(const DObject *object); + void onStereotypesChanged(const QString &stereotypes); void onObjectNameChanged(const QString &name); void onNamespaceChanged(const QString ¨Namespace); @@ -251,4 +256,126 @@ protected: QLabel *m_pointsLabel = nullptr; }; +template<class T, class V> +QList<T *> PropertiesView::MView::filter(const QList<V *> &elements) +{ + QList<T *> filtered; + for (auto *element : elements) { + auto t = dynamic_cast<T *>(element); + if (t) + filtered.append(t); + } + return filtered; +} + +template<class T, class V, class BASE> +inline bool PropertiesView::MView::haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value) +{ + QList<T *> elements = filter<T>(baseElements); + QMT_CHECK(!elements.isEmpty()); + V candidate = V(); // avoid warning of reading uninitialized variable + bool haveCandidate = false; + for (const auto *element : elements) { + if (!haveCandidate) { + candidate = ((*element).*getter)(); + haveCandidate = true; + } else { + if (candidate != ((*element).*getter)()) + return false; + } + } + QMT_CHECK(haveCandidate); + if (!haveCandidate) + return false; + if (value) + *value = candidate; + return true; +} + +template<class T, class V, class BASE> +inline bool PropertiesView::MView::isValueChanged(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, V (T::*getter)() const) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (const auto *element : elements) { + if (value != ((*element).*getter)()) + return true; + } + } + return false; +} + +template<class T, class V, class BASE> +inline void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, V (T::*getter)() const, void (T::*setter)(const V &)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + if (value != ((*element).*getter)()) { + m_propertiesView->beginUpdate(element); + ((*element).*setter)(value); + m_propertiesView->endUpdate(element, false); + } + } + } +} + +template<class T, class V, class BASE> +inline void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, V (T::*getter)() const, void (T::*setter)(V)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + if (value != ((*element).*getter)()) { + m_propertiesView->beginUpdate(element); + ((*element).*setter)(value); + m_propertiesView->endUpdate(element, false); + } + } + } +} + +template<class T, class E, class V, class BASE> +inline void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, E (T::*getter)() const, + void (T::*setter)(const E &), + V (E::*vGetter)() const, void (E::*vSetter)(const V &)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + E embedded = ((*element).*getter)(); + if (value != (embedded.*vGetter)()) { + m_propertiesView->beginUpdate(element); + (embedded.*vSetter)(value); + ((*element).*setter)(embedded); + m_propertiesView->endUpdate(element, false); + } + } + } +} + +template<class T, class E, class V, class BASE> +inline void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, E (T::*getter)() const, + void (T::*setter)(const E &), + V (E::*vGetter)() const, void (E::*vSetter)(V)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + E embedded = ((*element).*getter)(); + if (value != (embedded.*vGetter)()) { + m_propertiesView->beginUpdate(element); + (embedded.*vSetter)(value); + ((*element).*setter)(embedded); + m_propertiesView->endUpdate(element, false); + } + } + } +} + } // namespace qmt diff --git a/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp b/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp index 5121a51f3d..2d68862c32 100644 --- a/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp +++ b/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp @@ -29,10 +29,35 @@ #include "qark/qxmlinarchive.h" #include "qark/serialize.h" +#include <QBuffer> + using namespace qmt; namespace qark { +template<class Archive> +inline void save(Archive &archive, const QImage &image) +{ + QByteArray a; + QBuffer buffer(&a); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "PNG"); + // TODO add write(const QByteArray &) + archive.write(QString::fromLatin1(a.toBase64())); +} + +template<class Archive> +inline void load(Archive &archive, QImage &image) +{ + QString s; + // TODO add read(QByteArray &) + archive.read(&s); + QByteArray a = QByteArray::fromBase64(s.toLatin1()); + QBuffer buffer(&a); + buffer.open(QIODevice::ReadOnly); + image.load(&buffer, "PNG"); +} + // DElement QARK_REGISTER_TYPE_NAME(DElement, "DElement") @@ -102,7 +127,10 @@ inline void Access<Archive, DObject>::serialize(Archive &archive, DObject &objec || attr("visual-role", object, &visualRole, &setVisualRole) || attr("visual-role2", object, &DObject::visualSecondaryRole, &DObject::setVisualSecondaryRole) || attr("visual-emphasized", object, &DObject::isVisualEmphasized, &DObject::setVisualEmphasized) + || attr("linkedfile", object, &DObject::hasLinkedFile, &DObject::setLinkedFile) || attr("stereotype-display", object, &DObject::stereotypeDisplay, &DObject::setStereotypeDisplay) + || attr("image-path", object, &DObject::imagePath, &DObject::setImagePath) + || attr("image", object, &DObject::image, &DObject::setImage) // depth is not persistent || end; } diff --git a/src/libs/modelinglib/qmt/serializer/modelserializer.cpp b/src/libs/modelinglib/qmt/serializer/modelserializer.cpp index 115eba08d0..935a825ab2 100644 --- a/src/libs/modelinglib/qmt/serializer/modelserializer.cpp +++ b/src/libs/modelinglib/qmt/serializer/modelserializer.cpp @@ -95,6 +95,7 @@ inline void Access<Archive, MObject>::serialize(Archive &archive, MObject &objec archive || tag(object) || base<MElement>(object) || attr("name", object, &MObject::name, &MObject::setName) + || attr("linkedfilename", object, &MObject::linkedFileName, &MObject::setLinkedFileName) || attr("children", object, &MObject::children, &MObject::setChildren) || attr("relations", object, &MObject::relations, &MObject::setRelations) || end; diff --git a/src/libs/modelinglib/qmt/tasks/ielementtasks.h b/src/libs/modelinglib/qmt/tasks/ielementtasks.h index a40277d874..8c22632762 100644 --- a/src/libs/modelinglib/qmt/tasks/ielementtasks.h +++ b/src/libs/modelinglib/qmt/tasks/ielementtasks.h @@ -59,8 +59,13 @@ public: virtual void createAndOpenDiagram(const MElement *) = 0; virtual void createAndOpenDiagram(const DElement *, const MDiagram *) = 0; + virtual bool hasLinkedFile(const MElement *) const = 0; + virtual bool hasLinkedFile(const DElement *, const MDiagram *) const = 0; + virtual void openLinkedFile(const MElement *) = 0; + virtual void openLinkedFile(const DElement *, const MDiagram *) = 0; + virtual bool extendContextMenu(const DElement *, const MDiagram *, QMenu *) = 0; - virtual bool handleContextMenuAction(const DElement *, const MDiagram *, const QString &) = 0; + virtual bool handleContextMenuAction(DElement *, MDiagram *, const QString &) = 0; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp b/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp index 2bd7fbe049..fdc2e24bd4 100644 --- a/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp +++ b/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp @@ -147,12 +147,30 @@ void VoidElementTasks::createAndOpenDiagram(const DElement *, const MDiagram *) { } +bool VoidElementTasks::hasLinkedFile(const MElement *) const +{ + return false; +} + +bool VoidElementTasks::hasLinkedFile(const DElement *, const MDiagram *) const +{ + return false; +} + +void VoidElementTasks::openLinkedFile(const MElement *) +{ +} + +void VoidElementTasks::openLinkedFile(const DElement *, const MDiagram *) +{ +} + bool VoidElementTasks::extendContextMenu(const DElement *, const MDiagram *, QMenu *) { return false; } -bool VoidElementTasks::handleContextMenuAction(const DElement *, const MDiagram *, const QString &) +bool VoidElementTasks::handleContextMenuAction(DElement *, MDiagram *, const QString &) { return false; } diff --git a/src/libs/modelinglib/qmt/tasks/voidelementtasks.h b/src/libs/modelinglib/qmt/tasks/voidelementtasks.h index 3e3d483d9a..9e8753b71e 100644 --- a/src/libs/modelinglib/qmt/tasks/voidelementtasks.h +++ b/src/libs/modelinglib/qmt/tasks/voidelementtasks.h @@ -51,8 +51,13 @@ public: void createAndOpenDiagram(const MElement *) override; void createAndOpenDiagram(const DElement *, const MDiagram *) override; + bool hasLinkedFile(const qmt::MElement *) const override; + bool hasLinkedFile(const qmt::DElement *, const qmt::MDiagram *) const override; + void openLinkedFile(const qmt::MElement *) override; + void openLinkedFile(const qmt::DElement *, const qmt::MDiagram *) override; + bool extendContextMenu(const DElement *, const MDiagram *, QMenu *) override; - bool handleContextMenuAction(const DElement *, const MDiagram *, const QString &) override; + bool handleContextMenuAction(DElement *, MDiagram *, const QString &) override; }; } // namespace qmt diff --git a/src/plugins/modeleditor/elementtasks.cpp b/src/plugins/modeleditor/elementtasks.cpp index 3cc271214f..b2206da4b4 100644 --- a/src/plugins/modeleditor/elementtasks.cpp +++ b/src/plugins/modeleditor/elementtasks.cpp @@ -28,9 +28,11 @@ #include <cppeditor/indexitem.h> #include <cppeditor/searchsymbols.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> #include <utils/qtcassert.h> #include <QMenu> +#include <QMessageBox> using namespace Core; using namespace CppEditor; @@ -392,6 +394,68 @@ void ElementTasks::createAndOpenDiagram(const qmt::DElement *element, const qmt: createAndOpenDiagram(melement); } +bool ElementTasks::hasLinkedFile(const qmt::MElement *element) const +{ + if (auto mobject = dynamic_cast<const qmt::MObject *>(element)) { + QString filename = mobject->linkedFileName(); + if (!filename.isEmpty()) { + QString projectName = d->documentController->projectController()->project()->fileName(); + Utils::FilePath relativePath = Utils::FilePath::fromString(filename); + Utils::FilePath projectPath = Utils::FilePath::fromString(projectName); + QString filepath = relativePath.resolvePath(projectPath).toString(); + return QFileInfo::exists(filepath); + } + } + return false; +} + +bool ElementTasks::hasLinkedFile(const qmt::DElement *element, const qmt::MDiagram *diagram) const +{ + Q_UNUSED(diagram) + + qmt::MElement *melement = d->documentController->modelController()->findElement(element->modelUid()); + if (!melement) + return false; + return hasLinkedFile(melement); +} + +void ElementTasks::openLinkedFile(const qmt::MElement *element) +{ + if (auto mobject = dynamic_cast<const qmt::MObject *>(element)) { + QString filename = mobject->linkedFileName(); + if (!filename.isEmpty()) { + QString projectName = d->documentController->projectController()->project()->fileName(); + QString filepath; + if (QFileInfo(filename).isRelative()) + filepath = QFileInfo(QFileInfo(projectName).path() + "/" + filename).canonicalFilePath(); + else + filepath = filename; + if (QFileInfo::exists(filepath)) { + Core::EditorFactories list = Core::IEditorFactory::preferredEditorFactories(Utils::FilePath::fromString(filepath)); + if (list.empty() || (list.count() <= 1 && list.at(0)->id() == "Core.BinaryEditor")) { + // intentionally ignore return code + (void) Core::EditorManager::instance()->openExternalEditor(Utils::FilePath::fromString(filepath), "CorePlugin.OpenWithSystemEditor"); + } else { + // intentionally ignore return code + (void) Core::EditorManager::instance()->openEditor(Utils::FilePath::fromString(filepath)); + } + } else { + QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Opening File"), Tr::tr("File %1 does not exist.").arg(filepath)); + } + } + } +} + +void ElementTasks::openLinkedFile(const qmt::DElement *element, const qmt::MDiagram *diagram) +{ + Q_UNUSED(diagram) + + qmt::MElement *melement = d->documentController->modelController()->findElement(element->modelUid()); + if (!melement) + return; + openLinkedFile(melement); +} + bool ElementTasks::extendContextMenu(const qmt::DElement *delement, const qmt::MDiagram *, QMenu *menu) { bool extended = false; @@ -402,8 +466,9 @@ bool ElementTasks::extendContextMenu(const qmt::DElement *delement, const qmt::M return extended; } -bool ElementTasks::handleContextMenuAction(const qmt::DElement *element, const qmt::MDiagram *, const QString &id) +bool ElementTasks::handleContextMenuAction(qmt::DElement *element, qmt::MDiagram *diagram, const QString &id) { + Q_UNUSED(diagram); if (id == "updateIncludeDependencies") { qmt::MPackage *mpackage = d->documentController->modelController()->findElement<qmt::MPackage>(element->modelUid()); if (mpackage) diff --git a/src/plugins/modeleditor/elementtasks.h b/src/plugins/modeleditor/elementtasks.h index b99512d65f..caee1e9e6f 100644 --- a/src/plugins/modeleditor/elementtasks.h +++ b/src/plugins/modeleditor/elementtasks.h @@ -66,8 +66,13 @@ public: void createAndOpenDiagram(const qmt::MElement *element) override; void createAndOpenDiagram(const qmt::DElement *element, const qmt::MDiagram *diagram) override; - bool extendContextMenu(const qmt::DElement *delement, const qmt::MDiagram *, QMenu *menu) override; - bool handleContextMenuAction(const qmt::DElement *element, const qmt::MDiagram *, const QString &id) override; + bool hasLinkedFile(const qmt::MElement *element) const override; + bool hasLinkedFile(const qmt::DElement *element, const qmt::MDiagram *diagram) const override; + void openLinkedFile(const qmt::MElement *element) override; + void openLinkedFile(const qmt::DElement *element, const qmt::MDiagram *diagram) override; + + bool extendContextMenu(const qmt::DElement *delement, const qmt::MDiagram *diagram, QMenu *menu) override; + bool handleContextMenuAction(qmt::DElement *element, qmt::MDiagram *diagram, const QString &id) override; private: ElementTasksPrivate *d; diff --git a/src/plugins/modeleditor/extpropertiesmview.cpp b/src/plugins/modeleditor/extpropertiesmview.cpp index b57d1bc2a9..fcae9ada22 100644 --- a/src/plugins/modeleditor/extpropertiesmview.cpp +++ b/src/plugins/modeleditor/extpropertiesmview.cpp @@ -6,18 +6,60 @@ #include "modeleditortr.h" #include "qmt/model/mpackage.h" +#include "qmt/diagram/dobject.h" #include "qmt/project/project.h" #include "qmt/project_controller/projectcontroller.h" +#include <coreplugin/icore.h> +#include <utils/fileutils.h> #include <utils/pathchooser.h> #include <QLabel> #include <QFileInfo> #include <QDir> +#include <QMessageBox> +#include <QMimeDatabase> +#include <QImageReader> namespace ModelEditor { namespace Internal { +static QString imageNameFilterString() +{ + static QString result; + if (result.isEmpty()) { + QMimeDatabase mimeDatabase; + const QString separator = QStringLiteral(";;"); + const auto mimeTypes = QImageReader::supportedMimeTypes(); + for (const QByteArray &mimeType : mimeTypes) { + const QString filter = mimeDatabase.mimeTypeForName(QLatin1String(mimeType)) + .filterString(); + if (!filter.isEmpty()) { + if (mimeType == QByteArrayLiteral("image/png")) { + if (!result.isEmpty()) + result.prepend(separator); + result.prepend(filter); + } else { + if (!result.isEmpty()) + result.append(separator); + result.append(filter); + } + } + } + } + return result; +} + +/// Constructs an absolute FilePath from \a relativePath which +/// is interpreted as being relative to \a anchor. +Utils::FilePath absoluteFromRelativePath(const Utils::FilePath &relativePath, + const Utils::FilePath &anchor) +{ + QDir anchorDir = QFileInfo(anchor.path()).absoluteDir(); + QString absoluteFilePath = QFileInfo(anchorDir, relativePath.path()).canonicalFilePath(); + return Utils::FilePath::fromString(absoluteFilePath); +} + ExtPropertiesMView::ExtPropertiesMView(qmt::PropertiesView *view) : qmt::PropertiesView::MView(view) { @@ -45,7 +87,8 @@ void ExtPropertiesMView::visitMPackage(const qmt::MPackage *package) Utils::FilePath::fromString(project->fileName()).absolutePath()); addRow(Tr::tr("Config path:"), m_configPath, "configpath"); connect(m_configPath, &Utils::PathChooser::textChanged, - this, &ExtPropertiesMView::onConfigPathChanged); + this, &ExtPropertiesMView::onConfigPathChanged, + Qt::QueuedConnection); } if (!m_configPath->hasFocus()) { if (project->configPath().isEmpty()) { @@ -63,9 +106,78 @@ void ExtPropertiesMView::visitMPackage(const qmt::MPackage *package) } } -void ExtPropertiesMView::onConfigPathChanged() +void ExtPropertiesMView::visitMObjectBehind(const qmt::MObject *object) +{ + qmt::Project *project = m_projectController->project(); + QList<qmt::MObject *> selection = filter<qmt::MObject>(m_modelElements); + bool isSingleSelection = selection.size() == 1; + if (!m_filelinkPathChooser) { + m_filelinkPathChooser = new Utils::PathChooser(m_topWidget); + m_filelinkPathChooser->setPromptDialogTitle((Tr::tr("Select File Target"))); + m_filelinkPathChooser->setExpectedKind(Utils::PathChooser::File); + m_filelinkPathChooser->setInitialBrowsePathBackup(Utils::FilePath::fromString(QFileInfo(project->fileName()).absolutePath())); + addRow(Tr::tr("Linked file:"), m_filelinkPathChooser, "filelink"); + connect(m_filelinkPathChooser, &Utils::PathChooser::textChanged, + this, &ExtPropertiesMView::onFileLinkPathChanged, + Qt::QueuedConnection); + } + if (isSingleSelection) { + if (!m_filelinkPathChooser->hasFocus()) { + QString path = object->linkedFileName(); + if (path.isEmpty()) { + m_filelinkPathChooser->setPath(QString()); + } else { + Utils::FilePath relativePath = Utils::FilePath::fromString(path); + Utils::FilePath projectPath = Utils::FilePath::fromString(project->fileName()); + QString filePath = absoluteFromRelativePath(relativePath, projectPath).toString(); + m_filelinkPathChooser->setPath(filePath); + } + } + } else { + m_filelinkPathChooser->setPath(QString()); + } + if (m_filelinkPathChooser->isEnabled() != isSingleSelection) + m_filelinkPathChooser->setEnabled(isSingleSelection); +} + +void ExtPropertiesMView::visitDObjectBefore(const qmt::DObject *object) +{ + qmt::Project *project = m_projectController->project(); + QList<qmt::DObject *> selection = filter<qmt::DObject>(m_diagramElements); + bool isSingleSelection = selection.size() == 1; + if (!m_imagePathChooser) { + m_imagePathChooser = new Utils::PathChooser(m_topWidget); + m_imagePathChooser->setPromptDialogTitle(Tr::tr("Select Image File")); + m_imagePathChooser->setExpectedKind(Utils::PathChooser::File); + m_imagePathChooser->setPromptDialogFilter(imageNameFilterString()); + m_imagePathChooser->setInitialBrowsePathBackup( + Utils::FilePath::fromString(QFileInfo(project->fileName()).absolutePath())); + addRow(Tr::tr("Image:"), m_imagePathChooser, "imagepath"); + connect(m_imagePathChooser, &Utils::PathChooser::textChanged, + this, &ExtPropertiesMView::onImagePathChanged, + Qt::QueuedConnection); + } + if (isSingleSelection) { + if (!m_imagePathChooser->hasFocus()) { + QString path = object->imagePath(); + if (path.isEmpty()) { + m_imagePathChooser->setPath(QString()); + } else { + Utils::FilePath relativePath = Utils::FilePath::fromString(path); + Utils::FilePath projectPath = Utils::FilePath::fromString(project->fileName()); + QString filePath = absoluteFromRelativePath(relativePath, projectPath).toString(); + m_imagePathChooser->setPath(filePath); + } + } + } else { + m_imagePathChooser->setPath(QString()); + } + if (m_imagePathChooser->isEnabled() != isSingleSelection) + m_imagePathChooser->setEnabled(isSingleSelection); +} + +void ExtPropertiesMView::onConfigPathChanged(const QString &path) { - const Utils::FilePath path = m_configPath->rawFilePath(); bool modified = false; qmt::Project *project = m_projectController->project(); if (path.isEmpty()) { @@ -76,7 +188,7 @@ void ExtPropertiesMView::onConfigPathChanged() } } else { // make path relative to current project's directory - QFileInfo absConfigPath = path.toFileInfo(); + QFileInfo absConfigPath = Utils::FilePath::fromString(path).toFileInfo(); absConfigPath.makeAbsolute(); QDir projectDir = QFileInfo(project->fileName()).dir(); QString configPath = projectDir.relativeFilePath(absConfigPath.filePath()); @@ -90,5 +202,55 @@ void ExtPropertiesMView::onConfigPathChanged() m_configPathInfo->setText(Tr::tr("<font color=red>Model file must be reloaded.</font>")); } +void ExtPropertiesMView::onFileLinkPathChanged(const QString &path) +{ + qmt::Project *project = m_projectController->project(); + if (path.isEmpty()) { + assignModelElement<qmt::MObject, QString>(m_modelElements, SelectionSingle, QString(), + &qmt::MObject::linkedFileName, &qmt::MObject::setLinkedFileName); + } else { + // make path relative to current project's directory + Utils::FilePath filePath = Utils::FilePath::fromString(path); + Utils::FilePath projectPath = Utils::FilePath::fromString(QFileInfo(project->fileName()).path()); + QString relativeFilePath = filePath.relativePathFrom(projectPath).toString(); + if (!relativeFilePath.isEmpty()) { + assignModelElement<qmt::MObject, QString>(m_modelElements, SelectionSingle, relativeFilePath, + &qmt::MObject::linkedFileName, &qmt::MObject::setLinkedFileName); + } + } +} + +void ExtPropertiesMView::onImagePathChanged(const QString &path) +{ + qmt::Project *project = m_projectController->project(); + if (path.isEmpty()) { + assignModelElement<qmt::DObject, QString>(m_diagramElements, SelectionSingle, QString(), + &qmt::DObject::imagePath, &qmt::DObject::setImagePath); + assignModelElement<qmt::DObject, QImage>(m_diagramElements, SelectionSingle, QImage(), + &qmt::DObject::image, &qmt::DObject::setImage); + } else { + // make path relative to current project's directory + Utils::FilePath filePath = Utils::FilePath::fromString(path); + Utils::FilePath projectPath = Utils::FilePath::fromString( + QFileInfo(project->fileName()).path()); + QString relativeFilePath = filePath.relativePathFrom(projectPath).toString(); + if (!relativeFilePath.isEmpty() + && isValueChanged<qmt::DObject, QString>(m_diagramElements, SelectionSingle, relativeFilePath, + &qmt::DObject::imagePath)) + { + QImage image; + if (image.load(path)) { + assignModelElement<qmt::DObject, QString>(m_diagramElements, SelectionSingle, relativeFilePath, + &qmt::DObject::imagePath, &qmt::DObject::setImagePath); + assignModelElement<qmt::DObject, QImage>(m_diagramElements, SelectionSingle, image, + &qmt::DObject::image, &qmt::DObject::setImage); + } else { + QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Selecting Image"), + Tr::tr("Unable to read image file %1").arg(path)); + } + } + } +} + } // namespace Interal } // namespace ModelEditor diff --git a/src/plugins/modeleditor/extpropertiesmview.h b/src/plugins/modeleditor/extpropertiesmview.h index 19e061d78f..d0c9d76bcb 100644 --- a/src/plugins/modeleditor/extpropertiesmview.h +++ b/src/plugins/modeleditor/extpropertiesmview.h @@ -24,13 +24,21 @@ public: void visitMPackage(const qmt::MPackage *package) override; +protected: + void visitMObjectBehind(const qmt::MObject *object) override; + void visitDObjectBefore(const qmt::DObject *object) override; + private: - void onConfigPathChanged(); + void onConfigPathChanged(const QString &path); + void onFileLinkPathChanged(const QString &path); + void onImagePathChanged(const QString &path); private: qmt::ProjectController *m_projectController = nullptr; + Utils::PathChooser *m_filelinkPathChooser = nullptr; Utils::PathChooser *m_configPath = nullptr; QLabel *m_configPathInfo = nullptr; + Utils::PathChooser *m_imagePathChooser = nullptr; }; } // namespace Interal diff --git a/src/plugins/modeleditor/openelementvisitor.cpp b/src/plugins/modeleditor/openelementvisitor.cpp index 60866f63ae..4fbc610571 100644 --- a/src/plugins/modeleditor/openelementvisitor.cpp +++ b/src/plugins/modeleditor/openelementvisitor.cpp @@ -132,7 +132,8 @@ void OpenModelElementVisitor::visitMElement(const qmt::MElement *element) void OpenModelElementVisitor::visitMObject(const qmt::MObject *object) { - Q_UNUSED(object) + if (m_elementTasks->hasLinkedFile(object)) + m_elementTasks->openLinkedFile(object); } void OpenModelElementVisitor::visitMPackage(const qmt::MPackage *package) @@ -145,17 +146,26 @@ void OpenModelElementVisitor::visitMPackage(const qmt::MPackage *package) void OpenModelElementVisitor::visitMClass(const qmt::MClass *klass) { - m_elementTasks->openClassDefinition(klass); + if (m_elementTasks->hasClassDefinition(klass)) + m_elementTasks->openClassDefinition(klass); + else + visitMObject(klass); } void OpenModelElementVisitor::visitMComponent(const qmt::MComponent *component) { - m_elementTasks->openSourceFile(component); + if (m_elementTasks->hasSourceFile(component)) + m_elementTasks->openSourceFile(component); + else + visitMObject(component); } void OpenModelElementVisitor::visitMDiagram(const qmt::MDiagram *diagram) { - m_elementTasks->openDiagram(diagram); + if (m_elementTasks->hasDiagram(diagram)) + m_elementTasks->openDiagram(diagram); + else + visitMObject(diagram); } void OpenModelElementVisitor::visitMCanvasDiagram(const qmt::MCanvasDiagram *diagram) diff --git a/src/plugins/modeleditor/pxnodecontroller.cpp b/src/plugins/modeleditor/pxnodecontroller.cpp index e015e7a240..f659e46f3d 100644 --- a/src/plugins/modeleditor/pxnodecontroller.cpp +++ b/src/plugins/modeleditor/pxnodecontroller.cpp @@ -14,6 +14,7 @@ #include "qmt/model/mclass.h" #include "qmt/model/mcomponent.h" #include "qmt/model/mdiagram.h" +#include "qmt/model/mitem.h" #include "qmt/model/mcanvasdiagram.h" #include "qmt/controller/namecontroller.h" #include "qmt/controller/undocontroller.h" @@ -45,7 +46,10 @@ public: TYPE_ADD_PACKAGE_AND_DIAGRAM, TYPE_ADD_PACKAGE_MODEL, TYPE_ADD_COMPONENT_MODEL, - TYPE_ADD_CLASS_MODEL + TYPE_ADD_CLASS_MODEL, + TYPE_ADD_PACKAGE_LINK, + TYPE_ADD_DIAGRAM_LINK, + TYPE_ADD_DOCUMENT_LINK, }; public: @@ -58,6 +62,17 @@ public: { } + MenuAction(const QString &text, const QString &elementName, Type type, const QString &stereotype, + const QString &filePath, QObject *parent) + : QAction(text, parent), + elementName(elementName), + type(type), + index(-1), + stereotype(stereotype), + filePath(filePath) + { + } + MenuAction(const QString &text, const QString &elementName, Type type, QObject *parent) : QAction(text, parent), elementName(elementName), @@ -70,7 +85,8 @@ public: int type; int index; QString className; - QString packageStereotype; + QString stereotype; + QString filePath; }; class PxNodeController::PxNodeControllerPrivate @@ -151,6 +167,14 @@ void PxNodeController::addFileSystemEntry(const QString &filePath, int line, int ++index; } } + menu->addSeparator(); + QString fileName = fileInfo.fileName(); + menu->addAction(new MenuAction(Tr::tr("Add Package Link to %1").arg(fileName), fileName, + MenuAction::TYPE_ADD_PACKAGE_LINK, "package", filePath, menu)); + menu->addAction(new MenuAction(Tr::tr("Add Diagram Link to %1").arg(fileName), fileName, + MenuAction::TYPE_ADD_DIAGRAM_LINK, "diagram", filePath, menu)); + menu->addAction(new MenuAction(Tr::tr("Add Document Link to %1").arg(fileName), fileName, + MenuAction::TYPE_ADD_DOCUMENT_LINK, "document", filePath, menu)); connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); connect(menu, &QMenu::triggered, this, [this, filePath, topMostElementAtPos, pos, diagram]( QAction *action) { @@ -160,21 +184,13 @@ void PxNodeController::addFileSystemEntry(const QString &filePath, int line, int }); menu->popup(QCursor::pos()); } else if (fileInfo.exists() && fileInfo.isDir()) { - // ignore line and column - QString stereotype; auto menu = new QMenu; - auto action = new MenuAction(Tr::tr("Add Package %1").arg(elementName), elementName, - MenuAction::TYPE_ADD_PACKAGE, menu); - action->packageStereotype = stereotype; - menu->addAction(action); - action = new MenuAction(Tr::tr("Add Package and Diagram %1").arg(elementName), elementName, - MenuAction::TYPE_ADD_PACKAGE_AND_DIAGRAM, menu); - action->packageStereotype = stereotype; - menu->addAction(action); - action = new MenuAction(Tr::tr("Add Component Model"), elementName, - MenuAction::TYPE_ADD_COMPONENT_MODEL, menu); - action->packageStereotype = stereotype; - menu->addAction(action); + menu->addAction(new MenuAction(Tr::tr("Add Package %1").arg(elementName), elementName, + MenuAction::TYPE_ADD_PACKAGE, menu)); + menu->addAction(new MenuAction(Tr::tr("Add Package and Diagram %1").arg(elementName), elementName, + MenuAction::TYPE_ADD_PACKAGE_AND_DIAGRAM, menu)); + menu->addAction(new MenuAction(Tr::tr("Add Component Model"), elementName, + MenuAction::TYPE_ADD_COMPONENT_MODEL, menu)); connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); connect(menu, &QMenu::triggered, this, [this, filePath, topMostElementAtPos, pos, diagram]( QAction *action) { @@ -261,6 +277,7 @@ void PxNodeController::onMenuActionTriggered(PxNodeController::MenuAction *actio { qmt::MObject *newObject = nullptr; qmt::MDiagram *newDiagramInObject = nullptr; + bool dropInCurrentDiagram = false; switch (action->type) { case MenuAction::TYPE_ADD_COMPONENT: @@ -286,8 +303,8 @@ void PxNodeController::onMenuActionTriggered(PxNodeController::MenuAction *actio auto package = new qmt::MPackage(); package->setFlags(qmt::MElement::ReverseEngineered); package->setName(action->elementName); - if (!action->packageStereotype.isEmpty()) - package->setStereotypes({action->packageStereotype}); + if (!action->stereotype.isEmpty()) + package->setStereotypes({action->stereotype}); newObject = package; if (action->type == MenuAction::TYPE_ADD_PACKAGE_AND_DIAGRAM) { auto diagram = new qmt::MCanvasDiagram(); @@ -301,8 +318,8 @@ void PxNodeController::onMenuActionTriggered(PxNodeController::MenuAction *actio auto package = new qmt::MPackage(); package->setFlags(qmt::MElement::ReverseEngineered); package->setName(action->elementName); - if (!action->packageStereotype.isEmpty()) - package->setStereotypes({action->packageStereotype}); + if (!action->stereotype.isEmpty()) + package->setStereotypes({action->stereotype}); d->diagramSceneController->modelController()->undoController()->beginMergeSequence(Tr::tr("Create Component Model")); QStringList relativeElements = qmt::NameController::buildElementsPath( d->pxnodeUtilities->calcRelativePath(filePath, d->anchorFolder), true); @@ -321,34 +338,54 @@ void PxNodeController::onMenuActionTriggered(PxNodeController::MenuAction *actio d->diagramSceneController->modelController()->undoController()->endMergeSequence(); break; } + case MenuAction::TYPE_ADD_PACKAGE_LINK: + case MenuAction::TYPE_ADD_DIAGRAM_LINK: + case MenuAction::TYPE_ADD_DOCUMENT_LINK: + { + auto item = new qmt::MItem(); + item->setName(action->elementName); + item->setVariety(action->stereotype); + item->setVarietyEditable(false); + Utils::FilePath filePath = Utils::FilePath::fromString(action->filePath); + item->setLinkedFileName(filePath.relativePathFrom(Utils::FilePath::fromString(d->anchorFolder)).toString()); + newObject = item; + dropInCurrentDiagram = true; + break; + } } if (newObject) { d->diagramSceneController->modelController()->undoController()->beginMergeSequence(Tr::tr("Drop Node")); - qmt::MObject *parentForDiagram = nullptr; - QStringList relativeElements = qmt::NameController::buildElementsPath( - d->pxnodeUtilities->calcRelativePath(filePath, d->anchorFolder), - dynamic_cast<qmt::MPackage *>(newObject) != nullptr); - if (qmt::MObject *existingObject = d->pxnodeUtilities->findSameObject(relativeElements, newObject)) { - delete newObject; - newObject = nullptr; - d->diagramSceneController->addExistingModelElement(existingObject->uid(), pos, diagram); - parentForDiagram = existingObject; + if (dropInCurrentDiagram) { + auto *parentPackage = dynamic_cast<qmt::MPackage *>(diagram->owner()); + if (parentPackage) + d->diagramSceneController->dropNewModelElement(newObject, parentPackage, pos, diagram); } else { - qmt::MPackage *requestedRootPackage = d->diagramSceneController->findSuitableParentPackage(topMostElementAtPos, diagram); - qmt::MPackage *bestParentPackage = d->pxnodeUtilities->createBestMatchingPackagePath(requestedRootPackage, relativeElements); - d->diagramSceneController->dropNewModelElement(newObject, bestParentPackage, pos, diagram); - parentForDiagram = newObject; - } + qmt::MObject *parentForDiagram = nullptr; + QStringList relativeElements = qmt::NameController::buildElementsPath( + d->pxnodeUtilities->calcRelativePath(filePath, d->anchorFolder), + dynamic_cast<qmt::MPackage *>(newObject) != nullptr); + if (qmt::MObject *existingObject = d->pxnodeUtilities->findSameObject(relativeElements, newObject)) { + delete newObject; + newObject = nullptr; + d->diagramSceneController->addExistingModelElement(existingObject->uid(), pos, diagram); + parentForDiagram = existingObject; + } else { + qmt::MPackage *requestedRootPackage = d->diagramSceneController->findSuitableParentPackage(topMostElementAtPos, diagram); + qmt::MPackage *bestParentPackage = d->pxnodeUtilities->createBestMatchingPackagePath(requestedRootPackage, relativeElements); + d->diagramSceneController->dropNewModelElement(newObject, bestParentPackage, pos, diagram); + parentForDiagram = newObject; + } - // if requested and not existing then create new diagram in package - if (newDiagramInObject) { - auto package = dynamic_cast<qmt::MPackage *>(parentForDiagram); - QMT_ASSERT(package, return); - if (d->diagramSceneController->findDiagramBySearchId(package, newDiagramInObject->name())) - delete newDiagramInObject; - else - d->diagramSceneController->modelController()->addObject(package, newDiagramInObject); + // if requested and not existing then create new diagram in package + if (newDiagramInObject) { + auto package = dynamic_cast<qmt::MPackage *>(parentForDiagram); + QMT_ASSERT(package, return); + if (d->diagramSceneController->findDiagramBySearchId(package, newDiagramInObject->name())) + delete newDiagramInObject; + else + d->diagramSceneController->modelController()->addObject(package, newDiagramInObject); + } } d->diagramSceneController->modelController()->undoController()->endMergeSequence(); } |