1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Jochen Becher
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "relationstarter.h"
27 
28 #include "qmt/diagram_scene/diagramscenemodel.h"
29 #include "qmt/diagram_scene/diagramsceneconstants.h"
30 #include "qmt/diagram_scene/capabilities/relationable.h"
31 #include "qmt/infrastructure/qmtassert.h"
32 #include "qmt/style/stylecontroller.h"
33 
34 #include <QGraphicsSceneMouseEvent>
35 #include <QKeyEvent>
36 #include <QGraphicsScene>
37 #include <QPainter>
38 
39 namespace qmt {
40 
RelationStarter(IRelationable * owner,DiagramSceneModel * diagramSceneModel,QGraphicsItem * parent)41 RelationStarter::RelationStarter(IRelationable *owner, DiagramSceneModel *diagramSceneModel, QGraphicsItem *parent)
42     : QGraphicsRectItem(parent),
43       m_owner(owner),
44       m_diagramSceneModel(diagramSceneModel)
45 {
46     setBrush(QBrush(QColor(192, 192, 192)));
47     setPen(QPen(QColor(64, 64, 64)));
48     setFlag(QGraphicsItem::ItemIsFocusable);
49 }
50 
~RelationStarter()51 RelationStarter::~RelationStarter()
52 {
53 }
54 
boundingRect() const55 QRectF RelationStarter::boundingRect() const
56 {
57     return rect();
58 }
59 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)60 void RelationStarter::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
61 {
62     Q_UNUSED(option)
63     Q_UNUSED(widget)
64 
65     painter->save();
66     painter->setPen(pen());
67     painter->setBrush(brush());
68     painter->drawRoundedRect(rect(), 3, 3);
69     painter->restore();
70 }
71 
addArrow(const QString & id,ArrowItem::Shaft shaft,ArrowItem::Head startHead,ArrowItem::Head endHead,const QString & toolTip)72 void RelationStarter::addArrow(const QString &id, ArrowItem::Shaft shaft,
73                                ArrowItem::Head startHead, ArrowItem::Head endHead,
74                                const QString &toolTip)
75 {
76     QMT_CHECK(!id.isEmpty());
77     prepareGeometryChange();
78     auto arrow = new ArrowItem(this);
79     arrow->setArrowSize(10.0);
80     arrow->setDiamondSize(8.0);
81     arrow->setShaft(shaft);
82     arrow->setStartHead(startHead);
83     arrow->setEndHead(endHead);
84     arrow->setToolTip(toolTip.isEmpty() ? id : toolTip);
85     arrow->setPoints(QList<QPointF>() << QPointF(0.0, 10.0) << QPointF(15.0, 0.0));
86     arrow->setPos(6.0, m_arrows.size() * 20.0 + 8.0);
87     arrow->update(m_diagramSceneModel->styleController()->relationStarterStyle());
88     m_arrows.append(arrow);
89     m_arrowIds.insert(arrow, id);
90     setRect(0.0, 0.0, 26.0, m_arrows.size() * 20.0 + 6.0);
91 }
92 
mousePressEvent(QGraphicsSceneMouseEvent * event)93 void RelationStarter::mousePressEvent(QGraphicsSceneMouseEvent *event)
94 {
95     if (m_currentPreviewArrow)
96         return;
97     foreach (ArrowItem *item, m_arrows) {
98         if (item->boundingRect().contains(mapToItem(item, event->pos()))) {
99             prepareGeometryChange();
100             m_currentPreviewArrowIntermediatePoints.clear();
101             m_currentPreviewArrowId = m_arrowIds.value(item);
102             QMT_CHECK(!m_currentPreviewArrowId.isEmpty());
103             m_currentPreviewArrow = new ArrowItem(*item);
104             // TODO use constants for sizes (in relationitem.h also)
105             m_currentPreviewArrow->setArrowSize(12.0);
106             m_currentPreviewArrow->setDiamondSize(12.0);
107             m_currentPreviewArrow->setPoints(QList<QPointF>() << m_owner->relationStartPos() << mapToScene(event->pos()));
108             m_currentPreviewArrow->update(m_diagramSceneModel->styleController()->relationStarterStyle());
109             m_currentPreviewArrow->setZValue(PREVIEW_RELATION_ZVALUE);
110             scene()->addItem(m_currentPreviewArrow);
111             setFocus(); // receive keyboard events
112             break;
113         }
114     }
115 }
116 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)117 void RelationStarter::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
118 {
119     if (!m_currentPreviewArrow)
120         return;
121     updateCurrentPreviewArrow(mapToScene(event->pos()));
122 }
123 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)124 void RelationStarter::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
125 {
126     if (m_currentPreviewArrow) {
127         m_owner->relationDrawn(m_currentPreviewArrowId, mapToScene(event->pos()),
128                                m_currentPreviewArrowIntermediatePoints);
129         m_currentPreviewArrow->scene()->removeItem(m_currentPreviewArrow);
130         delete m_currentPreviewArrow;
131         m_currentPreviewArrow = nullptr;
132         m_currentPreviewArrowIntermediatePoints.clear();
133     }
134 }
135 
keyPressEvent(QKeyEvent * event)136 void RelationStarter::keyPressEvent(QKeyEvent *event)
137 {
138     if (!m_currentPreviewArrow)
139         return;
140     if (event->key() == Qt::Key_Shift) {
141         QPointF p = m_currentPreviewArrow->lastLineSegment().p1();
142         if (m_currentPreviewArrowIntermediatePoints.isEmpty() || m_currentPreviewArrowIntermediatePoints.last() != p) {
143             m_currentPreviewArrowIntermediatePoints.append(p);
144             // Do not update the preview arrow here because last two points are now identical which looks wired
145         }
146     } else if (event->key() == Qt::Key_Control) {
147         if (!m_currentPreviewArrowIntermediatePoints.isEmpty()) {
148             m_currentPreviewArrowIntermediatePoints.removeLast();
149             updateCurrentPreviewArrow(m_currentPreviewArrow->lastLineSegment().p1());
150         }
151     }
152 }
153 
focusOutEvent(QFocusEvent * event)154 void RelationStarter::focusOutEvent(QFocusEvent *event)
155 {
156     Q_UNUSED(event)
157     if (m_currentPreviewArrow) {
158         m_currentPreviewArrow->scene()->removeItem(m_currentPreviewArrow);
159         delete m_currentPreviewArrow;
160         m_currentPreviewArrow = nullptr;
161         m_currentPreviewArrowIntermediatePoints.clear();
162     }
163 }
164 
updateCurrentPreviewArrow(const QPointF & headPoint)165 void RelationStarter::updateCurrentPreviewArrow(const QPointF &headPoint)
166 {
167     prepareGeometryChange();
168     m_currentPreviewArrow->setPoints(QList<QPointF>() << m_owner->relationStartPos()
169                                       << m_currentPreviewArrowIntermediatePoints << headPoint);
170     m_currentPreviewArrow->update(m_diagramSceneModel->styleController()->relationStarterStyle());
171 }
172 
173 } // namespace qmt
174