componentitem.cpp 12.9 KB
Newer Older
1
/****************************************************************************
Jochen Becher's avatar
Jochen Becher committed
2
**
3
4
** Copyright (C) 2016 Jochen Becher
** Contact: https://www.qt.io/licensing/
Jochen Becher's avatar
Jochen Becher committed
5
6
7
8
9
10
11
**
** 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
12
13
14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
Jochen Becher's avatar
Jochen Becher committed
15
**
16
17
18
19
20
21
22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
Jochen Becher's avatar
Jochen Becher committed
23
24
25
26
27
28
29
30
31
32
33
**
****************************************************************************/

#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"
34
#include "qmt/diagram_scene/parts/editabletextitem.h"
Jochen Becher's avatar
Jochen Becher committed
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
#include "qmt/diagram_scene/parts/relationstarter.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 <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsSimpleTextItem>
#include <QBrush>
#include <QPen>
#include <QFont>

#include <algorithm>

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;

63
ComponentItem::ComponentItem(DComponent *component, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
64
    : ObjectItem(component, diagramSceneModel, parent)
Jochen Becher's avatar
Jochen Becher committed
65
66
67
68
69
70
71
72
73
74
75
76
{
}

ComponentItem::~ComponentItem()
{
}

void ComponentItem::update()
{
    prepareGeometryChange();
    updateStereotypeIconDisplay();

77
    const Style *style = adaptedStyle(stereotypeIconId());
Jochen Becher's avatar
Jochen Becher committed
78
79

    // custom icon
80
    if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) {
81
        if (!m_customIcon)
82
83
84
85
86
            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());
87
88
89
90
91
        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
92
93
94
    }

    // shape
95
    bool deleteRects = false;
96
    if (!m_customIcon) {
97
        if (!m_shape)
98
            m_shape = new QGraphicsRectItem(this);
99
100
        m_shape->setBrush(style->fillBrush());
        m_shape->setPen(style->outerLinePen());
101
        m_shape->setZValue(SHAPE_ZVALUE);
Jochen Becher's avatar
Jochen Becher committed
102
        if (!hasPlainShape()) {
103
            if (!m_upperRect)
104
                m_upperRect = new QGraphicsRectItem(this);
105
106
            m_upperRect->setBrush(style->fillBrush());
            m_upperRect->setPen(style->outerLinePen());
107
            m_upperRect->setZValue(SHAPE_DETAILS_ZVALUE);
108
            if (!m_lowerRect)
109
                m_lowerRect = new QGraphicsRectItem(this);
110
111
            m_lowerRect->setBrush(style->fillBrush());
            m_lowerRect->setPen(style->outerLinePen());
112
            m_lowerRect->setZValue(SHAPE_DETAILS_ZVALUE);
Jochen Becher's avatar
Jochen Becher committed
113
        } else {
114
            deleteRects = true;
Jochen Becher's avatar
Jochen Becher committed
115
116
        }
    } else {
117
        deleteRects = true;
118
119
120
121
        if (m_shape) {
            m_shape->scene()->removeItem(m_shape);
            delete m_shape;
            m_shape = 0;
Jochen Becher's avatar
Jochen Becher committed
122
123
        }
    }
124
    if (deleteRects) {
125
126
127
128
        if (m_lowerRect) {
            m_lowerRect->scene()->removeItem(m_lowerRect);
            delete m_lowerRect;
            m_lowerRect = 0;
Jochen Becher's avatar
Jochen Becher committed
129
        }
130
131
132
133
        if (m_upperRect) {
            m_upperRect->scene()->removeItem(m_upperRect);
            delete m_upperRect;
            m_upperRect = 0;
Jochen Becher's avatar
Jochen Becher committed
134
135
136
137
        }
    }

    // stereotypes
138
    updateStereotypes(stereotypeIconId(), stereotypeIconDisplay(), style);
Jochen Becher's avatar
Jochen Becher committed
139
140

    // component name
141
    updateNameItem(style);
Jochen Becher's avatar
Jochen Becher committed
142
143
144

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

156
    updateSelectionMarker(m_customIcon);
Jochen Becher's avatar
Jochen Becher committed
157
158
159

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

    updateAlignmentButtons();
    updateGeometry();
}

177
bool ComponentItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
Jochen Becher's avatar
Jochen Becher committed
178
179
{
    QPolygonF polygon;
180
    if (m_customIcon) {
181
        // TODO use customIcon path as shape
182
183
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
184
        polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
185
    } else if (hasPlainShape()) {
186
187
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
188
189
        polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
    } else {
190
191
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
192
193
194
195
196
197
198
199
200
201
        polygon << rect.topLeft()
                << rect.topRight()
                << rect.bottomRight()
                << rect.bottomLeft()
                << rect.bottomLeft() + QPointF(0, UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT)
                << rect.bottomLeft() + QPointF(-RECT_WIDTH * 0.5, UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT)
                << rect.bottomLeft() + QPointF(-RECT_WIDTH * 0.5, UPPER_RECT_Y)
                << rect.bottomLeft() + QPointF(0, UPPER_RECT_Y)
                << rect.topLeft();
    }
202
    return GeometryUtilities::intersect(polygon, line, intersectionPoint, intersectionLine);
Jochen Becher's avatar
Jochen Becher committed
203
204
}

205
QSizeF ComponentItem::minimumSize() const
Jochen Becher's avatar
Jochen Becher committed
206
207
208
209
{
    return calcMinimumGeometry();
}

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

215
QList<ILatchable::Latch> ComponentItem::verticalLatches(ILatchable::Action action, bool grabbedItem) const
Jochen Becher's avatar
Jochen Becher committed
216
{
217
    return ObjectItem::verticalLatches(action, grabbedItem);
Jochen Becher's avatar
Jochen Becher committed
218
219
}

