2016-09-15 17:55:28 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://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 https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** 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.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "transitionitem.h"
|
|
|
|
|
#include "connectableitem.h"
|
|
|
|
|
#include "cornergrabberitem.h"
|
|
|
|
|
#include "finalstateitem.h"
|
|
|
|
|
#include "graphicsitemprovider.h"
|
|
|
|
|
#include "graphicsscene.h"
|
|
|
|
|
#include "parallelitem.h"
|
|
|
|
|
#include "sceneutils.h"
|
|
|
|
|
#include "scxmldocument.h"
|
|
|
|
|
#include "scxmleditorconstants.h"
|
|
|
|
|
#include "scxmltagutils.h"
|
|
|
|
|
#include "scxmluifactory.h"
|
|
|
|
|
#include "serializer.h"
|
|
|
|
|
#include "stateitem.h"
|
|
|
|
|
#include "tagtextitem.h"
|
|
|
|
|
|
|
|
|
|
#include <QBrush>
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QGraphicsScene>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QPen>
|
|
|
|
|
#include <QUndoStack>
|
|
|
|
|
#include <QtMath>
|
|
|
|
|
|
|
|
|
|
using namespace ScxmlEditor::PluginInterface;
|
|
|
|
|
|
|
|
|
|
const qreal SELECTION_DISTANCE = 10;
|
|
|
|
|
|
|
|
|
|
TransitionItem::TransitionItem(BaseItem *parent)
|
|
|
|
|
: BaseItem(parent)
|
|
|
|
|
, m_startTargetFactor(0.5, 0.5)
|
|
|
|
|
, m_endTargetFactor(0.5, 0.5)
|
|
|
|
|
{
|
|
|
|
|
setFlag(ItemIsSelectable, true);
|
|
|
|
|
|
|
|
|
|
m_highlightPen = QPen(QColor(0xff, 0x00, 0x60));
|
|
|
|
|
m_highlightPen.setWidth(8);
|
|
|
|
|
m_highlightPen.setJoinStyle(Qt::MiterJoin);
|
|
|
|
|
|
|
|
|
|
m_pen = QPen(QColor(0x12, 0x12, 0x12));
|
|
|
|
|
m_pen.setWidth(2);
|
|
|
|
|
|
|
|
|
|
m_arrow << QPointF(0, 0)
|
|
|
|
|
<< QPointF(1, 1)
|
|
|
|
|
<< QPointF(0, 1);
|
|
|
|
|
|
|
|
|
|
m_eventTagItem = new TagTextItem(this);
|
|
|
|
|
connect(m_eventTagItem, &TagTextItem::selected, this, [=](bool sel){
|
|
|
|
|
setItemSelected(sel);
|
|
|
|
|
});
|
|
|
|
|
connect(m_eventTagItem, &TagTextItem::textReady, this, &TransitionItem::textHasChanged);
|
|
|
|
|
connect(m_eventTagItem, &TagTextItem::movePointChanged, this, &TransitionItem::textItemPositionChanged);
|
|
|
|
|
|
|
|
|
|
checkWarningItems();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TransitionItem::~TransitionItem()
|
|
|
|
|
{
|
|
|
|
|
setBlockUpdates(true);
|
|
|
|
|
removeTransition(Start);
|
|
|
|
|
removeTransition(End);
|
|
|
|
|
//updateTarget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::checkWarningItems()
|
|
|
|
|
{
|
|
|
|
|
ScxmlUiFactory *uifactory = uiFactory();
|
|
|
|
|
if (uifactory) {
|
|
|
|
|
auto provider = static_cast<GraphicsItemProvider*>(uifactory->object("graphicsItemProvider"));
|
|
|
|
|
if (provider) {
|
|
|
|
|
if (!m_warningItem)
|
|
|
|
|
m_warningItem = static_cast<TransitionWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_TRANSITION, this));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::setTag(ScxmlTag *tag)
|
|
|
|
|
{
|
|
|
|
|
BaseItem::setTag(tag);
|
|
|
|
|
if (tag) {
|
|
|
|
|
if (tag->tagType() == InitialTransition)
|
|
|
|
|
m_eventTagItem->setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::textItemPositionChanged()
|
|
|
|
|
{
|
|
|
|
|
QPointF p = m_eventTagItem->movePoint();
|
|
|
|
|
QString data;
|
|
|
|
|
if (p.toPoint() != QPoint(0, 0)) {
|
|
|
|
|
Serializer s;
|
|
|
|
|
s.append(p);
|
|
|
|
|
data = s.data();
|
|
|
|
|
}
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_MOVEPOINT, data);
|
|
|
|
|
|
|
|
|
|
updateComponents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::textHasChanged(const QString &text)
|
|
|
|
|
{
|
|
|
|
|
setTagValue("event", text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::createGrabbers()
|
|
|
|
|
{
|
|
|
|
|
if (m_cornerGrabbers.count() != m_cornerPoints.count()) {
|
|
|
|
|
int selectedGrabberIndex = m_cornerGrabbers.indexOf(m_selectedCornerGrabber);
|
|
|
|
|
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!m_cornerGrabbers.isEmpty()) {
|
2016-09-15 17:55:28 +02:00
|
|
|
qDeleteAll(m_cornerGrabbers);
|
|
|
|
|
m_cornerGrabbers.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_cornerPoints.count(); ++i) {
|
|
|
|
|
auto cornerGrabber = new CornerGrabberItem(this);
|
|
|
|
|
cornerGrabber->setGrabberType(CornerGrabberItem::Circle);
|
|
|
|
|
m_cornerGrabbers << cornerGrabber;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedGrabberIndex >= 0 && selectedGrabberIndex < m_cornerGrabbers.count())
|
|
|
|
|
m_selectedCornerGrabber = m_cornerGrabbers[selectedGrabberIndex];
|
|
|
|
|
else
|
|
|
|
|
m_selectedCornerGrabber = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pen.setStyle(Qt::DotLine);
|
|
|
|
|
|
|
|
|
|
m_lineSelected = true;
|
|
|
|
|
updateGrabberPositions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::removeGrabbers()
|
|
|
|
|
{
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!m_cornerGrabbers.isEmpty()) {
|
2016-09-15 17:55:28 +02:00
|
|
|
qDeleteAll(m_cornerGrabbers);
|
|
|
|
|
m_cornerGrabbers.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_lineSelected = false;
|
|
|
|
|
m_pen.setStyle(Qt::SolidLine);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateGrabberPositions()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < qMin(m_cornerGrabbers.count(), m_cornerPoints.count()); ++i)
|
|
|
|
|
m_cornerGrabbers[i]->setPos(m_cornerPoints[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::removeUnnecessaryPoints()
|
|
|
|
|
{
|
|
|
|
|
if (m_cornerPoints.count() > 2) {
|
|
|
|
|
bool found = true;
|
|
|
|
|
while (found) {
|
|
|
|
|
found = false;
|
|
|
|
|
for (int i = 1; i < (m_cornerPoints.count() - 1); ++i) {
|
|
|
|
|
if (QLineF(m_cornerPoints[i], m_cornerPoints[i + 1]).length() <= 20 || QLineF(m_cornerPoints[i], m_cornerPoints[i - 1]).length() <= 20) {
|
|
|
|
|
m_cornerPoints.takeAt(i);
|
|
|
|
|
if (i < m_cornerGrabbers.count())
|
|
|
|
|
delete m_cornerGrabbers.takeAt(i);
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
storeValues();
|
|
|
|
|
updateComponents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant TransitionItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
|
|
|
|
{
|
|
|
|
|
QVariant retValue = BaseItem::itemChange(change, value);
|
|
|
|
|
|
|
|
|
|
switch (change) {
|
|
|
|
|
case ItemSelectedChange:
|
|
|
|
|
if (!m_mouseGrabbed) {
|
|
|
|
|
if (value.toBool())
|
|
|
|
|
createGrabbers();
|
|
|
|
|
else
|
|
|
|
|
removeGrabbers();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ItemSceneHasChanged:
|
|
|
|
|
checkWarningItems();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::snapToAnyPoint(int id, const QPointF &newPoint, int diff)
|
|
|
|
|
{
|
|
|
|
|
// Check snap to grid
|
|
|
|
|
bool snappedX = false;
|
|
|
|
|
bool snappedY = false;
|
|
|
|
|
for (int i = 0; i < m_cornerPoints.count(); ++i) {
|
|
|
|
|
if (id != i) {
|
|
|
|
|
if (qAbs(newPoint.x() - m_cornerPoints[i].x()) < diff) {
|
|
|
|
|
m_cornerPoints[id].setX(m_cornerPoints[i].x());
|
|
|
|
|
snappedX = true;
|
|
|
|
|
}
|
|
|
|
|
if (qAbs(newPoint.y() - m_cornerPoints[i].y()) < diff) {
|
|
|
|
|
m_cornerPoints[id].setY(m_cornerPoints[i].y());
|
|
|
|
|
snappedY = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!snappedX)
|
|
|
|
|
m_cornerPoints[id].setX(newPoint.x());
|
|
|
|
|
|
|
|
|
|
if (!snappedY)
|
|
|
|
|
m_cornerPoints[id].setY(newPoint.y());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::snapPointToPoint(int idSnap, const QPointF &p, int diff)
|
|
|
|
|
{
|
2017-08-21 11:47:33 +03:00
|
|
|
if (idSnap >= 0 && static_cast<uint>(idSnap) < static_cast<uint>(m_cornerPoints.count())) {
|
2016-09-15 17:55:28 +02:00
|
|
|
if (qAbs(p.x() - m_cornerPoints[idSnap].x()) < diff)
|
|
|
|
|
m_cornerPoints[idSnap].setX(p.x());
|
|
|
|
|
if (qAbs(p.y() - m_cornerPoints[idSnap].y()) < diff)
|
|
|
|
|
m_cornerPoints[idSnap].setY(p.y());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
// We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
|
|
|
|
|
if (event->modifiers() & Qt::ShiftModifier) {
|
|
|
|
|
event->ignore();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF p = event->pos();
|
|
|
|
|
bool bLeftButton = event->button() == Qt::LeftButton;
|
|
|
|
|
if (m_mouseGrabbed) {
|
|
|
|
|
if (bLeftButton) {
|
|
|
|
|
m_cornerPoints.append(p);
|
|
|
|
|
snapToAnyPoint(m_cornerPoints.count() - 1, p);
|
|
|
|
|
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!m_cornerGrabbers.isEmpty()) {
|
2016-09-15 17:55:28 +02:00
|
|
|
auto corner = new CornerGrabberItem(this);
|
|
|
|
|
corner->setGrabberType(CornerGrabberItem::Circle);
|
|
|
|
|
corner->setPos(p);
|
|
|
|
|
m_cornerGrabbers.append(corner);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
event->accept();
|
|
|
|
|
} else {
|
|
|
|
|
// Check QuickTransition
|
|
|
|
|
if (bLeftButton) {
|
|
|
|
|
// If we found QuickTransition-item or CornerGrabber at this point, we must ignore mouse press here
|
|
|
|
|
// So we can press QuickTransition/CornerGrabber item although there is transition lines front of these items
|
|
|
|
|
foreach (QGraphicsItem *item, scene()->items(event->scenePos())) {
|
|
|
|
|
if (item->type() == QuickTransitionType || (item->type() == CornerGrabberType && item->parentItem() != this)) {
|
|
|
|
|
event->ignore();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check selection
|
|
|
|
|
bool sel = m_lineSelected;
|
|
|
|
|
int i;
|
|
|
|
|
int foundPointIndex = -1;
|
|
|
|
|
for (i = 0; i < m_cornerPoints.count(); ++i) {
|
|
|
|
|
if (QLineF(m_cornerPoints[i], p).length() <= SELECTION_DISTANCE) {
|
|
|
|
|
// Is pressed point close enough of the elbow-point
|
|
|
|
|
foundPointIndex = i;
|
|
|
|
|
sel = true;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
if (i < m_cornerPoints.count() - 1) {
|
|
|
|
|
QLineF line(m_cornerPoints[i], m_cornerPoints[i + 1]);
|
|
|
|
|
|
|
|
|
|
// Is pressed point close enough of the line
|
|
|
|
|
QPointF intersPoint;
|
|
|
|
|
QLineF line2(p, p + QPointF(SELECTION_DISTANCE, SELECTION_DISTANCE));
|
|
|
|
|
line2.setAngle(line.angle() + 90);
|
2019-08-29 14:41:14 +02:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
2016-09-15 17:55:28 +02:00
|
|
|
if (line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection)
|
2019-08-29 14:41:14 +02:00
|
|
|
#else
|
|
|
|
|
if (line.intersects(line2, &intersPoint) == QLineF::BoundedIntersection)
|
|
|
|
|
#endif
|
2016-09-15 17:55:28 +02:00
|
|
|
sel = true;
|
|
|
|
|
else {
|
|
|
|
|
line2.setAngle(line.angle() - 90);
|
2019-08-29 14:41:14 +02:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
2016-09-15 17:55:28 +02:00
|
|
|
sel = line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection;
|
2019-08-29 14:41:14 +02:00
|
|
|
#else
|
|
|
|
|
sel = line.intersects(line2, &intersPoint) == QLineF::BoundedIntersection;
|
|
|
|
|
#endif
|
2016-09-15 17:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sel)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create or remove grabbers
|
|
|
|
|
if (sel != m_lineSelected) {
|
|
|
|
|
if (sel)
|
|
|
|
|
createGrabbers();
|
|
|
|
|
else
|
|
|
|
|
removeGrabbers();
|
|
|
|
|
|
|
|
|
|
if (foundPointIndex > 0 && foundPointIndex < m_cornerGrabbers.count()) {
|
|
|
|
|
m_selectedCornerGrabber = m_cornerGrabbers[foundPointIndex];
|
|
|
|
|
m_selectedCornerGrabber->setSelected(true);
|
|
|
|
|
}
|
|
|
|
|
} else if (m_lineSelected && bLeftButton) {
|
|
|
|
|
m_cornerPoints.insert((i + 1), p);
|
|
|
|
|
m_selectedCornerGrabber = new CornerGrabberItem(this);
|
|
|
|
|
m_selectedCornerGrabber->setGrabberType(CornerGrabberItem::Circle);
|
|
|
|
|
m_selectedCornerGrabber->setPos(p);
|
|
|
|
|
m_cornerGrabbers.insert((i + 1), m_selectedCornerGrabber);
|
|
|
|
|
m_selectedCornerGrabber->setSelected(true);
|
|
|
|
|
} else if (m_lineSelected && !bLeftButton) {
|
|
|
|
|
showContextMenu(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sel)
|
|
|
|
|
event->accept();
|
|
|
|
|
else
|
|
|
|
|
event->ignore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
// We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
|
|
|
|
|
if (event->modifiers() & Qt::ShiftModifier) {
|
|
|
|
|
event->ignore();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_mouseGrabbed) {
|
|
|
|
|
setEndPos(event->pos());
|
|
|
|
|
event->ignore();
|
|
|
|
|
} else if (m_selectedCornerGrabber) {
|
|
|
|
|
snapToAnyPoint(m_cornerGrabbers.indexOf(m_selectedCornerGrabber), event->pos());
|
|
|
|
|
updateComponents();
|
|
|
|
|
storeValues();
|
|
|
|
|
BaseItem::mouseMoveEvent(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
// We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
|
|
|
|
|
if (event->modifiers() & Qt::ShiftModifier) {
|
|
|
|
|
event->ignore();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_mouseGrabbed) {
|
|
|
|
|
if (event->button() == Qt::RightButton) {
|
|
|
|
|
connectToTopItem(mapToScene(event->pos()), TransitionItem::End, m_grabbedTargetType);
|
|
|
|
|
setSelected(false);
|
|
|
|
|
tag()->document()->undoStack()->endMacro();
|
|
|
|
|
m_mouseGrabbed = false;
|
|
|
|
|
ungrabMouse();
|
|
|
|
|
storeValues();
|
|
|
|
|
}
|
|
|
|
|
event->accept();
|
|
|
|
|
} else {
|
|
|
|
|
if (event->button() == Qt::LeftButton) {
|
|
|
|
|
if (m_selectedCornerGrabber) {
|
|
|
|
|
m_selectedCornerGrabber = nullptr;
|
|
|
|
|
setSelected(true);
|
|
|
|
|
}
|
|
|
|
|
removeUnnecessaryPoints();
|
|
|
|
|
}
|
|
|
|
|
BaseItem::mouseReleaseEvent(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e)
|
|
|
|
|
{
|
|
|
|
|
int ind = -1;
|
|
|
|
|
for (int i = 0; i < m_cornerGrabbers.count(); ++i) {
|
|
|
|
|
if (m_cornerGrabbers[i]->isSelected()) {
|
|
|
|
|
ind = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_selectedGrabberIndex = ind;
|
|
|
|
|
BaseItem::checkSelectionBeforeContextMenu(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::createContextMenu(QMenu *menu)
|
|
|
|
|
{
|
|
|
|
|
QVariantMap data;
|
|
|
|
|
if (m_selectedGrabberIndex > 0) {
|
|
|
|
|
data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::RemovePoint;
|
|
|
|
|
data["cornerIndex"] = m_selectedGrabberIndex;
|
|
|
|
|
menu->addAction(tr("Remove Point"))->setData(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
BaseItem::createContextMenu(menu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::selectedMenuAction(const QAction *action)
|
|
|
|
|
{
|
|
|
|
|
if (action) {
|
|
|
|
|
QVariantMap data = action->data().toMap();
|
|
|
|
|
int actionType = data.value(Constants::C_SCXMLTAG_ACTIONTYPE, -1).toInt();
|
|
|
|
|
|
|
|
|
|
switch (actionType) {
|
|
|
|
|
case TagUtils::RemovePoint: {
|
|
|
|
|
int ind = data.value("cornerIndex", 0).toInt();
|
|
|
|
|
if (ind > 0) {
|
|
|
|
|
delete m_cornerGrabbers.takeAt(ind);
|
|
|
|
|
m_cornerPoints.takeAt(ind);
|
|
|
|
|
updateComponents();
|
|
|
|
|
storeValues();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
BaseItem::selectedMenuAction(action);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::keyPressEvent(QKeyEvent *event)
|
|
|
|
|
{
|
2020-03-09 10:30:41 +01:00
|
|
|
if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) {
|
2016-09-15 17:55:28 +02:00
|
|
|
bool bFound = false;
|
|
|
|
|
if (m_cornerGrabbers.count() > 2) {
|
|
|
|
|
for (int i = m_cornerGrabbers.count() - 1; i >= 1; i--) {
|
|
|
|
|
if (m_cornerGrabbers[i]->isSelected()) {
|
|
|
|
|
delete m_cornerGrabbers.takeAt(i);
|
|
|
|
|
m_cornerPoints.takeAt(i);
|
|
|
|
|
bFound = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bFound) {
|
|
|
|
|
updateComponents();
|
|
|
|
|
storeValues();
|
|
|
|
|
event->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseItem::keyPressEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TransitionItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (watched->type() == CornerGrabberType) {
|
|
|
|
|
auto c = qgraphicsitem_cast<CornerGrabberItem*>(watched);
|
|
|
|
|
auto mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
|
2016-09-18 09:18:12 +03:00
|
|
|
if (!c || !mouseEvent)
|
2016-09-15 17:55:28 +02:00
|
|
|
return BaseItem::sceneEventFilter(watched, event);
|
|
|
|
|
|
|
|
|
|
int cid = m_cornerGrabbers.indexOf(c);
|
|
|
|
|
if (mouseEvent->type() == QEvent::GraphicsSceneMouseMove) {
|
|
|
|
|
if (mouseEvent->buttons() & Qt::LeftButton) {
|
|
|
|
|
QPointF movingPoint = c->pressedPoint() - mouseEvent->pos();
|
|
|
|
|
|
|
|
|
|
if (cid == 0) {
|
|
|
|
|
if (!m_movingFirstPoint) {
|
|
|
|
|
m_movingFirstPoint = true;
|
|
|
|
|
removeTransition(Start);
|
|
|
|
|
}
|
|
|
|
|
} else if (cid == (m_cornerPoints.count() - 1)) {
|
|
|
|
|
if (!m_movingLastPoint) {
|
|
|
|
|
m_movingLastPoint = true;
|
|
|
|
|
if (m_endItem)
|
|
|
|
|
removeTransition(End);
|
|
|
|
|
else {
|
|
|
|
|
updateZValue();
|
|
|
|
|
updateTargetType();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cid >= 0 && cid < m_cornerPoints.count())
|
|
|
|
|
snapToAnyPoint(cid, m_cornerPoints[cid] - movingPoint);
|
|
|
|
|
|
|
|
|
|
updateComponents();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
} else if (mouseEvent->type() == QEvent::GraphicsSceneMouseRelease) {
|
|
|
|
|
if (mouseEvent->button() == Qt::LeftButton) {
|
|
|
|
|
if (cid == 0 || (cid == m_cornerPoints.count() - 1)) {
|
|
|
|
|
m_movingFirstPoint = false;
|
|
|
|
|
m_movingLastPoint = false;
|
|
|
|
|
connectToTopItem(watched->mapToScene(mouseEvent->pos()), cid == 0 ? Start : End, UnknownType);
|
|
|
|
|
}
|
|
|
|
|
removeUnnecessaryPoints();
|
|
|
|
|
} else
|
|
|
|
|
showContextMenu(mouseEvent);
|
|
|
|
|
|
|
|
|
|
storeValues();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseItem::sceneEventFilter(watched, event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::removeTransition(TransitionPoint p)
|
|
|
|
|
{
|
|
|
|
|
// Remove transition from the item
|
|
|
|
|
// Private function. Transition can itself remove connection from the item
|
|
|
|
|
if (p == Start && m_startItem) {
|
|
|
|
|
m_oldStartItem = m_startItem;
|
|
|
|
|
m_startItem->removeOutputTransition(this);
|
|
|
|
|
m_startItem = nullptr;
|
|
|
|
|
updateZValue();
|
|
|
|
|
updateTargetType();
|
|
|
|
|
if (m_oldStartItem)
|
|
|
|
|
m_oldStartItem->updateTransitions();
|
|
|
|
|
} else if (p == End && m_endItem) {
|
|
|
|
|
m_endItem->removeInputTransition(this);
|
|
|
|
|
m_endItem = nullptr;
|
|
|
|
|
updateZValue();
|
|
|
|
|
updateTargetType();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::disconnectItem(ConnectableItem *item)
|
|
|
|
|
{
|
|
|
|
|
// Disconnect item, normally called from the ConnectableItem
|
|
|
|
|
if (item == m_startItem)
|
|
|
|
|
removeTransition(Start);
|
|
|
|
|
if (item == m_endItem)
|
|
|
|
|
removeTransition(End);
|
|
|
|
|
|
|
|
|
|
updateTarget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::setStartItem(ConnectableItem *item)
|
|
|
|
|
{
|
|
|
|
|
m_oldStartItem = nullptr;
|
|
|
|
|
m_startItem = item;
|
|
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
|
if (tag())
|
|
|
|
|
tag()->document()->changeParent(tag(), m_startItem->tag());
|
|
|
|
|
item->addOutputTransition(this);
|
|
|
|
|
|
|
|
|
|
if (m_cornerPoints.isEmpty()) {
|
|
|
|
|
m_cornerPoints << sceneTargetPoint(TransitionPoint::Start);
|
|
|
|
|
m_cornerPoints << sceneTargetPoint(TransitionPoint::End);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateZValue();
|
|
|
|
|
updateComponents();
|
|
|
|
|
storeValues();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::startTransitionFrom(ConnectableItem *item, const QPointF &mouseScenePos)
|
|
|
|
|
{
|
|
|
|
|
m_oldStartItem = nullptr;
|
|
|
|
|
m_startItem = item;
|
|
|
|
|
m_startItem->addOutputTransition(this);
|
|
|
|
|
m_cornerPoints.clear();
|
|
|
|
|
m_cornerPoints << sceneTargetPoint(TransitionPoint::Start);
|
|
|
|
|
m_cornerPoints << mapFromScene(mouseScenePos);
|
|
|
|
|
|
|
|
|
|
createGrabbers();
|
|
|
|
|
updateZValue();
|
|
|
|
|
updateComponents();
|
|
|
|
|
storeValues();
|
|
|
|
|
m_cornerGrabbers.last()->setSelected(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::connectToTopItem(const QPointF &pos, TransitionPoint tp, ItemType targetType)
|
|
|
|
|
{
|
|
|
|
|
int cornerPoints = m_cornerPoints.count();
|
|
|
|
|
|
|
|
|
|
ConnectableItem *parentItem = nullptr;
|
|
|
|
|
ScxmlTag *parentTag = nullptr;
|
|
|
|
|
ScxmlDocument *document = tag()->document();
|
|
|
|
|
|
|
|
|
|
snapToAnyPoint(m_cornerPoints.count() - 1, pos);
|
|
|
|
|
QPointF p(m_cornerPoints.last());
|
|
|
|
|
|
|
|
|
|
// First try to find parentItem
|
|
|
|
|
QList<QGraphicsItem*> items = scene()->items(p);
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!items.isEmpty()) {
|
2016-09-15 17:55:28 +02:00
|
|
|
for (int i = 0; i < items.count(); ++i) {
|
|
|
|
|
ItemType type = ItemType(items[i]->type());
|
|
|
|
|
if ((targetType == UnknownType && type >= FinalStateType) || type >= StateType) {
|
|
|
|
|
auto it = qgraphicsitem_cast<ConnectableItem*>(items[i]);
|
|
|
|
|
if (it) {
|
|
|
|
|
parentItem = it;
|
|
|
|
|
parentTag = parentItem->tag();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-29 14:25:20 +01:00
|
|
|
if (!parentTag && document)
|
|
|
|
|
parentTag = document->rootTag();
|
2016-09-15 17:55:28 +02:00
|
|
|
|
|
|
|
|
// Connect existing item
|
|
|
|
|
if (targetType == UnknownType) {
|
|
|
|
|
switch (tp) {
|
|
|
|
|
case Start:
|
|
|
|
|
if (parentItem) {
|
|
|
|
|
m_startTargetFactor = calculateTargetFactor(parentItem, pos);
|
|
|
|
|
savePoint(m_startTargetFactor * 100, Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS);
|
|
|
|
|
}
|
|
|
|
|
setStartItem(parentItem ? parentItem : m_oldStartItem);
|
|
|
|
|
break;
|
|
|
|
|
case End:
|
|
|
|
|
m_endTargetFactor = calculateTargetFactor(parentItem, pos);
|
|
|
|
|
savePoint(m_endTargetFactor * 100, Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS);
|
|
|
|
|
setEndItem(parentItem);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setSelected(false);
|
|
|
|
|
if (parentItem)
|
|
|
|
|
parentItem->setSelected(true);
|
|
|
|
|
removeGrabbers();
|
|
|
|
|
if (m_startItem == m_endItem && cornerPoints == 2) {
|
|
|
|
|
setTagValue("type", "internal");
|
2018-11-24 20:21:25 +01:00
|
|
|
setEndItem(nullptr);
|
2016-09-15 17:55:28 +02:00
|
|
|
m_targetType = InternalNoTarget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateEventName();
|
|
|
|
|
storeValues();
|
|
|
|
|
} else {
|
|
|
|
|
// Create new item and connect to it
|
2016-09-18 09:18:12 +03:00
|
|
|
ConnectableItem *newItem = SceneUtils::createItem(targetType, parentItem ? parentItem->mapFromScene(p) : p);
|
2016-09-15 17:55:28 +02:00
|
|
|
if (newItem) {
|
|
|
|
|
ScxmlTag *newTag = SceneUtils::createTag(targetType, tag()->document());
|
|
|
|
|
newItem->setTag(newTag);
|
|
|
|
|
newItem->setParentItem(parentItem);
|
|
|
|
|
if (!parentItem)
|
|
|
|
|
scene()->addItem(newItem);
|
|
|
|
|
|
|
|
|
|
newItem->addInputTransition(this);
|
|
|
|
|
newItem->updateAttributes();
|
|
|
|
|
newItem->updateEditorInfo();
|
|
|
|
|
newItem->updateUIProperties();
|
|
|
|
|
|
|
|
|
|
if (parentItem)
|
|
|
|
|
parentItem->updateUIProperties();
|
|
|
|
|
|
2016-11-29 14:25:20 +01:00
|
|
|
if (document)
|
|
|
|
|
document->addTag(parentTag, newTag);
|
2016-09-15 17:55:28 +02:00
|
|
|
|
|
|
|
|
setEndItem(newItem);
|
|
|
|
|
setSelected(false);
|
|
|
|
|
newItem->setSelected(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeGrabbers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateTargetType();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::setEndPos(const QPointF &endPos, bool snap)
|
|
|
|
|
{
|
|
|
|
|
if (m_cornerPoints.count() >= 2) {
|
|
|
|
|
m_cornerPoints.last().setX(endPos.x());
|
|
|
|
|
m_cornerPoints.last().setY(endPos.y());
|
|
|
|
|
|
|
|
|
|
if (snap)
|
|
|
|
|
snapToAnyPoint(m_cornerPoints.count() - 1, endPos);
|
|
|
|
|
updateComponents();
|
|
|
|
|
storeValues();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-12 12:12:56 +01:00
|
|
|
void TransitionItem::setEndItem(ConnectableItem *item, bool fixValue)
|
2016-09-15 17:55:28 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (item) {
|
|
|
|
|
m_endItem = item;
|
|
|
|
|
item->addInputTransition(this);
|
|
|
|
|
setEndPos(sceneTargetPoint(End), false);
|
|
|
|
|
|
|
|
|
|
if (m_cornerPoints.count() > 2)
|
|
|
|
|
snapPointToPoint(m_cornerPoints.count() - 2, m_cornerPoints.last(), 15);
|
|
|
|
|
} else {
|
|
|
|
|
removeTransition(End);
|
|
|
|
|
updateComponents();
|
|
|
|
|
storeValues();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateZValue();
|
2021-01-12 12:12:56 +01:00
|
|
|
updateTarget(fixValue);
|
2016-09-15 17:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF TransitionItem::loadPoint(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
Serializer s;
|
|
|
|
|
QPointF p;
|
|
|
|
|
s.setData(editorInfo(name));
|
|
|
|
|
s.read(p);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::savePoint(const QPointF &p, const QString &name)
|
|
|
|
|
{
|
|
|
|
|
Serializer s;
|
|
|
|
|
s.append(p);
|
|
|
|
|
setEditorInfo(name, s.data(), true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF TransitionItem::calculateTargetFactor(ConnectableItem *item, const QPointF &pos)
|
|
|
|
|
{
|
|
|
|
|
if (item) {
|
|
|
|
|
QRectF r = item->sceneBoundingRect().adjusted(-8, -8, 8, 8);
|
|
|
|
|
QPointF pixelFactorPoint = pos - r.topLeft();
|
|
|
|
|
QPointF normalizedPoint(qBound(0.0, pixelFactorPoint.x() / r.width(), 1.0), qBound(0.0, pixelFactorPoint.y() / r.height(), 1.0));
|
|
|
|
|
|
|
|
|
|
// Center point if close enough
|
|
|
|
|
if (qAbs(normalizedPoint.x() - 0.5) < 0.2 && qAbs(normalizedPoint.y() - 0.5) < 0.2)
|
|
|
|
|
return QPointF(0.5, 0.5);
|
|
|
|
|
|
|
|
|
|
return normalizedPoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QPointF(0.5, 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF TransitionItem::sceneTargetPoint(TransitionPoint p)
|
|
|
|
|
{
|
|
|
|
|
ConnectableItem *item = nullptr;
|
|
|
|
|
QPointF factorPoint;
|
|
|
|
|
if (p == TransitionPoint::Start) {
|
|
|
|
|
item = m_startItem;
|
|
|
|
|
factorPoint = m_startTargetFactor;
|
|
|
|
|
} else {
|
|
|
|
|
if (m_endItem) {
|
|
|
|
|
item = m_endItem;
|
|
|
|
|
factorPoint = m_endTargetFactor;
|
|
|
|
|
} else {
|
|
|
|
|
item = m_startItem;
|
|
|
|
|
factorPoint = QPointF(0.5, 0.5);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QRectF r;
|
|
|
|
|
if (item)
|
|
|
|
|
r = item->sceneBoundingRect();
|
|
|
|
|
|
|
|
|
|
return r.topLeft() + QPointF(factorPoint.x() * r.width(), factorPoint.y() * r.height());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF TransitionItem::findIntersectionPoint(ConnectableItem *item, const QLineF &line, const QPointF &defaultPoint)
|
|
|
|
|
{
|
|
|
|
|
// Check circles
|
|
|
|
|
ItemType t = ItemType(item->type());
|
|
|
|
|
if (t >= InitialStateType && t <= HistoryType) {
|
|
|
|
|
QLineF l = item == m_endItem ? line : QLineF(line.p2(), line.p1());
|
|
|
|
|
l.setLength(l.length() - qMin(item->boundingRect().width(), item->boundingRect().height()) * 0.45);
|
|
|
|
|
return l.p2();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find intersection point between line and target item
|
|
|
|
|
QPolygonF itemPolygon = item->polygonShape();
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!itemPolygon.isEmpty()) {
|
2016-09-15 17:55:28 +02:00
|
|
|
QPointF intersectPoint;
|
|
|
|
|
QPointF p1 = itemPolygon.at(0) + item->scenePos();
|
|
|
|
|
QPointF p2;
|
|
|
|
|
QLineF checkLine;
|
|
|
|
|
for (int i = 1; i < itemPolygon.count(); ++i) {
|
|
|
|
|
p2 = itemPolygon.at(i) + item->scenePos();
|
|
|
|
|
checkLine = QLineF(p1, p2);
|
2019-08-29 14:41:14 +02:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
2016-09-15 17:55:28 +02:00
|
|
|
if (checkLine.intersect(line, &intersectPoint) == QLineF::BoundedIntersection)
|
2019-08-29 14:41:14 +02:00
|
|
|
#else
|
|
|
|
|
if (checkLine.intersects(line, &intersectPoint) == QLineF::BoundedIntersection)
|
|
|
|
|
#endif
|
2016-09-15 17:55:28 +02:00
|
|
|
return intersectPoint;
|
|
|
|
|
p1 = p2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return defaultPoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateComponents()
|
|
|
|
|
{
|
|
|
|
|
if (m_cornerPoints.count() < 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Check if we must move all points together
|
|
|
|
|
bool movePoints = (SceneUtils::isSomeSelected(m_startItem) && SceneUtils::isSomeSelected(m_endItem) && m_cornerPoints.count() > 2) || (m_movingFirstPoint && m_targetType == InternalNoTarget);
|
|
|
|
|
|
|
|
|
|
if (!movePoints && !m_mouseGrabbed) {
|
|
|
|
|
// Check the corners which are in the same line if cornerGrabbers are hidden
|
|
|
|
|
if (m_cornerGrabbers.isEmpty() && m_cornerPoints.count() > 2) {
|
|
|
|
|
for (int i = 0; i < m_cornerPoints.count() - 2; ++i) {
|
|
|
|
|
if (m_cornerPoints[i] != m_cornerPoints[i + 1] && m_cornerPoints[i] != m_cornerPoints[i + 2]) {
|
|
|
|
|
QLineF l1(m_cornerPoints[i], m_cornerPoints[i + 1]);
|
|
|
|
|
QLineF l2(m_cornerPoints[i], m_cornerPoints[i + 2]);
|
|
|
|
|
|
|
|
|
|
if (qRound(l1.angle()) == qRound(l2.angle())) {
|
|
|
|
|
m_cornerPoints.takeAt(i + 1);
|
|
|
|
|
storeValues();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((m_movingFirstPoint || m_movingLastPoint) && !(m_movingFirstPoint && m_targetType == InternalNoTarget))
|
|
|
|
|
movePoints = false;
|
|
|
|
|
|
|
|
|
|
// Init first line
|
|
|
|
|
QLineF firstLine(m_cornerPoints[0], m_cornerPoints[1]);
|
|
|
|
|
if (m_startItem) {
|
|
|
|
|
if (m_targetType <= InternalNoTarget) {
|
|
|
|
|
QPointF p = m_startItem->getInternalPosition(this, m_targetType);
|
|
|
|
|
firstLine.setP1(p);
|
|
|
|
|
firstLine.setP2(p + QPointF(20, 0));
|
|
|
|
|
m_cornerPoints[1] = firstLine.p2();
|
|
|
|
|
if (!(m_movingFirstPoint && m_targetType == InternalNoTarget))
|
|
|
|
|
movePoints = false;
|
|
|
|
|
} else {
|
|
|
|
|
firstLine.setP1(sceneTargetPoint(TransitionPoint::Start));
|
|
|
|
|
firstLine.setP1(findIntersectionPoint(m_startItem, firstLine, firstLine.p1()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (movePoints) {
|
|
|
|
|
if (m_movingFirstPoint && m_targetType == InternalNoTarget)
|
|
|
|
|
m_cornerPoints[1] = m_cornerPoints[0] + QPointF(20, 0);
|
|
|
|
|
else {
|
|
|
|
|
QPointF movingPoint = firstLine.p1() - m_cornerPoints[0];
|
|
|
|
|
for (int i = 0; i < m_cornerPoints.count(); ++i)
|
|
|
|
|
m_cornerPoints[i] += movingPoint;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_arrow.count(); ++i)
|
|
|
|
|
m_arrow[i] += movingPoint;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_cornerPoints[0] = firstLine.p1();
|
|
|
|
|
|
|
|
|
|
// Init last line
|
|
|
|
|
int lastIndex = m_cornerPoints.count() - 1;
|
|
|
|
|
QLineF lastLine(m_cornerPoints[lastIndex - 1], m_cornerPoints[lastIndex]);
|
|
|
|
|
if (m_endItem && m_targetType > InternalNoTarget) {
|
|
|
|
|
lastLine.setP2(sceneTargetPoint(TransitionPoint::End));
|
|
|
|
|
lastLine.setP2(findIntersectionPoint(m_endItem, lastLine, lastLine.p2()));
|
|
|
|
|
m_cornerPoints[lastIndex] = lastLine.p2();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_arrowAngle = 0;
|
|
|
|
|
if (m_targetType == InternalSameTarget) {
|
|
|
|
|
m_arrowAngle = M_PI * 0.6;
|
|
|
|
|
} else {
|
|
|
|
|
// Calculate angle of the lastLine and update arrow
|
|
|
|
|
if (lastLine.length() > 0) {
|
|
|
|
|
m_arrowAngle = qAcos(lastLine.dx() / lastLine.length()) + M_PI;
|
|
|
|
|
if (lastLine.dy() >= 0)
|
|
|
|
|
m_arrowAngle = (2 * M_PI) - m_arrowAngle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_arrow[0] = lastLine.p2() + QPointF(qSin(m_arrowAngle + M_PI / 3) * m_arrowSize, qCos(m_arrowAngle + M_PI / 3) * m_arrowSize);
|
|
|
|
|
m_arrow[1] = lastLine.p2();
|
|
|
|
|
m_arrow[2] = lastLine.p2() + QPointF(qSin(m_arrowAngle + M_PI - M_PI / 3) * m_arrowSize, qCos(m_arrowAngle + M_PI - M_PI / 3) * m_arrowSize);
|
|
|
|
|
|
|
|
|
|
setItemBoundingRect(m_cornerPoints.boundingRect().normalized().adjusted(-SELECTION_DISTANCE, -SELECTION_DISTANCE,
|
|
|
|
|
SELECTION_DISTANCE, SELECTION_DISTANCE));
|
|
|
|
|
|
|
|
|
|
// Set the right place for the name of the transition
|
|
|
|
|
int ind = m_cornerPoints.count() / 2 - 1;
|
|
|
|
|
QLineF nameLine(m_cornerPoints[ind], m_cornerPoints[ind + 1]);
|
|
|
|
|
if (m_targetType <= InternalNoTarget) {
|
|
|
|
|
m_eventTagItem->setPos(m_cornerPoints[1].x() + 6, m_cornerPoints[1].y() - m_eventTagItem->boundingRect().height() / 3);
|
|
|
|
|
} else {
|
|
|
|
|
const qreal w2 = m_eventTagItem->boundingRect().width() / 2;
|
|
|
|
|
QPointF startPos = nameLine.pointAt(0.5);
|
|
|
|
|
QLineF targetLine(startPos, startPos + QPointF(SELECTION_DISTANCE, SELECTION_DISTANCE));
|
|
|
|
|
targetLine.setAngle(nameLine.angle() + 90);
|
|
|
|
|
|
|
|
|
|
m_eventTagItem->setPos(targetLine.p2() + m_eventTagItem->movePoint() - QPointF(w2, m_eventTagItem->boundingRect().height() / 2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_warningItem)
|
|
|
|
|
m_warningItem->setPos(m_eventTagItem->pos() - QPointF(WARNING_ITEM_SIZE, 0));
|
|
|
|
|
updateGrabberPositions();
|
|
|
|
|
updateZValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::grabMouse(ItemType targetType)
|
|
|
|
|
{
|
|
|
|
|
m_grabbedTargetType = targetType;
|
|
|
|
|
m_mouseGrabbed = true;
|
|
|
|
|
BaseItem::grabMouse();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(option)
|
|
|
|
|
Q_UNUSED(widget)
|
|
|
|
|
|
|
|
|
|
painter->save();
|
|
|
|
|
|
|
|
|
|
painter->setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
|
painter->setPen(m_pen);
|
|
|
|
|
|
|
|
|
|
if (m_cornerPoints.count() >= 2) {
|
|
|
|
|
if (m_targetType == InternalSameTarget) {
|
|
|
|
|
QRectF rect(m_cornerPoints[0].x(), m_cornerPoints[0].y() - SELECTION_DISTANCE,
|
|
|
|
|
m_cornerPoints[1].x() - m_cornerPoints[0].x(),
|
|
|
|
|
SELECTION_DISTANCE * 2);
|
|
|
|
|
painter->drawArc(rect, 0, 180 * 16);
|
|
|
|
|
} else {
|
|
|
|
|
if (highlight()) {
|
|
|
|
|
painter->setPen(m_highlightPen);
|
|
|
|
|
painter->drawPolyline(m_cornerPoints);
|
|
|
|
|
}
|
|
|
|
|
painter->setPen(m_pen);
|
|
|
|
|
painter->drawPolyline(m_cornerPoints);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_cornerPoints.count() - 1; ++i)
|
|
|
|
|
painter->drawEllipse(m_cornerPoints[i], 2, 2);
|
|
|
|
|
|
|
|
|
|
if (highlight()) {
|
|
|
|
|
painter->setPen(m_highlightPen);
|
|
|
|
|
painter->drawPolyline(m_arrow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
painter->setPen(m_pen);
|
|
|
|
|
painter->drawPolyline(m_arrow);
|
|
|
|
|
|
|
|
|
|
painter->restore();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateEditorInfo(bool allChilds)
|
|
|
|
|
{
|
|
|
|
|
BaseItem::updateEditorInfo(allChilds);
|
|
|
|
|
|
|
|
|
|
const QColor fontColor = editorInfo(Constants::C_SCXML_EDITORINFO_FONTCOLOR);
|
|
|
|
|
m_eventTagItem->setDefaultTextColor(fontColor.isValid() ? fontColor : Qt::black);
|
|
|
|
|
|
|
|
|
|
const QColor stateColor = editorInfo(Constants::C_SCXML_EDITORINFO_STATECOLOR);
|
|
|
|
|
m_pen.setColor(stateColor.isValid() ? stateColor : qRgb(0x12, 0x12, 0x12));
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-12 12:12:56 +01:00
|
|
|
void TransitionItem::updateTarget(bool fixValue)
|
2016-09-15 17:55:28 +02:00
|
|
|
{
|
2021-01-12 12:12:56 +01:00
|
|
|
if (fixValue)
|
|
|
|
|
setTagValue("target", m_endItem ? m_endItem->itemId() : QString());
|
2016-09-15 17:55:28 +02:00
|
|
|
if (m_endItem)
|
|
|
|
|
m_endItem->checkInitial(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateAttributes()
|
|
|
|
|
{
|
|
|
|
|
BaseItem::updateAttributes();
|
|
|
|
|
|
|
|
|
|
// Find new target
|
2016-09-18 09:18:12 +03:00
|
|
|
if (!m_endItem || tagValue("target") != m_endItem->itemId()) {
|
2016-09-15 17:55:28 +02:00
|
|
|
if (m_endItem)
|
|
|
|
|
m_endItem->removeInputTransition(this);
|
|
|
|
|
|
|
|
|
|
m_endItem = nullptr;
|
|
|
|
|
findEndItem();
|
2021-01-12 12:12:56 +01:00
|
|
|
updateTarget(false);
|
2016-09-15 17:55:28 +02:00
|
|
|
updateZValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set event-name
|
|
|
|
|
updateEventName();
|
|
|
|
|
updateTargetType();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, bool blockUpdates)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(initChildren)
|
|
|
|
|
setBlockUpdates(blockUpdates);
|
|
|
|
|
|
|
|
|
|
setTag(tag);
|
|
|
|
|
setParentItem(parentItem);
|
|
|
|
|
updateEditorInfo();
|
|
|
|
|
|
|
|
|
|
if (blockUpdates)
|
|
|
|
|
setBlockUpdates(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::readUISpecifiedProperties(const ScxmlTag *tag)
|
|
|
|
|
{
|
|
|
|
|
if (tag) {
|
|
|
|
|
if (m_cornerPoints.count() >= 2) {
|
|
|
|
|
while (m_cornerPoints.count() > 2)
|
|
|
|
|
m_cornerPoints.takeAt(1);
|
|
|
|
|
|
|
|
|
|
Serializer s;
|
|
|
|
|
|
|
|
|
|
QPointF p = loadPoint(Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS);
|
|
|
|
|
if (p.isNull())
|
|
|
|
|
p = QPointF(50, 50);
|
|
|
|
|
m_startTargetFactor = p / 100;
|
|
|
|
|
|
|
|
|
|
p = loadPoint(Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS);
|
|
|
|
|
if (p.isNull())
|
|
|
|
|
p = QPointF(50, 50);
|
|
|
|
|
m_endTargetFactor = p / 100;
|
|
|
|
|
|
|
|
|
|
QString localPointsData = editorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY);
|
|
|
|
|
if (!localPointsData.isEmpty()) {
|
|
|
|
|
QPointF startPos = sceneTargetPoint(Start);
|
|
|
|
|
QPolygonF localPoints;
|
|
|
|
|
s.setData(localPointsData);
|
|
|
|
|
s.read(localPoints);
|
|
|
|
|
for (int i = 0; i < localPoints.count(); ++i)
|
|
|
|
|
m_cornerPoints.insert(i + 1, startPos + localPoints[i]);
|
|
|
|
|
} else {
|
|
|
|
|
QPolygonF scenePoints;
|
|
|
|
|
s.setData(editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY));
|
|
|
|
|
s.read(scenePoints);
|
|
|
|
|
for (int i = 0; i < scenePoints.count(); ++i)
|
|
|
|
|
m_cornerPoints.insert(i + 1, scenePoints[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_eventTagItem->resetMovePoint(loadPoint(Constants::C_SCXML_EDITORINFO_MOVEPOINT));
|
|
|
|
|
|
|
|
|
|
if (m_lineSelected)
|
|
|
|
|
createGrabbers();
|
|
|
|
|
updateComponents();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::finalizeCreation()
|
|
|
|
|
{
|
|
|
|
|
bool old = blockUpdates();
|
|
|
|
|
setBlockUpdates(true);
|
|
|
|
|
|
|
|
|
|
updateAttributes();
|
|
|
|
|
|
|
|
|
|
if (!old)
|
|
|
|
|
setBlockUpdates(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::checkVisibility(double scaleFactor)
|
|
|
|
|
{
|
|
|
|
|
m_eventTagItem->setVisible(scaleFactor > 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TransitionItem::containsScenePoint(const QPointF &p) const
|
|
|
|
|
{
|
|
|
|
|
QPointF pp = mapFromScene(p);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_cornerPoints.count() - 1; ++i) {
|
|
|
|
|
QLineF line(m_cornerPoints[i], m_cornerPoints[i + 1]);
|
|
|
|
|
|
|
|
|
|
// Is point close enough of the line
|
|
|
|
|
QPointF intersPoint;
|
|
|
|
|
QLineF line2(pp, pp + QPointF(SELECTION_DISTANCE, SELECTION_DISTANCE));
|
|
|
|
|
line2.setAngle(line.angle() + 90);
|
2019-08-29 14:41:14 +02:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
2016-09-15 17:55:28 +02:00
|
|
|
if (line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection) {
|
2019-08-29 14:41:14 +02:00
|
|
|
#else
|
|
|
|
|
if (line.intersects(line2, &intersPoint) == QLineF::BoundedIntersection) {
|
|
|
|
|
#endif
|
2016-09-15 17:55:28 +02:00
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
line2.setAngle(line.angle() - 90);
|
2019-08-29 14:41:14 +02:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
2016-09-15 17:55:28 +02:00
|
|
|
if (line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection)
|
2019-08-29 14:41:14 +02:00
|
|
|
#else
|
|
|
|
|
if (line.intersects(line2, &intersPoint) == QLineF::BoundedIntersection)
|
|
|
|
|
#endif
|
2016-09-15 17:55:28 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::findEndItem()
|
|
|
|
|
{
|
|
|
|
|
QString targetId = tagValue("target");
|
2016-09-18 09:18:12 +03:00
|
|
|
if (!m_endItem && !targetId.isEmpty()) {
|
2016-09-15 17:55:28 +02:00
|
|
|
QList<QGraphicsItem*> items = scene()->items();
|
|
|
|
|
for (int i = 0; i < items.count(); ++i) {
|
|
|
|
|
if (items[i]->type() >= FinalStateType) {
|
|
|
|
|
auto item = qgraphicsitem_cast<ConnectableItem*>(items[i]);
|
|
|
|
|
if (item && item->itemId() == targetId) {
|
2021-01-12 12:12:56 +01:00
|
|
|
setEndItem(item, false);
|
2016-09-15 17:55:28 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateEventName()
|
|
|
|
|
{
|
|
|
|
|
m_eventTagItem->setText(tagValue("event"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::storeGeometry(bool block)
|
|
|
|
|
{
|
|
|
|
|
if (tag()) {
|
|
|
|
|
if (m_cornerPoints.count() <= 2) {
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY, QString(), block);
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY, QString(), block);
|
|
|
|
|
} else {
|
|
|
|
|
QPolygonF pol = m_cornerPoints;
|
|
|
|
|
pol.takeFirst();
|
|
|
|
|
pol.takeLast();
|
|
|
|
|
Serializer s;
|
|
|
|
|
for (int i = 0; i < pol.count(); ++i) {
|
|
|
|
|
QPointF spos = sceneTargetPoint(Start);
|
|
|
|
|
pol[i].setX(pol[i].x() - spos.x());
|
|
|
|
|
pol[i].setY(pol[i].y() - spos.y());
|
|
|
|
|
}
|
|
|
|
|
s.append(pol);
|
|
|
|
|
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY, s.data(), block);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::storeMovePoint(bool block)
|
|
|
|
|
{
|
|
|
|
|
if (m_eventTagItem->movePoint().toPoint() == QPoint(0, 0))
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_MOVEPOINT, QString(), block);
|
|
|
|
|
else
|
|
|
|
|
savePoint(m_eventTagItem->movePoint(), Constants::C_SCXML_EDITORINFO_MOVEPOINT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::storeTargetFactors(bool block)
|
|
|
|
|
{
|
|
|
|
|
if (m_startTargetFactor == QPointF(0.5, 0.5))
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS, QString(), block);
|
|
|
|
|
else
|
|
|
|
|
savePoint(m_startTargetFactor * 100, Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS);
|
|
|
|
|
|
|
|
|
|
if (m_endTargetFactor == QPointF(0.5, 0.5))
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS, QString(), block);
|
|
|
|
|
else
|
|
|
|
|
savePoint(m_endTargetFactor * 100, Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::storeValues(bool block)
|
|
|
|
|
{
|
|
|
|
|
storeGeometry(block);
|
|
|
|
|
storeMovePoint(block);
|
|
|
|
|
storeTargetFactors(block);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateUIProperties()
|
|
|
|
|
{
|
2018-11-24 20:21:25 +01:00
|
|
|
if (tag() && isActiveScene())
|
2016-09-15 17:55:28 +02:00
|
|
|
storeValues();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateTargetType()
|
|
|
|
|
{
|
|
|
|
|
if (m_movingFirstPoint && m_targetType == InternalNoTarget)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TransitionTargetType type = ExternalTarget;
|
|
|
|
|
|
2018-11-24 20:21:25 +01:00
|
|
|
if (m_startItem && m_startItem == m_endItem)
|
2016-09-15 17:55:28 +02:00
|
|
|
type = InternalSameTarget;
|
2018-11-24 20:21:25 +01:00
|
|
|
else if (m_startItem && !m_endItem) {
|
2016-09-15 17:55:28 +02:00
|
|
|
if (m_movingLastPoint) {
|
|
|
|
|
type = ExternalNoTarget;
|
|
|
|
|
} else {
|
|
|
|
|
QRectF srect = m_startItem->sceneBoundingRect();
|
|
|
|
|
if (srect.contains(m_cornerPoints.last()))
|
|
|
|
|
type = InternalNoTarget;
|
|
|
|
|
else
|
|
|
|
|
type = ExternalNoTarget;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
type = ExternalTarget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type <= InternalNoTarget) {
|
|
|
|
|
m_eventTagItem->resetMovePoint();
|
|
|
|
|
m_arrowSize = 6;
|
|
|
|
|
// Remove extra points
|
|
|
|
|
while (m_cornerPoints.count() > 2)
|
|
|
|
|
m_cornerPoints.takeAt(1);
|
|
|
|
|
while (m_cornerGrabbers.count() > 2)
|
|
|
|
|
delete m_cornerGrabbers.takeAt(1);
|
|
|
|
|
|
|
|
|
|
// Remove editorinfo.geometry
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY, QString(), true);
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY, QString(), true);
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_MOVEPOINT, QString(), true);
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS, QString(), true);
|
|
|
|
|
setEditorInfo(Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS, QString(), true);
|
|
|
|
|
} else {
|
|
|
|
|
m_arrowSize = 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_targetType != type) {
|
|
|
|
|
m_targetType = type;
|
|
|
|
|
if (m_warningItem)
|
|
|
|
|
m_warningItem->check();
|
|
|
|
|
if (m_startItem && !m_startItem->blockUpdates()) {
|
|
|
|
|
m_startItem->updateOutputTransitions();
|
|
|
|
|
if (m_startItem->type() >= StateType)
|
|
|
|
|
static_cast<StateItem*>(m_startItem)->updateBoundingRect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ConnectableItem *TransitionItem::connectedItem(const ConnectableItem *other) const
|
|
|
|
|
{
|
|
|
|
|
if (other) {
|
|
|
|
|
if (other == m_startItem)
|
|
|
|
|
return m_endItem;
|
|
|
|
|
else if (other == m_endItem)
|
|
|
|
|
return m_startItem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TransitionItem::isStartItem(const ConnectableItem *item) const
|
|
|
|
|
{
|
|
|
|
|
return m_startItem == item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TransitionItem::isEndItem(const ConnectableItem *item) const
|
|
|
|
|
{
|
|
|
|
|
return m_endItem == item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TransitionItem::hasStartItem() const
|
|
|
|
|
{
|
|
|
|
|
return m_startItem != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TransitionItem::hasEndItem() const
|
|
|
|
|
{
|
|
|
|
|
return m_endItem != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransitionItem::updateZValue()
|
|
|
|
|
{
|
|
|
|
|
setZValue(qMax(m_startItem ? (m_startItem->zValue() + 1) : 1, m_endItem ? (m_endItem->zValue() + 1) : 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qreal TransitionItem::textWidth() const
|
|
|
|
|
{
|
|
|
|
|
return m_eventTagItem->boundingRect().width();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QRectF TransitionItem::wholeBoundingRect() const
|
|
|
|
|
{
|
|
|
|
|
return boundingRect().united(m_eventTagItem->sceneBoundingRect());
|
|
|
|
|
}
|