/*************************************************************************** ** ** Copyright (C) 2015 Jochen Becher ** Contact: http://www.qt.io/licensing ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms and ** conditions see http://www.qt.io/terms-conditions. For further information ** use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "packageitem.h" #include "qmt/diagram_controller/diagramcontroller.h" #include "qmt/diagram/dpackage.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/relationstarter.h" #include "qmt/diagram_scene/parts/stereotypesitem.h" #include "qmt/infrastructure/geometryutilities.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 namespace qmt { static const qreal TAB_HORIZ_BORDER = 4.0; static const qreal TAB_VERT_BORDER = 4.0; static const qreal TAB_MIN_RIGHT_SPACE = 16.0; static const qreal BODY_VERT_BORDER = 4.0; static const qreal BODY_HORIZ_BORDER = 4.0; static const qreal BODY_MIN_HEIGHT = 24.0; static const qreal MINIMUM_AUTO_WIDTH = 100.0; static const qreal MINIMUM_AUTO_HEIGHT = 70.0; class PackageItem::ShapeGeometry { public: ShapeGeometry(const QSizeF &minimumSize, const QSizeF &minimumTabSize) : m_minimumSize(minimumSize), m_minimumTabSize(minimumTabSize) { } QSizeF m_minimumSize; QSizeF m_minimumTabSize; }; PackageItem::PackageItem(DPackage *package, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent) : ObjectItem(package, diagramSceneModel, parent), m_customIcon(0), m_shape(0), m_packageName(0), m_contextLabel(0), m_relationStarter(0) { } PackageItem::~PackageItem() { } void PackageItem::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 = 0; } // shape if (!m_customIcon) { if (!m_shape) { m_shape = new QGraphicsPolygonItem(this); } m_shape->setBrush(style->fillBrush()); m_shape->setPen(style->outerLinePen()); m_shape->setZValue(SHAPE_ZVALUE); } else if (m_shape) { m_shape->scene()->removeItem(m_shape); delete m_shape; m_shape = 0; } // stereotypes updateStereotypes(stereotypeIconId(), stereotypeIconDisplay(), style); // package name if (!m_packageName) { m_packageName = new QGraphicsSimpleTextItem(this); } m_packageName->setBrush(style->textBrush()); m_packageName->setFont(style->headerFont()); m_packageName->setText(object()->name()); // context if (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 = 0; } updateSelectionMarker(m_customIcon); // relation starters if (isFocusSelected()) { if (!m_relationStarter) { m_relationStarter = new RelationStarter(this, diagramSceneModel(), 0); scene()->addItem(m_relationStarter); m_relationStarter->setZValue(RELATION_STARTER_ZVALUE); m_relationStarter->addArrow(QStringLiteral("dependency"), ArrowItem::ShaftDashed, ArrowItem::HeadOpen); } } else if (m_relationStarter) { scene()->removeItem(m_relationStarter); delete m_relationStarter; m_relationStarter = 0; } updateAlignmentButtons(); updateGeometry(); } bool PackageItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { QPolygonF polygon; if (m_customIcon) { // TODO use customIcon path as shape 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()); ShapeGeometry shape = calcMinimumGeometry(); polygon << rect.topLeft() << (rect.topLeft() + QPointF(shape.m_minimumTabSize.width(), 0.0)) << (rect.topLeft() + QPointF(shape.m_minimumTabSize.width(), shape.m_minimumTabSize.height())) << rect.topRight() + QPointF(0.0, shape.m_minimumTabSize.height()) << rect.bottomRight() << rect.bottomLeft() << rect.topLeft(); } return GeometryUtilities::intersect(polygon, line, intersectionPoint, intersectionLine); } QSizeF PackageItem::minimumSize() const { ShapeGeometry geometry = calcMinimumGeometry(); return geometry.m_minimumSize; } QList PackageItem::horizontalLatches(ILatchable::Action action, bool grabbedItem) const { return ObjectItem::horizontalLatches(action, grabbedItem); } QList PackageItem::verticalLatches(ILatchable::Action action, bool grabbedItem) const { return ObjectItem::verticalLatches(action, grabbedItem); } #if 0 QList PackageItem::horizontalLatches() const { QRectF rect = object()->rect(); rect.translate(object()->pos()); return QList() << rect.left() << rect.center().x() << rect.right(); } QList PackageItem::verticalLatches() const { QRectF rect = object()->rect(); rect.translate(object()->pos()); ShapeGeometry shape = calcMinimumGeometry(); return QList() << rect.topLeft().y() << (rect.topLeft() + QPointF(0.0, shape.m_minimumTabSize.height())).y() << rect.center().y() << rect.bottomRight().y(); } #endif QPointF PackageItem::relationStartPos() const { return pos(); } void PackageItem::relationDrawn(const QString &id, const QPointF &toScenePos, const QList &intermediatePoints) { DElement *targetElement = diagramSceneModel()->findTopmostElement(toScenePos); if (targetElement) { if (id == QStringLiteral("dependency")) { DObject *dependantObject = dynamic_cast(targetElement); if (dependantObject) { diagramSceneModel()->diagramSceneController()->createDependency(object(), dependantObject, intermediatePoints, diagramSceneModel()->diagram()); } } } } PackageItem::ShapeGeometry PackageItem::calcMinimumGeometry() const { double width = 0.0; double height = 0.0; double tabHeight = 0.0; double tabWidth = 0.0; if (m_customIcon) { return ShapeGeometry(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT), QSizeF(tabWidth, tabHeight)); } double bodyHeight = 0.0; double bodyWidth = 0.0; tabHeight += TAB_VERT_BORDER; if (StereotypesItem *stereotypesItem = ObjectItem::stereotypesItem()) { tabWidth = std::max(tabWidth, stereotypesItem->boundingRect().width() + 2 * TAB_HORIZ_BORDER); tabHeight += stereotypesItem->boundingRect().height(); } if (m_packageName) { tabWidth = std::max(tabWidth, m_packageName->boundingRect().width() + 2 * TAB_HORIZ_BORDER); tabHeight += m_packageName->boundingRect().height(); } tabHeight += TAB_VERT_BORDER; width = std::max(width, tabWidth + TAB_MIN_RIGHT_SPACE); height += tabHeight; bodyHeight = BODY_VERT_BORDER; if (CustomIconItem *stereotypeIconItem = ObjectItem::stereotypeIconItem()) { bodyWidth = std::max(bodyWidth, stereotypeIconItem->boundingRect().width() + 2 * BODY_HORIZ_BORDER); bodyHeight += stereotypeIconItem->boundingRect().height(); } if (m_contextLabel) { bodyHeight += m_contextLabel->height(); } bodyHeight += BODY_VERT_BORDER; bodyHeight = std::max(bodyHeight, BODY_MIN_HEIGHT); width = std::max(width, bodyWidth); height += bodyHeight; return ShapeGeometry(GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT), QSizeF(tabWidth, tabHeight)); } void PackageItem::updateGeometry() { prepareGeometryChange(); ShapeGeometry geometry = calcMinimumGeometry(); double width = geometry.m_minimumSize.width(); double height = geometry.m_minimumSize.height(); double tabWidth = geometry.m_minimumTabSize.width(); double tabHeight = geometry.m_minimumTabSize.height(); // calc width and height if (object()->hasAutoSize()) { if (!m_customIcon) { if (width < MINIMUM_AUTO_WIDTH) { width = MINIMUM_AUTO_WIDTH; } if (height < MINIMUM_AUTO_HEIGHT) { height = MINIMUM_AUTO_HEIGHT; } } } 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)); y += height; y += BODY_VERT_BORDER; if (StereotypesItem *stereotypesItem = ObjectItem::stereotypesItem()) { stereotypesItem->setPos(-stereotypesItem->boundingRect().width() / 2.0, y); y += stereotypesItem->boundingRect().height(); } if (m_packageName) { m_packageName->setPos(-m_packageName->boundingRect().width() / 2.0, y); y += m_packageName->boundingRect().height(); } if (m_contextLabel) { m_contextLabel->resetMaxWidth(); m_contextLabel->setPos(-m_contextLabel->boundingRect().width() / 2.0, y); y += m_contextLabel->boundingRect().height(); } } else if (m_shape) { QPolygonF polygon; polygon << rect.topLeft() << QPointF(left + tabWidth, top) << QPointF(left + tabWidth, top + tabHeight) << QPointF(right, top + tabHeight) << rect.bottomRight() << rect.bottomLeft(); m_shape->setPolygon(polygon); y += TAB_VERT_BORDER; if (StereotypesItem *stereotypesItem = ObjectItem::stereotypesItem()) { stereotypesItem->setPos(left + TAB_HORIZ_BORDER, y); y += stereotypesItem->boundingRect().height(); } if (m_packageName) { m_packageName->setPos(left + TAB_HORIZ_BORDER, y); y += m_packageName->boundingRect().height(); } y += TAB_VERT_BORDER; y += BODY_VERT_BORDER; if (CustomIconItem *stereotypeIconItem = ObjectItem::stereotypeIconItem()) { stereotypeIconItem->setPos(right - stereotypeIconItem->boundingRect().width() - BODY_HORIZ_BORDER, y); y += stereotypeIconItem->boundingRect().height(); } if (m_contextLabel) { m_contextLabel->setMaxWidth(width - 2 * BODY_HORIZ_BORDER); m_contextLabel->setPos(-m_contextLabel->boundingRect().width() / 2.0, y); y += m_contextLabel->boundingRect().height(); } } updateSelectionMarkerGeometry(rect); if (m_relationStarter) { m_relationStarter->setPos(mapToScene(QPointF(right + 8.0, top))); } updateAlignmentButtonsGeometry(rect); updateDepth(); } }