220
QPointF ComponentItem::relationStartPos() const
Jochen Becher's avatar
Jochen Becher committed
221
222
223
224
{
    return pos();
}

225
void ComponentItem::relationDrawn(const QString &id, const QPointF &toScenePos, const QList<QPointF> &intermediatePoints)
Jochen Becher's avatar
Jochen Becher committed
226
{
227
    DElement *targetElement = diagramSceneModel()->findTopmostElement(toScenePos);
228
    if (targetElement) {
Jochen Becher's avatar
Jochen Becher committed
229
       if (id == QStringLiteral("dependency")) {
Jochen Becher's avatar
Jochen Becher committed
230
            auto dependantObject = dynamic_cast<DObject *>(targetElement);
231
            if (dependantObject)
232
                diagramSceneModel()->diagramSceneController()->createDependency(object(), dependantObject, intermediatePoints, diagramSceneModel()->diagram());
Jochen Becher's avatar
Jochen Becher committed
233
234
235
236
237
238
        }
    }
}

bool ComponentItem::hasPlainShape() const
{
Jochen Becher's avatar
Jochen Becher committed
239
    auto diagramComponent = dynamic_cast<DComponent *>(object());
240
    QMT_CHECK(diagramComponent);
241
    return diagramComponent->isPlainShape();
Jochen Becher's avatar
Jochen Becher committed
242
243
244
245
246
247
248
}

QSizeF ComponentItem::calcMinimumGeometry() const
{
    double width = 0.0;
    double height = 0.0;

249
    if (m_customIcon) {
250
        return stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT);
251
    }
Jochen Becher's avatar
Jochen Becher committed
252
253

    height += BODY_VERT_BORDER;
254
    if (CustomIconItem *stereotypeIconItem = this->stereotypeIconItem()) {
255
256
        width = std::max(width, stereotypeIconItem->boundingRect().width());
        height += stereotypeIconItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
257
    }
258
    if (StereotypesItem *stereotypesItem = this->stereotypesItem()) {
259
260
        width = std::max(width, stereotypesItem->boundingRect().width());
        height += stereotypesItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
261
    }
262
263
264
    if (nameItem()) {
        width = std::max(width, nameItem()->boundingRect().width());
        height += nameItem()->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
265
    }
266
    if (m_contextLabel)
267
        height += m_contextLabel->height();
Jochen Becher's avatar
Jochen Becher committed
268
269
270
271
    height += BODY_VERT_BORDER;

    if (!hasPlainShape()) {
        width = RECT_WIDTH * 0.5 + BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER + RECT_WIDTH * 0.5;
272
        double minHeight = UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT + LOWER_RECT_MIN_Y;
273
        if (height < minHeight)
274
            height = minHeight;
Jochen Becher's avatar
Jochen Becher committed
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
    } 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();

294
    if (object()->isAutoSized()) {
Jochen Becher's avatar
Jochen Becher committed
295
296
        // nothing
    } else {
297
        QRectF rect = object()->rect();
298
        if (rect.width() > width)
Jochen Becher's avatar
Jochen Becher committed
299
            width = rect.width();
300
        if (rect.height() > height)
Jochen Becher's avatar
Jochen Becher committed
301
302
303
304
305
306
307
308
309
            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;

310
    setPos(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
311
312
313
314
315
316

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

319
320
321
    if (m_customIcon) {
        m_customIcon->setPos(left, top);
        m_customIcon->setActualSize(QSizeF(width, height));
Jochen Becher's avatar
Jochen Becher committed
322
323
324
        y += height;
    }

325
    if (m_shape)
326
        m_shape->setRect(rect);
Jochen Becher's avatar
Jochen Becher committed
327

328
    if (m_upperRect) {
329
330
        QRectF upperRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
        m_upperRect->setRect(upperRect);
331
        m_upperRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y);
Jochen Becher's avatar
Jochen Becher committed
332
333
    }

334
    if (m_lowerRect) {
335
336
        QRectF lowerRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
        m_lowerRect->setRect(lowerRect);
337
        m_lowerRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE);
Jochen Becher's avatar
Jochen Becher committed
338
339
340
    }

    y += BODY_VERT_BORDER;
341
    if (CustomIconItem *stereotypeIconItem = this->stereotypeIconItem()) {
342
343
        stereotypeIconItem->setPos(right - stereotypeIconItem->boundingRect().width() - BODY_HORIZ_BORDER, y);
        y += stereotypeIconItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
344
    }
345
    if (StereotypesItem *stereotypesItem = this->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 (nameItem()) {
        nameItem()->setPos(-nameItem()->boundingRect().width() / 2.0, y);
        y += nameItem()->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
352
    }
353
354
355
    if (m_contextLabel) {
        if (m_customIcon) {
            m_contextLabel->resetMaxWidth();
Jochen Becher's avatar
Jochen Becher committed
356
        } else {
357
358
            double maxContextWidth = width - 2 * BODY_HORIZ_BORDER - (hasPlainShape() ? 0 : RECT_WIDTH);
            m_contextLabel->setMaxWidth(maxContextWidth);
Jochen Becher's avatar
Jochen Becher committed
359
        }
360
        m_contextLabel->setPos(-m_contextLabel->boundingRect().width() / 2.0, y);
Jochen Becher's avatar
Jochen Becher committed
361
362
363
364
    }

    updateSelectionMarkerGeometry(rect);

365
    if (m_relationStarter)
366
        m_relationStarter->setPos(mapToScene(QPointF(right + 8.0, top)));
Jochen Becher's avatar
Jochen Becher committed
367
368
369
370
371

    updateAlignmentButtonsGeometry(rect);
    updateDepth();
}

372
} // namespace qmt