// Copyright (C) 2016 Jochen Becher // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "componentitem.h" #include "qmt/diagram_controller/diagramcontroller.h" #include "qmt/diagram/dcomponent.h" #include "qmt/diagram_scene/diagramsceneconstants.h" #include "qmt/diagram_scene/diagramscenemodel.h" #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/stereotypesitem.h" #include "qmt/infrastructure/geometryutilities.h" #include "qmt/infrastructure/qmtassert.h" #include "qmt/stereotype/stereotypecontroller.h" #include "qmt/stereotype/stereotypeicon.h" #include "qmt/style/style.h" #include "qmt/tasks/diagramscenecontroller.h" #include #include #include #include #include #include #include namespace qmt { static const qreal RECT_HEIGHT = 15.0; static const qreal RECT_WIDTH = 45.0; static const qreal UPPER_RECT_Y = 10.0; static const qreal RECT_Y_DISTANCE = 10.0; static const qreal LOWER_RECT_MIN_Y = 10.0; 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", component, diagramSceneModel, parent) { } ComponentItem::~ComponentItem() { } void ComponentItem::update() { prepareGeometryChange(); updateStereotypeIconDisplay(); 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; } // shape bool deleteRects = false; if (!m_customIcon) { if (!m_shape) m_shape = new QGraphicsRectItem(this); m_shape->setBrush(style->fillBrush()); m_shape->setPen(style->outerLinePen()); m_shape->setZValue(SHAPE_ZVALUE); if (!hasPlainShape()) { if (!m_upperRect) m_upperRect = new QGraphicsRectItem(this); m_upperRect->setBrush(style->fillBrush()); m_upperRect->setPen(style->outerLinePen()); m_upperRect->setZValue(SHAPE_DETAILS_ZVALUE); if (!m_lowerRect) m_lowerRect = new QGraphicsRectItem(this); m_lowerRect->setBrush(style->fillBrush()); m_lowerRect->setPen(style->outerLinePen()); m_lowerRect->setZValue(SHAPE_DETAILS_ZVALUE); } else { deleteRects = true; } } else { deleteRects = true; if (m_shape) { m_shape->scene()->removeItem(m_shape); delete m_shape; m_shape = nullptr; } } if (deleteRects) { if (m_lowerRect) { m_lowerRect->scene()->removeItem(m_lowerRect); delete m_lowerRect; m_lowerRect = nullptr; } if (m_upperRect) { m_upperRect->scene()->removeItem(m_upperRect); delete m_upperRect; m_upperRect = nullptr; } } // stereotypes updateStereotypes(stereotypeIconId(), stereotypeIconDisplay(), style); // component name updateNameItem(style); // context if (!suppressTextDisplay() && showContext()) { if (!m_contextLabel) m_contextLabel = new ContextLabelItem(this); m_contextLabel->setFont(style->smallFont()); m_contextLabel->setBrush(style->textBrush()); m_contextLabel->setContext(object()->context()); } else if (m_contextLabel) { m_contextLabel->scene()->removeItem(m_contextLabel); delete m_contextLabel; m_contextLabel = nullptr; } updateSelectionMarker(m_customIcon); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); } bool ComponentItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { if (m_customIcon) { QList polygons = m_customIcon->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { if (nameItem()) { QPolygonF polygon(nameItem()->boundingRect()); polygon.translate(object()->pos() + nameItem()->pos()); polygons.append(polygon); } if (m_contextLabel) { QPolygonF polygon(m_contextLabel->boundingRect()); polygon.translate(object()->pos() + m_contextLabel->pos()); polygons.append(polygon); } } return GeometryUtilities::intersect(polygons, line, nullptr, intersectionPoint, intersectionLine); } QPolygonF polygon; if (hasPlainShape()) { QRectF rect = object()->rect(); rect.translate(object()->pos()); polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft() << rect.topLeft(); } else { QRectF rect = object()->rect(); rect.translate(object()->pos()); polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft() << rect.topLeft() + QPointF(0, UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT) << rect.topLeft() + QPointF(-RECT_WIDTH * 0.5, UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT) << rect.topLeft() + QPointF(-RECT_WIDTH * 0.5, UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE) << rect.topLeft() + QPointF(0, UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE) << rect.topLeft() + QPointF(0, UPPER_RECT_Y + RECT_HEIGHT) << rect.topLeft() + QPointF(-RECT_WIDTH * 0.5, UPPER_RECT_Y + RECT_HEIGHT) << rect.topLeft() + QPointF(-RECT_WIDTH * 0.5, UPPER_RECT_Y) << rect.topLeft() + QPointF(0, UPPER_RECT_Y) << rect.topLeft(); } return GeometryUtilities::intersect(polygon, line, intersectionPoint, intersectionLine); } QSizeF ComponentItem::minimumSize() const { return calcMinimumGeometry(); } QList ComponentItem::horizontalLatches(ILatchable::Action action, bool grabbedItem) const { return ObjectItem::horizontalLatches(action, grabbedItem); } QList ComponentItem::verticalLatches(ILatchable::Action action, bool grabbedItem) const { return ObjectItem::verticalLatches(action, grabbedItem); } bool ComponentItem::hasPlainShape() const { auto diagramComponent = dynamic_cast(object()); QMT_ASSERT(diagramComponent, return false); return diagramComponent->isPlainShape(); } QSizeF ComponentItem::calcMinimumGeometry() const { double width = 0.0; double height = 0.0; if (m_customIcon) { QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), 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(); } height += BODY_VERT_BORDER; if (CustomIconItem *stereotypeIconItem = this->stereotypeIconItem()) { width = std::max(width, stereotypeIconItem->boundingRect().width()); height += stereotypeIconItem->boundingRect().height(); } if (StereotypesItem *stereotypesItem = this->stereotypesItem()) { width = std::max(width, stereotypesItem->boundingRect().width()); height += stereotypesItem->boundingRect().height(); } if (nameItem()) { width = std::max(width, nameItem()->boundingRect().width()); height += nameItem()->boundingRect().height(); } if (m_contextLabel) height += m_contextLabel->height(); height += BODY_VERT_BORDER; if (!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) height = minHeight; } else { width = BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER; } return GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT); } void ComponentItem::updateGeometry() { prepareGeometryChange(); // calc width and height double width = 0.0; double height = 0.0; QSizeF geometry = calcMinimumGeometry(); width = geometry.width(); height = geometry.height(); if (object()->isAutoSized()) { // nothing } else { QRectF rect = object()->rect(); if (rect.width() > width) width = rect.width(); if (rect.height() > height) height = rect.height(); } // update sizes and positions double left = -width / 2.0; double right = width / 2.0; double top = -height / 2.0; double y = top; setPos(object()->pos()); QRectF rect(left, top, width, height); // the object is updated without calling DiagramController intentionally. // attribute rect is not a real attribute stored on DObject but // 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 (m_shape) m_shape->setRect(rect); if (m_upperRect) { QRectF upperRect(0, 0, RECT_WIDTH, RECT_HEIGHT); m_upperRect->setRect(upperRect); m_upperRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y); } if (m_lowerRect) { QRectF lowerRect(0, 0, RECT_WIDTH, RECT_HEIGHT); m_lowerRect->setRect(lowerRect); m_lowerRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE); } if (m_customIcon) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; break; case qmt::StereotypeIcon::TextalignCenter: { double h = 0.0; if (CustomIconItem *stereotypeIconItem = this->stereotypeIconItem()) h += stereotypeIconItem->boundingRect().height(); if (StereotypesItem *stereotypesItem = this->stereotypesItem()) h += stereotypesItem->boundingRect().height(); if (nameItem()) h += nameItem()->boundingRect().height(); if (m_contextLabel) h += m_contextLabel->height(); y = top + (height - h) / 2.0; break; } case qmt::StereotypeIcon::TextalignNone: // nothing to do break; case qmt::StereotypeIcon::TextalignTop: y += BODY_VERT_BORDER; break; } } else { y += BODY_VERT_BORDER; } if (CustomIconItem *stereotypeIconItem = this->stereotypeIconItem()) { stereotypeIconItem->setPos(right - stereotypeIconItem->boundingRect().width() - BODY_HORIZ_BORDER, y); y += stereotypeIconItem->boundingRect().height(); } if (StereotypesItem *stereotypesItem = this->stereotypesItem()) { stereotypesItem->setPos(-stereotypesItem->boundingRect().width() / 2.0, y); y += stereotypesItem->boundingRect().height(); } if (nameItem()) { nameItem()->setPos(-nameItem()->boundingRect().width() / 2.0, y); y += nameItem()->boundingRect().height(); } if (m_contextLabel) { if (m_customIcon) { m_contextLabel->resetMaxWidth(); } else { double maxContextWidth = width - 2 * BODY_HORIZ_BORDER - (hasPlainShape() ? 0 : RECT_WIDTH); m_contextLabel->setMaxWidth(maxContextWidth); } m_contextLabel->setPos(-m_contextLabel->boundingRect().width() / 2.0, y); } updateSelectionMarkerGeometry(rect); updateRelationStarterGeometry(rect); updateAlignmentButtonsGeometry(rect); updateDepth(); } } // namespace qmt