componentitem.cpp 13.3 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
/***************************************************************************
**
** 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 "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"
39
#include "qmt/diagram_scene/parts/editabletextitem.h"
Jochen Becher's avatar
Jochen Becher committed
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
67
#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;

68
ComponentItem::ComponentItem(DComponent *component, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
69
    : ObjectItem(component, diagramSceneModel, parent)
Jochen Becher's avatar
Jochen Becher committed
70
71
72
73
74
75
76
77
78
79
80
81
{
}

ComponentItem::~ComponentItem()
{
}

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

82
    const Style *style = adaptedStyle(stereotypeIconId());
Jochen Becher's avatar
Jochen Becher committed
83
84

    // custom icon
85
    if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) {
86
        if (!m_customIcon)
87
88
89
90
91
            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());
92
93
94
95
96
        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
97
98
99
    }

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

    // stereotypes
143
    updateStereotypes(stereotypeIconId(), stereotypeIconDisplay(), style);
Jochen Becher's avatar
Jochen Becher committed
144
145

    // component name
146
    updateNameItem(style);
Jochen Becher's avatar
Jochen Becher committed
147
148
149

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

161
    updateSelectionMarker(m_customIcon);
Jochen Becher's avatar
Jochen Becher committed
162
163
164

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

    updateAlignmentButtons();
    updateGeometry();
}

182
bool ComponentItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
Jochen Becher's avatar
Jochen Becher committed
183
184
{
    QPolygonF polygon;
185
    if (m_customIcon) {
186
        // TODO use customIcon path as shape
187
188
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
189
        polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
190
    } else if (hasPlainShape()) {
191
192
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
193
194
        polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft() << rect.topLeft();
    } else {
195
196
        QRectF rect = object()->rect();
        rect.translate(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
197
198
199
200
201
202
203
204
205
206
        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();
    }
207
    return GeometryUtilities::intersect(polygon, line, intersectionPoint, intersectionLine);
Jochen Becher's avatar
Jochen Becher committed
208
209
}

210
QSizeF ComponentItem::minimumSize() const
Jochen Becher's avatar
Jochen Becher committed
211
212
213
214
{
    return calcMinimumGeometry();
}

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

220
QList<ILatchable::Latch> ComponentItem::verticalLatches(ILatchable::Action action, bool grabbedItem) const
Jochen Becher's avatar
Jochen Becher committed
221
{
222
    return ObjectItem::verticalLatches(action, grabbedItem);
Jochen Becher's avatar
Jochen Becher committed
223
224
}

225
QPointF ComponentItem::relationStartPos() const
Jochen Becher's avatar
Jochen Becher committed
226
227
228
229
{
    return pos();
}

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

bool ComponentItem::hasPlainShape() const
{
Jochen Becher's avatar
Jochen Becher committed
244
    auto diagramComponent = dynamic_cast<DComponent *>(object());
245
    QMT_CHECK(diagramComponent);
246
    return diagramComponent->isPlainShape();
Jochen Becher's avatar
Jochen Becher committed
247
248
249
250
251
252
253
}

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

254
    if (m_customIcon) {
255
        return stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT);
256
    }
Jochen Becher's avatar
Jochen Becher committed
257
258

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

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

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

315
    setPos(object()->pos());
Jochen Becher's avatar
Jochen Becher committed
316
317
318
319
320
321

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

324
325
326
    if (m_customIcon) {
        m_customIcon->setPos(left, top);
        m_customIcon->setActualSize(QSizeF(width, height));
Jochen Becher's avatar
Jochen Becher committed
327
328
329
        y += height;
    }

330
    if (m_shape)
331
        m_shape->setRect(rect);
Jochen Becher's avatar
Jochen Becher committed
332

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

339
    if (m_lowerRect) {
340
341
        QRectF lowerRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
        m_lowerRect->setRect(lowerRect);
342
        m_lowerRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE);
Jochen Becher's avatar
Jochen Becher committed
343
344
345
    }

    y += BODY_VERT_BORDER;
346
    if (CustomIconItem *stereotypeIconItem = this->stereotypeIconItem()) {
347
348
        stereotypeIconItem->setPos(right - stereotypeIconItem->boundingRect().width() - BODY_HORIZ_BORDER, y);
        y += stereotypeIconItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
349
    }
350
    if (StereotypesItem *stereotypesItem = this->stereotypesItem()) {
351
352
        stereotypesItem->setPos(-stereotypesItem->boundingRect().width() / 2.0, y);
        y += stereotypesItem->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
353
    }
354
355
356
    if (nameItem()) {
        nameItem()->setPos(-nameItem()->boundingRect().width() / 2.0, y);
        y += nameItem()->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
357
    }
358
359
360
    if (m_contextLabel) {
        if (m_customIcon) {
            m_contextLabel->resetMaxWidth();
Jochen Becher's avatar
Jochen Becher committed
361
        } else {
362
363
            double maxContextWidth = width - 2 * BODY_HORIZ_BORDER - (hasPlainShape() ? 0 : RECT_WIDTH);
            m_contextLabel->setMaxWidth(maxContextWidth);
Jochen Becher's avatar
Jochen Becher committed
364
        }
365
366
        m_contextLabel->setPos(-m_contextLabel->boundingRect().width() / 2.0, y);
        y += m_contextLabel->boundingRect().height();
Jochen Becher's avatar
Jochen Becher committed
367
368
369
370
    }

    updateSelectionMarkerGeometry(rect);

371
    if (m_relationStarter)
372
        m_relationStarter->setPos(mapToScene(QPointF(right + 8.0, top)));
Jochen Becher's avatar
Jochen Becher committed
373
374
375
376
377

    updateAlignmentButtonsGeometry(rect);
    updateDepth();
}

378
} // namespace qmt