packageitem.cpp 13.9 KB
Newer Older
Jochen Becher's avatar
Jochen Becher committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/***************************************************************************
**
** 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 <QGraphicsScene>
#include <QBrush>
#include <QPen>
#include <QFont>

#include <algorithm>


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;


67 68 69
class PackageItem::ShapeGeometry
{
public:
70 71 72
    ShapeGeometry(const QSizeF &minimumSize, const QSizeF &minimumTabSize)
        : m_minimumSize(minimumSize),
          m_minimumTabSize(minimumTabSize)
Jochen Becher's avatar
Jochen Becher committed
73 74 75
    {
    }

76 77
    QSizeF m_minimumSize;
    QSizeF m_minimumTabSize;
Jochen Becher's avatar
Jochen Becher committed
78 79 80
};


81 82
PackageItem::PackageItem(DPackage *package, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
    : ObjectItem(package, diagramSceneModel, parent),
83 84 85 86 87
      m_customIcon(0),
      m_shape(0),
      m_packageName(0),
      m_contextLabel(0),
      m_relationStarter(0)
Jochen Becher's avatar
Jochen Becher committed
88 89 90 91 92 93 94 95 96 97 98 99 100
{
}

PackageItem::~PackageItem()
{
}

void PackageItem::update()
{
    prepareGeometryChange();

    updateStereotypeIconDisplay();

101
    const Style *style = adaptedStyle(stereotypeIconId());
Jochen Becher's avatar
Jochen Becher committed
102 103

    // custom icon
104
    if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) {
105
        if (!m_customIcon) {
106
            m_customIcon = new CustomIconItem(diagramSceneModel(), this);
Jochen Becher's avatar
Jochen Becher committed
107
        }
108 109 110 111
        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());
112 113 114 115 116
        m_customIcon->setZValue(SHAPE_ZVALUE);
    } else if (m_customIcon) {
        m_customIcon->scene()->removeItem(m_customIcon);
        delete m_customIcon;
        m_customIcon = 0;
Jochen Becher's avatar
Jochen Becher committed
117 118 119
    }

    // shape
120 121 122
    if (!m_customIcon) {
        if (!m_shape) {
            m_shape = new QGraphicsPolygonItem(this);
Jochen Becher's avatar
Jochen Becher committed
123
        }
124 125
        m_shape->setBrush(style->fillBrush());
        m_shape->setPen(style->outerLinePen());
126 127 128 129 130
        m_shape->setZValue(SHAPE_ZVALUE);
    } else if (m_shape) {
        m_shape->scene()->removeItem(m_shape);
        delete m_shape;
        m_shape = 0;
Jochen Becher's avatar
Jochen Becher committed
131 132 133
    }

    // stereotypes
134
    updateStereotypes(stereotypeIconId(), stereotypeIconDisplay(), style);
Jochen Becher's avatar
Jochen Becher committed
135 136

    // package name
137 138
    if (!m_packageName) {
        m_packageName = new QGraphicsSimpleTextItem(this);
Jochen Becher's avatar
Jochen Becher committed
139
    }
140 141 142
    m_packageName->setBrush(style->textBrush());
    m_packageName->setFont(style->headerFont());
    m_packageName->setText(object()->name());
Jochen Becher's avatar
Jochen Becher committed
143 144 145

    // context
    if (showContext()) {
146 147
        if (!m_contextLabel) {
            m_contextLabel = new ContextLabelItem(this);
Jochen Becher's avatar
Jochen Becher committed
148
        }
149 150 151
        m_contextLabel->setFont(style->smallFont());
        m_contextLabel->setBrush(style->textBrush());
        m_contextLabel->setContext(object()->context());
152 153 154 155
    } else if (m_contextLabel) {
        m_contextLabel->scene()->removeItem(m_contextLabel);
        delete m_contextLabel;
        m_contextLabel = 0;
Jochen Becher's avatar
Jochen Becher committed
156 157
    }

158
    updateSelectionMarker(m_customIcon);
Jochen Becher's avatar
Jochen Becher committed
159 160 161

    // relation starters
    if (isFocusSelected()) {
162
        if (!m_relationStarter) {
163
            m_relationStarter = new RelationStarter(this, diagramSceneModel(), 0);
164 165
            scene()->addItem(m_relationStarter);
            m_relationStarter->setZValue(RELATION_STARTER_ZVALUE);
166
            m_relationStarter->addArrow(QStringLiteral("dependency"), ArrowItem::ShaftDashed, ArrowItem::HeadOpen);
Jochen Becher's avatar
Jochen Becher committed
167
        }
168 169 170 171
    } else if (m_relationStarter) {
        scene()->removeItem(m_relationStarter);
        delete m_relationStarter;
        m_relationStarter = 0;
Jochen Becher's avatar
Jochen Becher committed
172 173 174 175 176 177 178
    }

    updateAlignmentButtons();

    updateGeometry();
}

179
bool PackageItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
Jochen Becher's avatar
Jochen Becher committed
180 181
{
    QPolygonF polygon;
182
    if (m_customIcon) {
183
        // TODO use customIcon path as shape
184 185
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
186 187
        polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
    } else {
188 189
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
190
        ShapeGeometry shape = calcMinimumGeometry();
191 192 193
        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())
Jochen Becher's avatar
Jochen Becher committed
194 195
                << rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
    }
196
    return GeometryUtilities::intersect(polygon, line, intersectionPoint, intersectionLine);
Jochen Becher's avatar
Jochen Becher committed
197 198
}

199
QSizeF PackageItem::minimumSize() const
Jochen Becher's avatar
Jochen Becher committed
200 201
{
    ShapeGeometry geometry = calcMinimumGeometry();
202
    return geometry.m_minimumSize;
Jochen Becher's avatar
Jochen Becher committed
203 204
}

205
QList<ILatchable::Latch> PackageItem::horizontalLatches(ILatchable::Action action, bool grabbedItem) const
Jochen Becher's avatar
Jochen Becher committed
206
{
207
    return ObjectItem::horizontalLatches(action, grabbedItem);
Jochen Becher's avatar
Jochen Becher committed
208 209
}

210
QList<ILatchable::Latch> PackageItem::verticalLatches(ILatchable::Action action, bool grabbedItem) const
Jochen Becher's avatar
Jochen Becher committed
211
{
212
    return ObjectItem::verticalLatches(action, grabbedItem);
Jochen Becher's avatar
Jochen Becher committed
213 214 215
}

#if 0
216
QList<qreal> PackageItem::horizontalLatches() const
Jochen Becher's avatar
Jochen Becher committed
217
{
218 219
    QRectF rect = object()->rect();
    rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
220 221 222
    return QList<qreal>() << rect.left() << rect.center().x() << rect.right();
}

223
QList<qreal> PackageItem::verticalLatches() const
Jochen Becher's avatar
Jochen Becher committed
224
{
225 226
    QRectF rect = object()->rect();
    rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
227
    ShapeGeometry shape = calcMinimumGeometry();
228
    return QList<qreal>() << rect.topLeft().y() << (rect.topLeft() + QPointF(0.0, shape.m_minimumTabSize.height())).y() << rect.center().y() << rect.bottomRight().y();
Jochen Becher's avatar
Jochen Becher committed
229 230 231
}
#endif

232
QPointF PackageItem::relationStartPos() const
Jochen Becher's avatar
Jochen Becher committed
233 234 235 236
{
    return pos();
}

237
void PackageItem::relationDrawn(const QString &id, const QPointF &toScenePos, const QList<QPointF> &intermediatePoints)
Jochen Becher's avatar
Jochen Becher committed
238
{
239
    DElement *targetElement = diagramSceneModel()->findTopmostElement(toScenePos);
240
    if (targetElement) {
Jochen Becher's avatar
Jochen Becher committed
241
        if (id == QStringLiteral("dependency")) {
242 243
            DObject *dependantObject = dynamic_cast<DObject *>(targetElement);
            if (dependantObject) {
244
                diagramSceneModel()->diagramSceneController()->createDependency(object(), dependantObject, intermediatePoints, diagramSceneModel()->diagram());
Jochen Becher's avatar
Jochen Becher committed
245 246 247 248 249 250 251 252 253
            }
        }
    }
}

PackageItem::ShapeGeometry PackageItem::calcMinimumGeometry() const
{
    double width = 0.0;
    double height = 0.0;
254 255
    double tabHeight = 0.0;
    double tabWidth = 0.0;
Jochen Becher's avatar
Jochen Becher committed
256

257
    if (m_customIcon) {
258
        return ShapeGeometry(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT), QSizeF(tabWidth, tabHeight));
Jochen Becher's avatar
Jochen Becher committed
259 260
    }

261 262
    double bodyHeight = 0.0;
    double bodyWidth = 0.0;
Jochen Becher's avatar
Jochen Becher committed
263

264
    tabHeight += TAB_VERT_BORDER;
265
    if (StereotypesItem *stereotypesItem = ObjectItem::stereotypesItem()) {
266 267
        tabWidth = std::max(tabWidth, stereotypesItem->boundingRect().width() + 2 * TAB_HORIZ_BORDER);
        tabHeight += stereotypesItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
268
    }
269
    if (m_packageName) {
270 271
        tabWidth = std::max(tabWidth, m_packageName->boundingRect().width() + 2 * TAB_HORIZ_BORDER);
        tabHeight += m_packageName->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
272
    }
273 274 275 276 277
    tabHeight += TAB_VERT_BORDER;
    width = std::max(width, tabWidth + TAB_MIN_RIGHT_SPACE);
    height += tabHeight;

    bodyHeight = BODY_VERT_BORDER;
278
    if (CustomIconItem *stereotypeIconItem = ObjectItem::stereotypeIconItem()) {
279 280
        bodyWidth = std::max(bodyWidth, stereotypeIconItem->boundingRect().width() + 2 * BODY_HORIZ_BORDER);
        bodyHeight += stereotypeIconItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
281
    }
282
    if (m_contextLabel) {
283
        bodyHeight += m_contextLabel->height();
Jochen Becher's avatar
Jochen Becher committed
284
    }
285 286 287 288
    bodyHeight += BODY_VERT_BORDER;
    bodyHeight = std::max(bodyHeight, BODY_MIN_HEIGHT);
    width = std::max(width, bodyWidth);
    height += bodyHeight;
Jochen Becher's avatar
Jochen Becher committed
289

290
    return ShapeGeometry(GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT), QSizeF(tabWidth, tabHeight));
Jochen Becher's avatar
Jochen Becher committed
291 292 293 294 295 296 297 298
}

void PackageItem::updateGeometry()
{
    prepareGeometryChange();

    ShapeGeometry geometry = calcMinimumGeometry();

299 300
    double width = geometry.m_minimumSize.width();
    double height = geometry.m_minimumSize.height();
301 302
    double tabWidth = geometry.m_minimumTabSize.width();
    double tabHeight = geometry.m_minimumTabSize.height();
Jochen Becher's avatar
Jochen Becher committed
303 304

    // calc width and height
305
    if (object()->hasAutoSize()) {
306
        if (!m_customIcon) {
Jochen Becher's avatar
Jochen Becher committed
307 308 309 310 311 312 313 314
            if (width < MINIMUM_AUTO_WIDTH) {
                width = MINIMUM_AUTO_WIDTH;
            }
            if (height < MINIMUM_AUTO_HEIGHT) {
                height = MINIMUM_AUTO_HEIGHT;
            }
        }
    } else {
315
        QRectF rect = object()->rect();
Jochen Becher's avatar
Jochen Becher committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329
        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;

330
    setPos(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
331 332 333 334 335 336

    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.
337
    object()->setRect(rect);
Jochen Becher's avatar
Jochen Becher committed
338

339 340 341
    if (m_customIcon) {
        m_customIcon->setPos(left, top);
        m_customIcon->setActualSize(QSizeF(width, height));
Jochen Becher's avatar
Jochen Becher committed
342 343 344
        y += height;

        y += BODY_VERT_BORDER;
345
        if (StereotypesItem *stereotypesItem = ObjectItem::stereotypesItem()) {
346 347
            stereotypesItem->setPos(-stereotypesItem->boundingRect().width() / 2.0, y);
            y += stereotypesItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
348
        }
349 350 351
        if (m_packageName) {
            m_packageName->setPos(-m_packageName->boundingRect().width() / 2.0, y);
            y += m_packageName->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
352
        }
353 354 355 356
        if (m_contextLabel) {
            m_contextLabel->resetMaxWidth();
            m_contextLabel->setPos(-m_contextLabel->boundingRect().width() / 2.0, y);
            y += m_contextLabel->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
357
        }
358
    } else if (m_shape) {
Jochen Becher's avatar
Jochen Becher committed
359 360
        QPolygonF polygon;
        polygon << rect.topLeft()
361 362 363
                << QPointF(left + tabWidth, top)
                << QPointF(left + tabWidth, top + tabHeight)
                << QPointF(right, top + tabHeight)
Jochen Becher's avatar
Jochen Becher committed
364 365
                << rect.bottomRight()
                << rect.bottomLeft();
366
        m_shape->setPolygon(polygon);
Jochen Becher's avatar
Jochen Becher committed
367 368

        y += TAB_VERT_BORDER;
369
        if (StereotypesItem *stereotypesItem = ObjectItem::stereotypesItem()) {
370 371
            stereotypesItem->setPos(left + TAB_HORIZ_BORDER, y);
            y += stereotypesItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
372
        }
373 374 375
        if (m_packageName) {
            m_packageName->setPos(left + TAB_HORIZ_BORDER, y);
            y += m_packageName->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
376 377 378
        }
        y += TAB_VERT_BORDER;
        y += BODY_VERT_BORDER;
379
        if (CustomIconItem *stereotypeIconItem = ObjectItem::stereotypeIconItem()) {
380 381
            stereotypeIconItem->setPos(right - stereotypeIconItem->boundingRect().width() - BODY_HORIZ_BORDER, y);
            y += stereotypeIconItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
382
        }
383 384 385 386
        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();
Jochen Becher's avatar
Jochen Becher committed
387 388 389 390 391
        }
    }

    updateSelectionMarkerGeometry(rect);

392 393
    if (m_relationStarter) {
        m_relationStarter->setPos(mapToScene(QPointF(right + 8.0, top)));
Jochen Becher's avatar
Jochen Becher committed
394 395 396 397 398 399 400 401
    }

    updateAlignmentButtonsGeometry(rect);

    updateDepth();
}

}