ScxmlEditor: Initial import

Change-Id: I4701b77ebd4e2520f2616c42206ac17be3a12b60
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Alessandro Portale
2016-09-15 17:55:28 +02:00
parent 1a9919a208
commit 4ea332b062
237 changed files with 25257 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** 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 "actionhandler.h"
#include "mytypes.h"
#include <QAction>
using namespace ScxmlEditor::PluginInterface;
ActionHandler::ActionHandler(QObject *parent)
: QObject(parent)
{
using AH = ActionHandler;
const struct {
const char *icon;
QString name;
QString tooltip;
const char *keyseq;
bool checkable;
} actionInfos[] = {
{ ":/scxmleditor/images/icon-zoom-in.png", AH::tr("Zoom In"), AH::tr("Zoom In (Ctrl + + / Ctrl + Wheel)"), "Ctrl++", false },
{ ":/scxmleditor/images/icon-zoom-out.png", AH::tr("Zoom Out"), AH::tr("Zoom Out (Ctrl + - / Ctrl + Wheel)"), "Ctrl+-", false },
{ ":/scxmleditor/images/icon-fit-screen.png", AH::tr("Fit to View"), AH::tr("Fit to View (F11)"), "F11", false },
{ ":/scxmleditor/images/icon-pan.png", AH::tr("Panning"), AH::tr("Panning (Shift)"), "Shift", true },
{ ":/scxmleditor/images/icon-search.png", AH::tr("Magnifier"), AH::tr("Magnifier Tool (Alt)"), "Alt", true },
{ ":/scxmleditor/images/navigator.png", AH::tr("Navigator"), AH::tr("Navigator (Ctrl+E)"), "Ctrl+E", true },
{ ":/scxmleditor/images/icon-copy.png", AH::tr("Copy"), AH::tr("Copy (Ctrl + C)"), "Ctrl+C", false },
{ ":/scxmleditor/images/icon-cut.png", AH::tr("Cut"), AH::tr("Cut (Ctrl + X)"), "Ctrl+X", false },
{ ":/scxmleditor/images/icon-paste.png", AH::tr("Paste"), AH::tr("Paste (Ctrl + V)"), "Ctrl+V", false },
{ ":/scxmleditor/images/screenshot.png", AH::tr("Screenshot"), AH::tr("Screenshot (Ctrl + Shift + C)"), "Ctrl+Shift+C", false },
{ ":/scxmleditor/images/icon-export-canvas.png", AH::tr("Export to Image"), AH::tr("Export to Image"), "Ctrl+Shift+E", false },
{ ":/scxmleditor/images/fullnamespace.png", AH::tr("Toggle Full Namespace"), AH::tr("Toggle Full Namespace"), "Ctrl+Shift+N", true },
{ ":/scxmleditor/images/align_left.png", AH::tr("Align Left"), AH::tr("Align Left (Ctrl+L,1)"), "Ctrl+L,1", false },
{ ":/scxmleditor/images/align_right.png", AH::tr("Align Right"), AH::tr("Align Right (Ctrl+L,2)"), "Ctrl+L,2", false },
{ ":/scxmleditor/images/align_top.png", AH::tr("Align Top"), AH::tr("Align Top (Ctrl+L,3)"), "Ctrl+L,3", false },
{ ":/scxmleditor/images/align_bottom.png", AH::tr("Align Bottom"), AH::tr("Align Bottom (Ctrl+L,4)"), "Ctrl+L,4", false },
{ ":/scxmleditor/images/align_horizontal.png", AH::tr("Align Horizontal"), AH::tr("Align Horizontal (Ctrl+L,5)"), "Ctrl+L,5", false },
{ ":/scxmleditor/images/align_vertical.png", AH::tr("Align Vertical"), AH::tr("Align Vertical (Ctrl+L,6)"), "Ctrl+L,6", false },
{ ":/scxmleditor/images/adjust_width.png", AH::tr("Adjust Width"), AH::tr("Adjust Width (Ctrl+L,7)"), "Ctrl+L,7", false },
{ ":/scxmleditor/images/adjust_height.png", AH::tr("Adjust Height"), AH::tr("Adjust Height (Ctrl+L,8)"), "Ctrl+L,8", false },
{ ":/scxmleditor/images/adjust_size.png", AH::tr("Adjust Size"), AH::tr("Adjust Size (Ctrl+L,9)"), "Ctrl+L,9", false },
{ ":/scxmleditor/images/statistics.png", AH::tr("Show Statistics..."), AH::tr("Show Statistics"), "", false }
};
// Init actions
for (auto info: actionInfos) {
auto action = new QAction(QIcon(QLatin1String(info.icon)), info.name, this);
action->setCheckable(info.checkable);
action->setToolTip(info.tooltip);
action->setShortcut(QKeySequence(QLatin1String(info.keyseq)));
m_actions << action;
}
}
QAction *ActionHandler::action(ActionType type) const
{
if (type >= ActionZoomIn && type < ActionLast)
return m_actions[type];
return nullptr;
}

View File

@@ -0,0 +1,52 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "mytypes.h"
#include <QObject>
QT_FORWARD_DECLARE_CLASS(QAction)
namespace ScxmlEditor {
namespace PluginInterface {
class ActionHandler : public QObject
{
Q_OBJECT
public:
explicit ActionHandler(QObject *parent = nullptr);
QAction *action(ActionType type) const;
private:
QVector<QAction*> m_actions;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,52 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QObject>
QT_FORWARD_DECLARE_CLASS(QMenu)
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlTag;
class ActionProvider : public QObject
{
Q_OBJECT
public:
explicit ActionProvider(QObject *parent = nullptr)
: QObject(parent)
{
}
virtual void initStateMenu(const ScxmlTag *tag, QMenu *menu) = 0;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,44 @@
/****************************************************************************
**
** 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 "attributeitemdelegate.h"
using namespace ScxmlEditor::PluginInterface;
AttributeItemDelegate::AttributeItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
, m_tag(0)
{
}
void AttributeItemDelegate::setTag(ScxmlTag *tag)
{
m_tag = tag;
}
ScxmlTag *AttributeItemDelegate::tag() const
{
return m_tag;
}

View File

@@ -0,0 +1,52 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "scxmltag.h"
#include <QPointer>
#include <QStyledItemDelegate>
namespace ScxmlEditor {
namespace PluginInterface {
class AttributeItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit AttributeItemDelegate(QObject *parent = nullptr);
void setTag(ScxmlTag *tag);
ScxmlTag *tag() const;
protected:
QPointer<ScxmlTag> m_tag;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** 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 "attributeitemmodel.h"
using namespace ScxmlEditor::PluginInterface;
AttributeItemModel::AttributeItemModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
void AttributeItemModel::setTag(ScxmlTag *tag)
{
beginResetModel();
m_tag = tag;
m_document = m_tag ? m_tag->document() : 0;
endResetModel();
emit layoutChanged();
emit dataChanged(QModelIndex(), QModelIndex());
}
ScxmlTag *AttributeItemModel::tag() const
{
return m_tag;
}

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "scxmldocument.h"
#include "scxmltag.h"
#include <QAbstractTableModel>
#include <QPointer>
namespace ScxmlEditor {
namespace PluginInterface {
class AttributeItemModel : public QAbstractTableModel
{
Q_OBJECT
public:
AttributeItemModel(QObject *parent = nullptr);
virtual void setTag(ScxmlTag *tag);
ScxmlTag *tag() const;
protected:
QPointer<ScxmlDocument> m_document;
QPointer<ScxmlTag> m_tag;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,430 @@
/****************************************************************************
**
** 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 "baseitem.h"
#include "actionhandler.h"
#include "actionprovider.h"
#include "graphicsscene.h"
#include "sceneutils.h"
#include "scxmldocument.h"
#include "scxmleditorconstants.h"
#include "scxmltagutils.h"
#include "scxmluifactory.h"
#include "stateitem.h"
#include <QCoreApplication>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QMenu>
#include <QUndoStack>
using namespace ScxmlEditor::PluginInterface;
BaseItem::BaseItem(BaseItem *parent)
: QGraphicsObject(parent)
{
setFlag(ItemIsFocusable, true);
setItemBoundingRect(QRectF(-60, -50, 120, 100));
m_scene = static_cast<GraphicsScene*>(scene());
if (m_scene)
m_scene->addChild(this);
}
BaseItem::~BaseItem()
{
if (m_scene)
m_scene->removeChild(this);
}
void BaseItem::checkParentBoundingRect()
{
BaseItem *parentBaseItem = this->parentBaseItem();
if (parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates()) {
auto parentStateItem = qgraphicsitem_cast<StateItem*>(parentBaseItem);
if (parentStateItem && (parentStateItem->type() >= StateType))
parentStateItem->updateBoundingRect();
}
}
void BaseItem::updatePolygon()
{
m_polygon.clear();
m_polygon << m_boundingRect.topLeft()
<< m_boundingRect.topRight()
<< m_boundingRect.bottomRight()
<< m_boundingRect.bottomLeft()
<< m_boundingRect.topLeft();
}
void BaseItem::updateDepth()
{
const BaseItem *baseItem = parentBaseItem();
m_depth = baseItem ? baseItem->depth() + 1 : 0;
update();
}
void BaseItem::setItemBoundingRect(const QRectF &r)
{
if (m_boundingRect != r) {
prepareGeometryChange();
m_boundingRect = r;
if (!m_blockUpdates)
checkParentBoundingRect();
updatePolygon();
emit geometryChanged();
}
}
QVariant BaseItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
switch (change) {
case ItemSceneHasChanged:
m_scene = static_cast<GraphicsScene*>(scene());
if (m_scene)
m_scene->addChild(this);
break;
case ItemSelectedHasChanged: {
emit selectedStateChanged(value.toBool());
if (value.toBool() && tag())
tag()->document()->setCurrentTag(tag());
break;
}
case ItemPositionChange:
if (m_scene && type() >= InitialStateType) {
// Snap to items
QPointF currentCenter = sceneCenter();
QPointF targetCenter;
QPair<bool, bool> res = m_scene->checkSnapToItem(this, currentCenter, targetCenter);
QPointF val = value.toPointF();
QPointF change = val - pos();
if (res.first && qAbs(change.x()) < 12)
val.setX(pos().x() + targetCenter.x() - currentCenter.x());
if (res.second && qAbs(change.y()) < 12)
val.setY(pos().y() + targetCenter.y() - currentCenter.y());
return val;
}
break;
case ItemParentChange:
case ItemPositionHasChanged:
case ItemTransformHasChanged:
updateUIProperties();
break;
default:
break;
}
return QGraphicsObject::itemChange(change, value);
}
void BaseItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
if (e->button() == Qt::RightButton) {
e->accept();
showContextMenu(e);
} else {
QGraphicsObject::mousePressEvent(e);
}
}
void BaseItem::showContextMenu(QGraphicsSceneMouseEvent *e)
{
checkSelectionBeforeContextMenu(e);
QMenu menu;
createContextMenu(&menu);
selectedMenuAction(menu.exec(e->screenPos()));
}
void BaseItem::checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e)
{
if (!isSelected() && !(e->modifiers() & Qt::ControlModifier) && m_scene)
m_scene->unselectAll();
if (m_tag)
m_tag->document()->setCurrentTag(m_tag);
}
void BaseItem::createContextMenu(QMenu *menu)
{
if (!menu || !tag())
return;
if (m_scene) {
menu->addAction(m_scene->actionHandler()->action(ActionCopy));
menu->addAction(m_scene->actionHandler()->action(ActionPaste));
menu->addSeparator();
const ScxmlUiFactory *uiFactory = m_scene->uiFactory();
if (uiFactory) {
auto actionProvider = static_cast<ActionProvider*>(uiFactory->object(Constants::C_UI_FACTORY_OBJECT_ACTIONPROVIDER));
if (actionProvider) {
actionProvider->initStateMenu(tag(), menu);
menu->addSeparator();
}
}
}
TagUtils::createChildMenu(tag(), menu);
}
void BaseItem::selectedMenuAction(const QAction *action)
{
if (!action)
return;
ScxmlTag *tag = this->tag();
if (tag) {
const QVariantMap data = action->data().toMap();
int actionType = data.value(Constants::C_SCXMLTAG_ACTIONTYPE, -1).toInt();
switch (actionType) {
case TagUtils::AddChild: {
const ScxmlDocument *document = tag->document();
if (m_scene && document) {
document->undoStack()->beginMacro(tr("Add child"));
SceneUtils::addChild(tag, data, m_scene);
document->undoStack()->endMacro();
}
break;
}
case TagUtils::Remove:
postDeleteEvent();
break;
default:
break;
}
}
}
void BaseItem::postDeleteEvent()
{
QCoreApplication::postEvent(scene(), new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier));
}
void BaseItem::setEditorInfo(const QString &key, const QString &value, bool block)
{
if (m_tag && m_tag->editorInfo(key) != value) {
if (!m_blockUpdates && !block && m_tag->document())
m_tag->document()->setEditorInfo(m_tag, key, value);
else
m_tag->setEditorInfo(key, value);
}
}
QString BaseItem::editorInfo(const QString &key) const
{
if (m_tag)
return m_tag->editorInfo(key);
return QString();
}
void BaseItem::setTagValue(const QString &key, const QString &value)
{
if (m_tag && m_tag->attribute(key) != value) {
if (!m_blockUpdates && m_tag->document())
m_tag->document()->setValue(m_tag, key, value);
else
m_tag->setAttribute(key, value);
}
}
QString BaseItem::tagValue(const QString &key, bool useNameSpace) const
{
return m_tag ? m_tag->attribute(key, useNameSpace) : QString();
}
ScxmlTag *BaseItem::tag() const
{
return m_tag;
}
void BaseItem::setItemSelected(bool sel, bool unselectOthers)
{
if (sel) {
if (unselectOthers && m_scene)
m_scene->unselectAll();
if (m_tag)
m_tag->document()->setCurrentTag(m_tag);
} else {
setSelected(false);
}
}
QRectF BaseItem::boundingRect() const
{
return m_boundingRect;
}
void BaseItem::setTag(ScxmlTag *tag)
{
m_tag = tag;
}
void BaseItem::init(ScxmlTag *tag, BaseItem *parentItem, bool /*initChildren*/, bool /*blockUpdates*/)
{
setTag(tag);
readUISpecifiedProperties(tag);
setParentItem(parentItem);
}
void BaseItem::setBlockUpdates(bool block)
{
m_blockUpdates = block;
}
void BaseItem::setHighlight(bool hl)
{
if (hl != m_highlight) {
m_highlight = hl;
update();
}
}
void BaseItem::setOverlapping(bool ol)
{
if (ol != m_overlapping) {
m_overlapping = ol;
update();
}
}
bool BaseItem::highlight() const
{
return m_highlight;
}
bool BaseItem::overlapping() const
{
return m_overlapping;
}
bool BaseItem::isActiveScene() const
{
return m_scene ? m_scene->topMostScene() : false;
}
ScxmlUiFactory *BaseItem::uiFactory() const
{
return m_scene ? m_scene->uiFactory() : 0;
}
GraphicsScene *BaseItem::graphicsScene() const
{
return m_scene;
}
void BaseItem::setParentItem(BaseItem *item)
{
QGraphicsObject::setParentItem(item);
updateColors();
}
void BaseItem::checkWarnings()
{
}
void BaseItem::checkOverlapping()
{
}
void BaseItem::checkVisibility(double scaleFactor)
{
Q_UNUSED(scaleFactor)
}
void BaseItem::updateUIProperties()
{
}
void BaseItem::readUISpecifiedProperties(const ScxmlTag *tag)
{
Q_UNUSED(tag)
}
void BaseItem::updateAttributes()
{
}
void BaseItem::updateColors()
{
}
void BaseItem::updateEditorInfo(bool /*allChildren*/)
{
readUISpecifiedProperties(m_tag);
}
void BaseItem::moveStateBy(qreal /*dx*/, qreal /*dy*/)
{
}
void BaseItem::finalizeCreation()
{
}
void BaseItem::doLayout(int depth)
{
Q_UNUSED(depth)
}
void BaseItem::checkInitial(bool parent)
{
Q_UNUSED(parent)
}
bool BaseItem::containsScenePoint(const QPointF &p) const
{
return sceneBoundingRect().contains(p);
}
QString BaseItem::itemId() const
{
if (m_tag) {
if (m_tag->tagType() == Transition)
return m_tag->attribute("event");
else
return m_tag->attribute("id", true);
}
return QString();
}
bool BaseItem::blockUpdates() const
{
return m_blockUpdates;
}
BaseItem *BaseItem::parentBaseItem() const
{
QGraphicsItem *parent = parentItem();
return (parent && parent->type() > UnknownType)
? qgraphicsitem_cast<BaseItem*>(parent) : nullptr;
}

View File

@@ -0,0 +1,151 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "mytypes.h"
#include "scxmltag.h"
#include <QGraphicsItem>
#include <QPointer>
#include <QTimer>
QT_FORWARD_DECLARE_CLASS(QAction)
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
QT_FORWARD_DECLARE_CLASS(QVariant)
namespace ScxmlEditor {
namespace PluginInterface {
const qreal WARNING_ITEM_SIZE = 25;
class GraphicsScene;
class ScxmlUiFactory;
/**
* @brief The BaseItem class is the base class for every items in the scene which represent the one tag in the SCXML-tree.
*
*
*/
class BaseItem : public QGraphicsObject
{
Q_OBJECT
public:
explicit BaseItem(BaseItem *parent = nullptr);
~BaseItem() override;
QRectF boundingRect() const override;
QPointF sceneCenter() const
{
return mapToScene(m_boundingRect.center());
}
QString itemId() const;
void setItemBoundingRect(const QRectF &r);
QPolygonF polygonShape() const
{
return m_polygon;
}
void updateDepth();
int depth() const
{
return m_depth;
}
void setParentItem(BaseItem *item);
virtual void checkWarnings();
virtual void checkOverlapping();
virtual void checkVisibility(double scaleFactor);
virtual void setTag(ScxmlTag *tag);
virtual void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false);
virtual void updateAttributes();
virtual void updateColors();
virtual void updateEditorInfo(bool allChildren = false);
virtual void moveStateBy(qreal dx, qreal dy);
virtual ScxmlTag *tag() const;
virtual void finalizeCreation();
virtual void doLayout(int depth);
virtual void setHighlight(bool hl);
virtual void checkInitial(bool parent = false);
virtual bool containsScenePoint(const QPointF &p) const;
void setEditorInfo(const QString &key, const QString &value, bool block = false);
QString editorInfo(const QString &key) const;
void setTagValue(const QString &key, const QString &value);
QString tagValue(const QString &key, bool useNameSpace = false) const;
void setBlockUpdates(bool block);
void setOverlapping(bool ol);
bool blockUpdates() const;
bool highlight() const;
bool overlapping() const;
BaseItem *parentBaseItem() const;
bool isActiveScene() const;
GraphicsScene *graphicsScene() const;
ScxmlUiFactory *uiFactory() const;
virtual void updateUIProperties();
protected:
virtual void updatePolygon();
void setItemSelected(bool sel, bool unselectOthers = true);
signals:
void geometryChanged();
void selectedStateChanged(bool sel);
void openToDifferentView(BaseItem *item);
protected:
void postDeleteEvent();
void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
void showContextMenu(QGraphicsSceneMouseEvent *e);
virtual void checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e);
virtual void createContextMenu(QMenu *menu);
virtual void selectedMenuAction(const QAction *action);
virtual void readUISpecifiedProperties(const ScxmlTag *tag);
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
void checkParentBoundingRect();
QPolygonF m_polygon;
private:
QRectF m_boundingRect;
QPointer<ScxmlTag> m_tag;
QPointer<GraphicsScene> m_scene;
bool m_blockUpdates = false;
int m_depth = 0;
bool m_highlight = false;
bool m_overlapping = false;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,809 @@
/****************************************************************************
**
** 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 "connectableitem.h"
#include "cornergrabberitem.h"
#include "graphicsscene.h"
#include "highlightitem.h"
#include "quicktransitionitem.h"
#include "sceneutils.h"
#include "scxmleditorconstants.h"
#include "serializer.h"
#include "stateitem.h"
#include <QDebug>
#include <QPainter>
#include <QPen>
#include <QStringList>
#include <QUndoStack>
using namespace ScxmlEditor::PluginInterface;
ConnectableItem::ConnectableItem(const QPointF &p, BaseItem *parent)
: BaseItem(parent)
{
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
setAcceptDrops(true);
m_selectedPen.setStyle(Qt::DotLine);
m_selectedPen.setColor(QColor(0x44, 0x44, 0xed));
m_selectedPen.setCosmetic(true);
m_releasedFromParentBrush = QBrush(QColor(0x98, 0x98, 0x98));
setPos(p);
connect(this, &ConnectableItem::geometryChanged, this, &ConnectableItem::updateCornerPositions);
}
ConnectableItem::~ConnectableItem()
{
setBlockUpdates(true);
foreach (ConnectableItem *item, m_overlappedItems) {
item->removeOverlappingItem(this);
}
m_overlappedItems.clear();
foreach (TransitionItem *transition, m_outputTransitions) {
transition->disconnectItem(this);
}
m_outputTransitions.clear();
foreach (TransitionItem *transition, m_inputTransitions) {
transition->disconnectItem(this);
}
m_inputTransitions.clear();
qDeleteAll(m_quickTransitions);
m_quickTransitions.clear();
}
void ConnectableItem::createCorners()
{
for (int i = 0; i < 8; ++i) {
Qt::CursorShape cur = Qt::SizeHorCursor;
switch (i) {
case 0:
case 4:
cur = Qt::SizeFDiagCursor;
break;
case 1:
case 5:
cur = Qt::SizeVerCursor;
break;
case 2:
case 6:
cur = Qt::SizeBDiagCursor;
break;
case 3:
case 7:
cur = Qt::SizeHorCursor;
break;
default:
break;
}
m_corners << new CornerGrabberItem(this, cur);
}
qDeleteAll(m_quickTransitions);
m_quickTransitions.clear();
m_quickTransitions << new QuickTransitionItem(0, UnknownType, this);
m_quickTransitions << new QuickTransitionItem(1, StateType, this);
m_quickTransitions << new QuickTransitionItem(2, ParallelType, this);
m_quickTransitions << new QuickTransitionItem(3, HistoryType, this);
m_quickTransitions << new QuickTransitionItem(4, FinalStateType, this);
updateCornerPositions();
}
void ConnectableItem::removeCorners()
{
qDeleteAll(m_corners);
m_corners.clear();
qDeleteAll(m_quickTransitions);
m_quickTransitions.clear();
}
void ConnectableItem::updateCornerPositions()
{
QRectF r = boundingRect();
if (m_corners.count() == 8) {
qreal cx = r.center().x();
qreal cy = r.center().y();
m_corners[0]->setPos(r.topLeft());
m_corners[1]->setPos(cx, r.top());
m_corners[2]->setPos(r.topRight());
m_corners[3]->setPos(r.right(), cy);
m_corners[4]->setPos(r.bottomRight());
m_corners[5]->setPos(cx, r.bottom());
m_corners[6]->setPos(r.bottomLeft());
m_corners[7]->setPos(r.left(), cy);
}
for (int i = 0; i < m_quickTransitions.count(); ++i) {
m_quickTransitions[i]->setPos(r.topLeft());
m_quickTransitions[i]->setVisible(!m_releasedFromParent && canStartTransition(m_quickTransitions[i]->connectionType()));
}
updateShadowClipRegion();
}
bool ConnectableItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
if (watched->type() == CornerGrabberType) {
auto c = qgraphicsitem_cast<CornerGrabberItem*>(watched);
auto mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
if (c == nullptr || mouseEvent == nullptr)
return BaseItem::sceneEventFilter(watched, event);
QRectF r = boundingRect();
if (event->type() == QEvent::GraphicsSceneMousePress) {
setOpacity(0.5);
} else if (event->type() == QEvent::GraphicsSceneMouseMove) {
QPointF pw = watched->mapToItem(this, mouseEvent->pos());
QRectF rMin;
if (type() >= StateType)
rMin = qgraphicsitem_cast<StateItem*>(this)->childItemsBoundingRect();
if (rMin.isNull()) {
rMin = QRectF(0, 0, m_minimumWidth, m_minimumHeight);
rMin.moveCenter(r.center());
}
switch (m_corners.indexOf(c)) {
case 0:
r.setTopLeft(QPointF(qMin(pw.x(), rMin.left()), qMin(pw.y(), rMin.top())));
break;
case 1:
r.setTop(qMin(pw.y(), rMin.top()));
break;
case 2:
r.setTopRight(QPointF(qMax(pw.x(), rMin.right()), qMin(pw.y(), rMin.top())));
break;
case 3:
r.setRight(qMax(pw.x(), rMin.right()));
break;
case 4:
r.setBottomRight(QPointF(qMax(pw.x(), rMin.right()), qMax(pw.y(), rMin.bottom())));
break;
case 5:
r.setBottom(qMax(pw.y(), rMin.bottom()));
break;
case 6:
r.setBottomLeft(QPointF(qMin(pw.x(), rMin.left()), qMax(pw.y(), rMin.bottom())));
break;
case 7:
r.setLeft(qMin(pw.x(), rMin.left()));
break;
default:
break;
}
setItemBoundingRect(r);
updateCornerPositions();
updateTransitions();
return true;
} else if (event->type() == QEvent::GraphicsSceneMouseRelease) {
setOpacity(1.0);
updateUIProperties();
checkOverlapping();
}
} else if (watched->type() == QuickTransitionType) {
auto mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
if (!mouseEvent)
return BaseItem::sceneEventFilter(watched, event);
if (event->type() == QEvent::GraphicsSceneMousePress) {
m_newTransitionStartedPoint = mouseEvent->pos();
tag()->document()->undoStack()->beginMacro(tr("Add new state"));
m_newTransition = new TransitionItem;
scene()->addItem(m_newTransition);
ScxmlTag *newTag = nullptr;
if (tag()->tagType() == Initial || tag()->tagType() == History)
newTag = new ScxmlTag(InitialTransition, tag()->document());
else
newTag = new ScxmlTag(Transition, tag()->document());
newTag->setAttribute("type", "external");
m_newTransition->init(newTag);
tag()->document()->addTag(tag(), newTag);
m_newTransition->startTransitionFrom(this, watched->mapToScene(mouseEvent->pos()));
return true;
} else if (event->type() == QEvent::GraphicsSceneMouseMove) {
if (m_newTransition)
m_newTransition->setEndPos(watched->mapToScene(mouseEvent->pos()));
} else if (event->type() == QEvent::GraphicsSceneMouseRelease) {
auto tra = qgraphicsitem_cast<QuickTransitionItem*>(watched);
if (!tra)
return BaseItem::sceneEventFilter(watched, event);
if (m_newTransition) {
QLineF line(m_newTransitionStartedPoint, mouseEvent->pos());
if (line.length() > 30) {
m_newTransition->connectToTopItem(watched->mapToScene(mouseEvent->pos()), TransitionItem::End, tra->connectionType());
m_newTransition = nullptr;
setSelected(false);
tag()->document()->undoStack()->endMacro();
} else {
m_newTransition->grabMouse(tra->connectionType());
m_newTransition = nullptr;
setSelected(false);
}
return true;
}
}
}
return BaseItem::sceneEventFilter(watched, event);
}
void ConnectableItem::addOutputTransition(TransitionItem *transition)
{
m_outputTransitions.append(transition);
transitionCountChanged();
}
void ConnectableItem::addInputTransition(TransitionItem *transition)
{
m_inputTransitions.append(transition);
transitionCountChanged();
}
void ConnectableItem::removeOutputTransition(TransitionItem *transition)
{
m_outputTransitions.removeAll(transition);
transitionCountChanged();
}
void ConnectableItem::removeInputTransition(TransitionItem *transition)
{
m_inputTransitions.removeAll(transition);
transitionCountChanged();
}
void ConnectableItem::updateInputTransitions()
{
foreach (TransitionItem *transition, m_inputTransitions) {
transition->updateComponents();
transition->updateUIProperties();
}
transitionsChanged();
}
void ConnectableItem::updateOutputTransitions()
{
foreach (TransitionItem *transition, m_outputTransitions) {
transition->updateComponents();
transition->updateUIProperties();
}
transitionsChanged();
}
void ConnectableItem::updateTransitions(bool allChildren)
{
updateOutputTransitions();
updateInputTransitions();
if (allChildren) {
foreach (QGraphicsItem *it, childItems()) {
auto item = static_cast<ConnectableItem*>(it);
if (item && item->type() >= InitialStateType)
item->updateTransitions(allChildren);
}
}
}
void ConnectableItem::updateTransitionAttributes(bool allChildren)
{
foreach (TransitionItem *transition, m_outputTransitions)
transition->updateTarget();
foreach (TransitionItem *transition, m_inputTransitions)
transition->updateTarget();
if (allChildren) {
foreach (QGraphicsItem *it, childItems()) {
auto item = static_cast<ConnectableItem*>(it);
if (item && item->type() >= InitialStateType)
item->updateTransitionAttributes(allChildren);
}
}
}
void ConnectableItem::transitionsChanged()
{
}
void ConnectableItem::transitionCountChanged()
{
}
QPointF ConnectableItem::getInternalPosition(const TransitionItem *transition, TransitionItem::TransitionTargetType type) const
{
const QRectF srect = sceneBoundingRect();
int ind = 0;
if (type == TransitionItem::InternalNoTarget) {
foreach (TransitionItem *item, m_outputTransitions) {
if (item->targetType() == TransitionItem::InternalSameTarget)
ind++;
}
}
for (int i = 0; i < m_outputTransitions.count(); ++i) {
TransitionItem *item = m_outputTransitions[i];
if (item == transition)
break;
else if (item->targetType() == type)
ind++;
}
return QPointF(srect.left() + 20, srect.top() + srect.height() * 0.06 + 40 + ind * 30);
}
void ConnectableItem::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;
}
BaseItem::mousePressEvent(event);
}
void ConnectableItem::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_moveMacroStarted) {
m_moveMacroStarted = true;
tag()->document()->undoStack()->beginMacro(tr("Move State"));
}
//Restore old behavior if ctrl & alt modifiers are present
if (!m_releasedFromParent && !(event->modifiers() & Qt::AltModifier) && !(event->modifiers() & Qt::ControlModifier)) {
releaseFromParent();
foreach (QGraphicsItem *it, scene()->selectedItems()) {
if (it->type() >= InitialStateType && it != this) {
qgraphicsitem_cast<ConnectableItem*>(it)->releaseFromParent();
}
}
} else
setOpacity(0.5);
BaseItem::mouseMoveEvent(event);
}
void ConnectableItem::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;
}
BaseItem::mouseReleaseEvent(event);
if (m_releasedFromParent) {
// Try to find parent
QList<QGraphicsItem*> parentItems = scene()->items(event->scenePos());
BaseItem *parentItem = nullptr;
for (int i = 0; i < parentItems.count(); ++i) {
auto item = static_cast<BaseItem*>(parentItems[i]);
if (item && item != this && !item->isSelected()
&& item->type() >= StateType
&& SceneUtils::canDrop(item->type(), type())) {
parentItem = item;
break;
}
}
connectToParent(parentItem);
foreach (QGraphicsItem *it, scene()->selectedItems()) {
if (it->type() >= InitialStateType && it != this)
qgraphicsitem_cast<ConnectableItem*>(it)->connectToParent(parentItem);
}
} else
setOpacity(1.0);
if (m_moveMacroStarted) {
m_moveMacroStarted = false;
tag()->document()->undoStack()->endMacro();
}
checkOverlapping();
}
void ConnectableItem::releaseFromParent()
{
m_releasedFromParent = true;
setOpacity(0.5);
m_releasedIndex = tag()->index();
m_releasedParent = parentItem();
tag()->document()->changeParent(tag(), 0, m_releasedParent == nullptr ? m_releasedIndex : -1);
setZValue(503);
for (int i = 0; i < m_quickTransitions.count(); ++i)
m_quickTransitions[i]->setVisible(false);
for (int i = 0; i < m_corners.count(); ++i)
m_corners[i]->setVisible(false);
update();
}
void ConnectableItem::connectToParent(BaseItem *parentItem)
{
for (int i = 0; i < m_quickTransitions.count(); ++i)
m_quickTransitions[i]->setVisible(canStartTransition(m_quickTransitions[i]->connectionType()));
for (int i = 0; i < m_corners.count(); ++i)
m_corners[i]->setVisible(true);
tag()->document()->changeParent(tag(), parentItem ? parentItem->tag() : 0, parentItem == m_releasedParent ? m_releasedIndex : -1);
setZValue(0);
m_releasedIndex = -1;
m_releasedParent = nullptr;
m_releasedFromParent = false;
setOpacity(1.0);
}
QVariant ConnectableItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
switch (change) {
case ItemSelectedHasChanged: {
if (value.toBool()) {
createCorners();
SceneUtils::moveTop(this, static_cast<GraphicsScene*>(scene()));
} else
removeCorners();
break;
}
case ItemParentHasChanged:
updateTransitions(true);
updateTransitionAttributes(true);
// FIXME: intended fallthrough?
case ItemPositionHasChanged:
if (!m_releasedFromParent && !blockUpdates())
checkParentBoundingRect();
break;
case ItemScenePositionHasChanged:
updateTransitions();
if (m_highlighItem)
m_highlighItem->advance(1);
break;
default:
break;
}
return BaseItem::itemChange(change, value);
}
qreal ConnectableItem::getOpacity()
{
if (opacity() < 1.0)
return opacity();
if (overlapping())
return 0.5;
if (parentBaseItem())
if (parentBaseItem()->type() >= InitialStateType)
return static_cast<ConnectableItem*>(parentBaseItem())->getOpacity();
return 1;
}
void ConnectableItem::updateShadowClipRegion()
{
QPainterPath br, sr;
//StateItem Background rounded rectangle
br.addRoundedRect(boundingRect().adjusted(5, 5, -5, -5), 10, 10);
//Shadow rounded rectangle
sr.addRoundedRect(boundingRect().adjusted(10, 10, 0, 0), 10, 10);
//Clippath is subtract
m_shadowClipPath = sr - br;
}
void ConnectableItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setOpacity(getOpacity());
if (m_releasedFromParent) {
painter->setPen(Qt::NoPen);
painter->setBrush(m_releasedFromParentBrush);
painter->setClipping(true);
painter->setClipPath(m_shadowClipPath);
//Since the form is already cliped just draw a rectangle
painter->drawRect(boundingRect().adjusted(10, 10, 0, 0));
painter->setClipping(false);
}
if (isSelected()) {
painter->setPen(m_selectedPen);
painter->setBrush(Qt::NoBrush);
painter->drawRect(boundingRect());
}
painter->restore();
}
void ConnectableItem::updateUIProperties()
{
if (tag() != 0 && isActiveScene()) {
Serializer s;
s.append(pos());
s.append(boundingRect());
setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY, s.data());
s.clear();
s.append(scenePos());
s.append(sceneBoundingRect());
setEditorInfo("scenegeometry", s.data());
}
}
void ConnectableItem::updateAttributes()
{
BaseItem::updateAttributes();
foreach (TransitionItem *transition, m_inputTransitions) {
if (transition->isEndItem(this))
transition->setTagValue("target", itemId());
}
updateInputTransitions();
update();
}
void ConnectableItem::updateEditorInfo(bool allChildren)
{
BaseItem::updateEditorInfo(allChildren);
updateTransitions();
}
void ConnectableItem::moveStateBy(qreal dx, qreal dy)
{
moveBy(dx, dy);
updateUIProperties();
updateTransitions();
}
void ConnectableItem::setHighlight(bool hl)
{
BaseItem::setHighlight(hl);
if (highlight()) {
if (!m_highlighItem) {
m_highlighItem = new HighlightItem(this);
scene()->addItem(m_highlighItem);
}
} else {
delete m_highlighItem;
m_highlighItem = nullptr;
}
if (m_highlighItem)
m_highlighItem->advance(0);
}
void ConnectableItem::readUISpecifiedProperties(const ScxmlTag *tag)
{
if (tag) {
QString data = editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY);
if (!data.isEmpty()) {
QPointF p(0, 0);
QRectF r(-60, 50, 120, 100);
Serializer s;
s.setData(data);
s.read(p);
s.read(r);
setItemBoundingRect(r);
setPos(p);
}
}
}
void ConnectableItem::addTransitions(const ScxmlTag *tag)
{
if (scene()) {
for (int i = 0; i < tag->childCount(); ++i) {
ScxmlTag *child = tag->child(i);
if (child->tagType() == Transition || child->tagType() == InitialTransition) {
auto transition = new TransitionItem;
scene()->addItem(transition);
transition->setStartItem(this);
transition->init(child);
}
}
}
}
void ConnectableItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, bool /*blockUpdates*/)
{
BaseItem::init(tag, parentItem);
if (initChildren)
addTransitions(tag);
}
void ConnectableItem::setMinimumWidth(int width)
{
m_minimumWidth = width;
QRectF r = boundingRect();
if (r.width() < width) {
r.setWidth(width);
setItemBoundingRect(r);
}
}
void ConnectableItem::setMinimumHeight(int height)
{
m_minimumHeight = height;
QRectF r = boundingRect();
if (r.height() < height) {
r.setHeight(height);
setItemBoundingRect(r);
}
}
void ConnectableItem::finalizeCreation()
{
bool old = blockUpdates();
setBlockUpdates(true);
updateAttributes();
updateEditorInfo();
updateUIProperties();
checkInitial(true);
if (!old)
setBlockUpdates(false);
}
bool ConnectableItem::hasInputTransitions(const ConnectableItem *parentItem, bool checkChildren) const
{
foreach (const TransitionItem *it, m_inputTransitions) {
if (!SceneUtils::isChild(parentItem, it->connectedItem(this)))
return true;
}
if (checkChildren) {
foreach (QGraphicsItem *it, childItems()) {
if (it->type() >= InitialStateType) {
auto item = qgraphicsitem_cast<ConnectableItem*>(it);
if (item && item->hasInputTransitions(parentItem, checkChildren))
return true;
}
}
}
return false;
}
bool ConnectableItem::hasOutputTransitions(const ConnectableItem *parentItem, bool checkChildren) const
{
foreach (TransitionItem *it, m_outputTransitions) {
if (!SceneUtils::isChild(parentItem, it->connectedItem(this)))
return true;
}
if (checkChildren) {
foreach (QGraphicsItem *it, childItems()) {
if (it->type() >= InitialStateType) {
auto item = qgraphicsitem_cast<ConnectableItem*>(it);
if (item && item->hasOutputTransitions(parentItem, checkChildren))
return true;
}
}
}
return false;
}
void ConnectableItem::addOverlappingItem(ConnectableItem *item)
{
if (!m_overlappedItems.contains(item))
m_overlappedItems.append(item);
setOverlapping(m_overlappedItems.count() > 0);
}
void ConnectableItem::removeOverlappingItem(ConnectableItem *item)
{
if (m_overlappedItems.contains(item))
m_overlappedItems.removeAll(item);
setOverlapping(m_overlappedItems.count() > 0);
}
void ConnectableItem::checkOverlapping()
{
QVector<ConnectableItem*> overlappedItems;
foreach (QGraphicsItem *it, collidingItems()) {
if (it->type() >= InitialStateType && it->parentItem() == parentItem()) {
overlappedItems << qgraphicsitem_cast<ConnectableItem*>(it);
}
}
// Remove unnecessary items
for (int i = m_overlappedItems.count(); i--;) {
if (!overlappedItems.contains(m_overlappedItems[i])) {
m_overlappedItems[i]->removeOverlappingItem(this);
m_overlappedItems.removeAt(i);
}
}
// Add new overlapped items
foreach (ConnectableItem *it, overlappedItems) {
if (!m_overlappedItems.contains(it)) {
m_overlappedItems << it;
it->addOverlappingItem(this);
}
}
setOverlapping(m_overlappedItems.count() > 0);
}
bool ConnectableItem::canStartTransition(ItemType type) const
{
Q_UNUSED(type)
return true;
}
QVector<TransitionItem*> ConnectableItem::outputTransitions() const
{
return m_outputTransitions;
}
QVector<TransitionItem*> ConnectableItem::inputTransitions() const
{
return m_inputTransitions;
}
int ConnectableItem::transitionCount() const
{
return m_outputTransitions.count() + m_inputTransitions.count();
}
int ConnectableItem::outputTransitionCount() const
{
return m_outputTransitions.count();
}
int ConnectableItem::inputTransitionCount() const
{
return m_inputTransitions.count();
}

View File

@@ -0,0 +1,146 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
#include "transitionitem.h"
#include <QPen>
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
namespace ScxmlEditor {
namespace PluginInterface {
class CornerGrabberItem;
class HighlightItem;
class QuickTransitionItem;
/**
* @brief The ConnectableItem class is the base class for all draggable state-items.
*/
class ConnectableItem : public BaseItem
{
Q_OBJECT
public:
explicit ConnectableItem(const QPointF &pos, BaseItem *parent = nullptr);
~ConnectableItem() override;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override;
void addOutputTransition(TransitionItem *transition);
void addInputTransition(TransitionItem *transition);
void removeOutputTransition(TransitionItem *transition);
void removeInputTransition(TransitionItem *transition);
QVector<TransitionItem*> outputTransitions() const;
QVector<TransitionItem*> inputTransitions() const;
void setMinimumWidth(int width);
void setMinimumHeight(int height);
int minHeight() const
{
return m_minimumHeight;
}
int minWidth() const
{
return m_minimumWidth;
}
void finalizeCreation() override;
void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false) override;
void updateUIProperties() override;
void updateAttributes() override;
void updateEditorInfo(bool allChildren = false) override;
void moveStateBy(qreal dx, qreal dy) override;
void setHighlight(bool hl) override;
int transitionCount() const;
int outputTransitionCount() const;
int inputTransitionCount() const;
bool hasInputTransitions(const ConnectableItem *parentItem, bool checkChildren = false) const;
bool hasOutputTransitions(const ConnectableItem *parentItem, bool checkChildren = false) const;
QPointF getInternalPosition(const TransitionItem *transition, TransitionItem::TransitionTargetType type) const;
void updateOutputTransitions();
void updateInputTransitions();
void updateTransitions(bool allChildren = false);
void updateTransitionAttributes(bool allChildren = false);
void addOverlappingItem(ConnectableItem *item);
void removeOverlappingItem(ConnectableItem *item);
void checkOverlapping() override;
// Parent change
virtual void releaseFromParent();
virtual void connectToParent(BaseItem *parentItem);
qreal getOpacity();
protected:
void readUISpecifiedProperties(const ScxmlTag *tag) override;
void addTransitions(const ScxmlTag *tag);
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
virtual bool canStartTransition(ItemType type) const;
virtual void transitionCountChanged();
virtual void transitionsChanged();
private:
void updateCornerPositions();
void createCorners();
void removeCorners();
void updateShadowClipRegion();
QVector<TransitionItem*> m_outputTransitions;
QVector<TransitionItem*> m_inputTransitions;
QVector<CornerGrabberItem*> m_corners;
QVector<QuickTransitionItem*> m_quickTransitions;
HighlightItem *m_highlighItem = nullptr;
TransitionItem *m_newTransition = nullptr;
QPen m_selectedPen;
QBrush m_releasedFromParentBrush;
int m_minimumWidth = 120;
int m_minimumHeight = 100;
bool m_releasedFromParent = false;
int m_releasedIndex = -1;
QGraphicsItem *m_releasedParent = nullptr;
QPointF m_newTransitionStartedPoint;
QPainterPath m_shadowClipPath;
QPointF m_moveStartPoint;
bool m_moveMacroStarted = false;
QVector<ConnectableItem*> m_overlappedItems;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,93 @@
/****************************************************************************
**
** 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 "cornergrabberitem.h"
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
CornerGrabberItem::CornerGrabberItem(QGraphicsItem *parent, Qt::CursorShape cshape)
: QGraphicsObject(parent)
, m_cursorShape(cshape)
{
setFlag(ItemIgnoresTransformations, true);
setParentItem(parent);
installSceneEventFilter(parent);
setZValue(500);
m_rect = QRectF(-5, -5, 10, 10);
m_drawingRect = m_rect.adjusted(2, 2, -2, -2);
setAcceptHoverEvents(true);
}
void CornerGrabberItem::updateCursor()
{
setCursor(m_selected ? m_cursorShape : Qt::ArrowCursor);
}
void CornerGrabberItem::setSelected(bool para)
{
m_selected = para;
updateCursor();
update();
}
void CornerGrabberItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
{
Q_UNUSED(e)
setSelected(false);
}
void CornerGrabberItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
{
Q_UNUSED(e)
setSelected(true);
}
void CornerGrabberItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
m_pressPoint = e->pos();
}
void CornerGrabberItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(Qt::NoPen);
painter->setBrush(isEnabled() ? QColor(0x62, 0x62, 0xf9) : QColor(0x12, 0x12, 0x12));
if (m_grabberType == Square)
painter->drawRect(m_drawingRect);
else
painter->drawEllipse(m_drawingRect);
painter->restore();
}

View File

@@ -0,0 +1,96 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneHoverEvent)
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The CornerGrabberItem class provides the way to connect/create item(s) straight from the second item.
*
*/
class CornerGrabberItem : public QGraphicsObject
{
public:
explicit CornerGrabberItem(QGraphicsItem *parent, Qt::CursorShape cshape = Qt::PointingHandCursor);
enum GrabberType {
Square,
Circle
};
/**
* @brief type of the item
*/
int type() const override
{
return CornerGrabberType;
}
QRectF boundingRect() const override
{
return m_rect;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QPointF pressedPoint()
{
return m_pressPoint;
}
void setGrabberType(GrabberType type)
{
m_grabberType = type;
}
void setSelected(bool para);
bool isSelected() const
{
return m_selected;
}
private:
void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
void updateCursor();
QRectF m_rect, m_drawingRect;
QPointF m_pressPoint;
GrabberType m_grabberType = Square;
Qt::CursorShape m_cursorShape;
bool m_selected = false;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,80 @@
/****************************************************************************
**
** 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 "finalstateitem.h"
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
FinalStateItem::FinalStateItem(const QPointF &pos, BaseItem *parent)
: ConnectableItem(pos, parent)
{
setItemBoundingRect(QRectF(-20, -20, 40, 40));
setMinimumHeight(40);
setMinimumWidth(40);
m_pen.setColor(qRgb(0x12, 0x12, 0x12));
m_pen.setWidth(2);
}
void FinalStateItem::updatePolygon()
{
QRectF r = boundingRect();
m_size = qMin(r.width() * 0.45, r.height() * 0.45);
QPointF center = r.center();
m_polygon.clear();
m_polygon << (center + QPointF(-m_size, -m_size))
<< (center + QPointF(m_size, -m_size))
<< (center + QPointF(m_size, m_size))
<< (center + QPointF(-m_size, m_size))
<< (center + QPointF(-m_size, -m_size));
}
void FinalStateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
ConnectableItem::paint(painter, option, widget);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setOpacity(getOpacity());
painter->setBrush(QColor(0xff, 0xff, 0xff));
m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
painter->setPen(m_pen);
painter->drawEllipse(boundingRect().center(), m_size, m_size);
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0x4d, 0x4d, 0x4d));
painter->drawEllipse(boundingRect().center(), m_size * 0.8, m_size * 0.8);
painter->restore();
}
bool FinalStateItem::canStartTransition(ItemType type) const
{
Q_UNUSED(type)
return false;
}

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "connectableitem.h"
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The FinalStateItem class represents Final-state of the SCXML-standard. It is a extended class from the ConnectableItem.
*/
class FinalStateItem : public ConnectableItem
{
Q_OBJECT
public:
FinalStateItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
int type() const override
{
return FinalStateType;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
protected:
void updatePolygon() override;
bool canStartTransition(ItemType type) const override;
private:
qreal m_size;
QPen m_pen;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,85 @@
/****************************************************************************
**
** 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 "genericscxmlplugin.h"
#include "mytypes.h"
#include "scattributeitemdelegate.h"
#include "scattributeitemmodel.h"
#include "scgraphicsitemprovider.h"
#include "scshapeprovider.h"
#include "scutilsprovider.h"
#include "scxmluifactory.h"
using namespace ScxmlEditor::PluginInterface;
GenericScxmlPlugin::GenericScxmlPlugin(QObject *parent)
: QObject(parent)
{
}
GenericScxmlPlugin::~GenericScxmlPlugin()
{
delete m_attributeItemDelegate;
delete m_attributeItemModel;
delete m_graphicsItemProvider;
delete m_shapeProvider;
delete m_utilsProvider;
}
void GenericScxmlPlugin::documentChanged(DocumentChangeType type, ScxmlDocument *doc)
{
Q_UNUSED(type)
Q_UNUSED(doc)
}
void GenericScxmlPlugin::detach()
{
m_factory->unregisterObject("attributeItemDelegate", m_attributeItemDelegate);
m_factory->unregisterObject("attributeItemModel", m_attributeItemModel);
m_factory->unregisterObject("graphicsItemProvider", m_graphicsItemProvider);
m_factory->unregisterObject("shapeProvider", m_shapeProvider);
m_factory->unregisterObject("utilsProvider", m_utilsProvider);
}
void GenericScxmlPlugin::init(ScxmlUiFactory *factory)
{
m_factory = factory;
m_attributeItemDelegate = new SCAttributeItemDelegate;
m_attributeItemModel = new SCAttributeItemModel;
m_graphicsItemProvider = new SCGraphicsItemProvider;
m_shapeProvider = new SCShapeProvider;
m_utilsProvider = new SCUtilsProvider;
m_factory->registerObject("attributeItemDelegate", m_attributeItemDelegate);
m_factory->registerObject("attributeItemModel", m_attributeItemModel);
m_factory->registerObject("graphicsItemProvider", m_graphicsItemProvider);
m_factory->registerObject("shapeProvider", m_shapeProvider);
m_factory->registerObject("utilsProvider", m_utilsProvider);
}
void GenericScxmlPlugin::refresh()
{
}

View File

@@ -0,0 +1,69 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "isceditor.h"
#include "mytypes.h"
#include <QObject>
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlUiFactory;
class SCAttributeItemDelegate;
class SCAttributeItemModel;
class ScxmlDocument;
class SCGraphicsItemProvider;
class SCShapeProvider;
class SCUtilsProvider;
class GenericScxmlPlugin : public QObject, public ISCEditor
{
Q_OBJECT
Q_INTERFACES(ScxmlEditor::PluginInterface::ISCEditor)
public:
GenericScxmlPlugin(QObject *parent = nullptr);
~GenericScxmlPlugin() override;
void init(ScxmlUiFactory *factory) override;
void detach() override;
void documentChanged(DocumentChangeType type, ScxmlDocument *doc) override;
void refresh() override;
private:
ScxmlUiFactory *m_factory = nullptr;
SCAttributeItemDelegate *m_attributeItemDelegate = nullptr;
SCAttributeItemModel *m_attributeItemModel = nullptr;
SCGraphicsItemProvider *m_graphicsItemProvider = nullptr;
SCShapeProvider *m_shapeProvider = nullptr;
SCUtilsProvider *m_utilsProvider = nullptr;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QObject>
namespace ScxmlEditor {
namespace PluginInterface {
class BaseItem;
class WarningItem;
class GraphicsItemProvider : public QObject
{
Q_OBJECT
public:
explicit GraphicsItemProvider(QObject *parent = nullptr)
: QObject(parent)
{
}
virtual WarningItem *createWarningItem(const QString &key, BaseItem *parentItem) const = 0;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,944 @@
/****************************************************************************
**
** 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 "graphicsscene.h"
#include "actionhandler.h"
#include "actionprovider.h"
#include "finalstateitem.h"
#include "initialstateitem.h"
#include "parallelitem.h"
#include "sceneutils.h"
#include "scxmleditorconstants.h"
#include "scxmltagutils.h"
#include "scxmluifactory.h"
#include "snapline.h"
#include "stateitem.h"
#include "transitionitem.h"
#include "utilsprovider.h"
#include "warning.h"
#include "warningitem.h"
#include <utils/qtcassert.h>
#include <QAction>
#include <QGuiApplication>
#include <QClipboard>
#include <QMenu>
#include <QMimeData>
#include <QThread>
#include <QUndoStack>
using namespace ScxmlEditor::PluginInterface;
GraphicsScene::GraphicsScene(QObject *parent)
: QGraphicsScene(parent)
{
//setMinimumRenderSize(5);
setItemIndexMethod(QGraphicsScene::NoIndex);
}
GraphicsScene::~GraphicsScene()
{
clear();
}
void GraphicsScene::unselectAll()
{
const QList<QGraphicsItem*> selectedItems = this->selectedItems();
foreach (QGraphicsItem *it, selectedItems)
it->setSelected(false);
if (m_document)
m_document->setCurrentTag(0);
}
void GraphicsScene::unhighlightAll()
{
foreach (BaseItem *it, m_baseItems)
it->setHighlight(false);
}
void GraphicsScene::highlightItems(const QVector<ScxmlTag*> &lstIds)
{
foreach (BaseItem *it, m_baseItems)
it->setHighlight(lstIds.contains(it->tag()));
}
QRectF GraphicsScene::selectedBoundingRect() const
{
QRectF r;
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected())
r = r.united(item->sceneBoundingRect());
}
return r;
}
qreal GraphicsScene::selectedMaxWidth() const
{
qreal maxw = 0;
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
maxw = qMax(maxw, item->sceneBoundingRect().width());
}
return maxw;
}
qreal GraphicsScene::selectedMaxHeight() const
{
qreal maxh = 0;
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
maxh = qMax(maxh, item->sceneBoundingRect().height());
}
return maxh;
}
void GraphicsScene::alignStates(int alignType)
{
if (alignType >= ActionAlignLeft && alignType <= ActionAlignVertical) {
m_document->undoStack()->beginMacro(tr("Align states"));
QRectF r = selectedBoundingRect();
if (r.isValid()) {
switch (alignType) {
case ActionAlignLeft:
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
item->moveStateBy(r.left() - item->sceneBoundingRect().left(), 0);
}
break;
case ActionAlignRight:
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
item->moveStateBy(r.right() - item->sceneBoundingRect().right(), 0);
}
break;
case ActionAlignTop:
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
item->moveStateBy(0, r.top() - item->sceneBoundingRect().top());
}
break;
case ActionAlignBottom:
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
item->moveStateBy(0, r.bottom() - item->sceneBoundingRect().bottom());
}
break;
case ActionAlignHorizontal:
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
item->moveStateBy(0, r.center().y() - item->sceneBoundingRect().center().y());
}
break;
case ActionAlignVertical:
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType)
item->moveStateBy(r.center().x() - item->sceneBoundingRect().center().x(), 0);
}
break;
default:
break;
}
}
m_document->undoStack()->endMacro();
}
}
void GraphicsScene::adjustStates(int adjustType)
{
if (adjustType >= ActionAdjustWidth && adjustType <= ActionAdjustSize) {
m_document->undoStack()->beginMacro(tr("Adjust states"));
qreal maxw = selectedMaxWidth();
qreal maxh = selectedMaxHeight();
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= InitialStateType) {
QRectF rr = item->boundingRect();
if ((adjustType == ActionAdjustWidth || adjustType == ActionAdjustSize) && !qFuzzyCompare(rr.width(), maxw))
rr.setWidth(maxw);
if ((adjustType == ActionAdjustHeight || adjustType == ActionAdjustSize) && !qFuzzyCompare(rr.height(), maxh))
rr.setHeight(maxh);
item->setItemBoundingRect(rr);
qgraphicsitem_cast<ConnectableItem*>(item)->updateTransitions(true);
}
}
m_document->undoStack()->endMacro();
}
}
void GraphicsScene::cut()
{
m_document->undoStack()->beginMacro(tr("Cut"));
copy();
removeSelectedItems();
m_document->undoStack()->endMacro();
}
void GraphicsScene::removeSelectedItems()
{
QVector<ScxmlTag*> tags = SceneUtils::findRemovedTags(m_baseItems);
if (tags.count() > 0) {
m_document->undoStack()->beginMacro(tr("Remove item(s)"));
// Then remove found tags
for (int i = tags.count(); i--;) {
m_document->setCurrentTag(tags[i]);
m_document->removeTag(tags[i]);
}
m_document->setCurrentTag(0);
m_document->undoStack()->endMacro();
}
}
void GraphicsScene::copy()
{
QPointF minPos;
QVector<ScxmlTag*> tags;
if (m_document->currentTag()->tagType() == Scxml) {
QVector<BaseItem*> items;
foreach (BaseItem *item, m_baseItems) {
if (!item->parentItem())
items << item;
}
tags = SceneUtils::findCopyTags(items, minPos);
} else {
tags = SceneUtils::findCopyTags(m_baseItems, minPos);
}
if (tags.isEmpty() && m_document->currentTag())
tags << m_document->currentTag();
if (tags.count() > 0) {
auto mime = new QMimeData;
QByteArray result = m_document->content(tags);
mime->setText(QLatin1String(result));
mime->setData("StateChartEditor/StateData", result);
QStringList strTypes;
foreach (const ScxmlTag *tag, tags) {
strTypes << tag->tagName(false);
}
mime->setData("StateChartEditor/CopiedTagTypes", strTypes.join(",").toLocal8Bit());
mime->setData("StateChartEditor/CopiedMinPos", QString::fromLatin1("%1:%2").arg(minPos.x()).arg(minPos.y()).toLocal8Bit());
QGuiApplication::clipboard()->setMimeData(mime);
}
checkPaste();
}
void GraphicsScene::checkPaste()
{
const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
const QString copiedTagTypes = QLatin1String(mimeData->data("StateChartEditor/CopiedTagTypes"));
emit pasteAvailable(TagUtils::checkPaste(copiedTagTypes, m_document->currentTag()));
}
void GraphicsScene::paste(const QPointF &targetPos)
{
const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
QPointF startPos(targetPos);
BaseItem *targetItem = nullptr;
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= StateType) {
targetItem = item;
break;
}
}
if (m_lastPasteTargetItem != targetItem)
m_pasteCounter = 0;
m_lastPasteTargetItem = targetItem;
if (m_lastPasteTargetItem)
startPos = m_lastPasteTargetItem->boundingRect().topLeft();
QPointF pastedPos = startPos + QPointF(m_pasteCounter * 30, m_pasteCounter * 30);
m_pasteCounter++;
QString strMinPos = QLatin1String(mimeData->data("StateChartEditor/CopiedMinPos"));
QPointF minPos(0, 0);
if (!strMinPos.isEmpty()) {
QStringList coords = strMinPos.split(":", QString::SkipEmptyParts);
if (coords.count() == 2)
minPos = QPointF(coords[0].toDouble(), coords[1].toDouble());
}
m_document->pasteData(mimeData->data("StateChartEditor/StateData"), minPos, pastedPos);
}
void GraphicsScene::setEditorInfo(const QString &key, const QString &value)
{
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected() && item->type() >= TransitionType)
item->setEditorInfo(key, value);
}
}
void GraphicsScene::setDocument(ScxmlDocument *document)
{
if (m_document)
disconnect(m_document, 0, this, 0);
m_document = document;
init();
connectDocument();
}
void GraphicsScene::connectDocument()
{
if (m_document) {
connect(m_document, &ScxmlDocument::beginTagChange, this, &GraphicsScene::beginTagChange);
connect(m_document, &ScxmlDocument::endTagChange, this, &GraphicsScene::endTagChange);
}
}
void GraphicsScene::disconnectDocument()
{
if (m_document)
m_document->disconnect(this);
}
void GraphicsScene::init()
{
m_initializing = true;
disconnectDocument();
clear();
addItem(m_lineX = new SnapLine);
addItem(m_lineY = new SnapLine);
if (m_document) {
const ScxmlTag *rootTag = m_document->rootTag();
if (rootTag) {
for (int i = 0; i < rootTag->childCount(); ++i) {
ScxmlTag *child = rootTag->child(i);
ConnectableItem *newItem = SceneUtils::createItemByTagType(child->tagType());
if (newItem) {
addItem(newItem);
newItem->init(child);
}
}
const QList<QGraphicsItem*> items = this->items();
for (int i = 0; i < items.count(); ++i) {
if (items[i]->type() >= TransitionType) {
auto item = qgraphicsitem_cast<BaseItem*>(items[i]);
if (item)
item->finalizeCreation();
}
}
}
}
m_initializing = false;
warningVisibilityChanged(0, 0);
emit selectedStateCountChanged(0);
emit selectedBaseItemCountChanged(0);
}
void GraphicsScene::runLayoutToSelectedStates()
{
m_document->undoStack()->beginMacro(tr("Relayout"));
QVector<BaseItem*> selectedItems;
foreach (BaseItem *node, m_baseItems) {
if (node->isSelected()) {
int index = 0;
for (int i = 0; i < selectedItems.count(); ++i) {
if (node->depth() <= selectedItems[i]->depth()) {
index = i;
break;
}
}
selectedItems.insert(index, node);
}
}
// Layout selected items
for (int i = 0; i < selectedItems.count(); ++i)
selectedItems[i]->doLayout(selectedItems[i]->depth());
// Layout scene items if necessary
if (selectedItems.isEmpty()) {
QList<QGraphicsItem*> sceneItems;
foreach (BaseItem *item, m_baseItems) {
if (item->type() >= InitialStateType && !item->parentItem())
sceneItems << item;
}
SceneUtils::layout(sceneItems);
foreach (QGraphicsItem *item, sceneItems) {
if (item->type() >= StateType)
static_cast<StateItem*>(item)->shrink();
}
}
// Update properties
foreach (BaseItem *node, selectedItems) {
node->updateUIProperties();
}
m_document->undoStack()->endMacro();
}
void GraphicsScene::runAutomaticLayout()
{
m_autoLayoutRunning = true;
// 1. Find max depth
int maxDepth = 0;
foreach (BaseItem *node, m_baseItems) {
maxDepth = qMax(maxDepth, node->depth());
node->setBlockUpdates(true);
}
// 2. Layout every depth-level separately
for (int d = (maxDepth + 1); d--;) {
foreach (BaseItem *node, m_baseItems)
node->doLayout(d);
}
// 3. Layout scene items
QList<QGraphicsItem*> sceneItems;
foreach (BaseItem *item, m_baseItems) {
if (item->type() >= InitialStateType && !item->parentItem())
sceneItems << item;
}
SceneUtils::layout(sceneItems);
foreach (QGraphicsItem *item, sceneItems) {
if (item->type() >= StateType)
static_cast<StateItem*>(item)->shrink();
}
foreach (BaseItem *node, m_baseItems) {
node->updateUIProperties();
node->setBlockUpdates(false);
}
m_autoLayoutRunning = false;
}
void GraphicsScene::beginTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
{
switch (change) {
case ScxmlDocument::TagRemoveChild: {
if (tag)
removeItems(tag->child(value.toInt()));
break;
}
default:
break;
}
}
void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
{
Q_UNUSED(value)
QTC_ASSERT(tag, return);
switch (change) {
case ScxmlDocument::TagAttributesChanged: {
foreach (BaseItem *item, m_baseItems) {
if (item->tag() == tag)
item->updateAttributes();
}
break;
}
case ScxmlDocument::TagEditorInfoChanged: {
foreach (BaseItem *item, m_baseItems) {
if (item->tag() == tag)
item->updateEditorInfo();
}
break;
}
case ScxmlDocument::TagCurrentChanged: {
foreach (BaseItem *item, m_baseItems) {
if (!item->isSelected() && item->tag() == tag)
item->setSelected(true);
}
checkPaste();
break;
}
case ScxmlDocument::TagChangeParent: {
auto childItem = qobject_cast<ConnectableItem*>(findItem(tag));
if (childItem) {
BaseItem *newParentItem = findItem(tag->parentTag());
BaseItem *oldParentItem = childItem ? childItem->parentBaseItem() : nullptr;
QPointF sPos = childItem->scenePos();
if (oldParentItem) {
childItem->setParentItem(nullptr);
childItem->setPos(sPos);
}
if (newParentItem)
childItem->setPos(newParentItem->mapFromScene(childItem->sceneBoundingRect().center()) - childItem->boundingRect().center());
childItem->setParentItem(newParentItem);
childItem->updateUIProperties();
childItem->updateTransitions(true);
childItem->updateTransitionAttributes(true);
childItem->checkWarnings();
childItem->checkInitial();
if (newParentItem) {
newParentItem->checkInitial();
newParentItem->updateAttributes();
newParentItem->checkWarnings();
newParentItem->checkOverlapping();
newParentItem->updateUIProperties();
}
if (oldParentItem)
oldParentItem->checkInitial();
if (oldParentItem == nullptr || newParentItem == nullptr)
checkInitialState();
}
break;
}
case ScxmlDocument::TagAddTags: {
// Finalize transitions
QVector<ScxmlTag*> childTransitionTags;
if (tag->tagName(false) == "transition")
childTransitionTags << tag;
TagUtils::findAllTransitionChildren(tag, childTransitionTags);
for (int i = 0; i < childTransitionTags.count(); ++i) {
BaseItem *item = findItem(childTransitionTags[i]);
if (item)
item->finalizeCreation();
}
}
// FIXME: intended fallthrough?
case ScxmlDocument::TagAddChild: {
ScxmlTag *childTag = tag->child(value.toInt());
if (childTag) {
// Check that there is no any item with this tag
BaseItem *childItem = findItem(childTag);
BaseItem *parentItem = findItem(tag);
if (!childItem) {
if (childTag->tagType() == Transition || childTag->tagType() == InitialTransition) {
auto transition = new TransitionItem;
addItem(transition);
transition->setStartItem(qgraphicsitem_cast<ConnectableItem*>(parentItem));
transition->init(childTag, 0, false, false);
transition->updateAttributes();
} else {
childItem = SceneUtils::createItemByTagType(childTag->tagType(), QPointF());
if (childItem) {
childItem->init(childTag, parentItem, false);
if (!parentItem)
addItem(childItem);
childItem->finalizeCreation();
childItem->updateUIProperties();
}
}
}
if (parentItem) {
parentItem->updateAttributes();
parentItem->updateUIProperties();
parentItem->checkInitial();
} else
checkInitialState();
}
break;
}
case ScxmlDocument::TagRemoveChild: {
BaseItem *parentItem = findItem(tag);
if (parentItem) {
parentItem->updateAttributes();
parentItem->checkInitial();
} else {
checkInitialState();
}
break;
}
case ScxmlDocument::TagChangeOrder: {
BaseItem *parentItem = findItem(tag->parentTag());
if (parentItem)
parentItem->updateAttributes();
else
checkInitialState();
}
default:
break;
}
}
void GraphicsScene::setTopMostScene(bool topmost)
{
m_topMostScene = topmost;
}
bool GraphicsScene::topMostScene() const
{
return m_topMostScene;
}
void GraphicsScene::setActionHandler(ActionHandler *mgr)
{
m_actionHandler = mgr;
}
void GraphicsScene::setWarningModel(ScxmlEditor::OutputPane::WarningModel *model)
{
m_warningModel = model;
}
void GraphicsScene::setUiFactory(ScxmlUiFactory *uifactory)
{
m_uiFactory = uifactory;
}
ActionHandler *GraphicsScene::actionHandler() const
{
return m_actionHandler;
}
ScxmlEditor::OutputPane::WarningModel *GraphicsScene::warningModel() const
{
return m_warningModel;
}
ScxmlUiFactory *GraphicsScene::uiFactory() const
{
return m_uiFactory;
}
void GraphicsScene::addConnectableItem(ItemType type, const QPointF &pos, BaseItem *parentItem)
{
m_document->undoStack()->beginMacro(tr("Add new state"));
ConnectableItem *newItem = SceneUtils::createItem(type, pos);
if (newItem) {
ScxmlTag *newTag = SceneUtils::createTag(type, m_document);
ScxmlTag *parentTag = parentItem ? parentItem->tag() : m_document->rootTag();
newItem->setTag(newTag);
newItem->setParentItem(parentItem);
if (!parentItem)
addItem(newItem);
newItem->updateAttributes();
newItem->updateEditorInfo();
newItem->updateUIProperties();
if (parentItem)
parentItem->updateUIProperties();
m_document->addTag(parentTag, newTag);
unselectAll();
newItem->setSelected(true);
}
m_document->undoStack()->endMacro();
}
void GraphicsScene::keyPressEvent(QKeyEvent *event)
{
QGraphicsItem *focusItem = this->focusItem();
if (focusItem == nullptr || focusItem->type() != TextType) {
if (event->key() == Qt::Key_Delete)
removeSelectedItems();
}
QGraphicsScene::keyPressEvent(event);
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem *it = itemAt(event->scenePos(), QTransform());
if (it == nullptr || it->type() == LayoutType) {
if (event->button() == Qt::LeftButton) {
QGraphicsScene::mousePressEvent(event);
m_document->setCurrentTag(m_document->rootTag());
return;
} else if (m_actionHandler && event->button() == Qt::RightButton) {
event->accept();
QMenu menu;
menu.addAction(m_actionHandler->action(ActionCopy));
menu.addAction(m_actionHandler->action(ActionPaste));
menu.addAction(m_actionHandler->action(ActionScreenshot));
menu.addAction(m_actionHandler->action(ActionExportToImage));
menu.addSeparator();
menu.addAction(m_actionHandler->action(ActionZoomIn));
menu.addAction(m_actionHandler->action(ActionZoomOut));
menu.addAction(m_actionHandler->action(ActionFitToView));
if (m_uiFactory) {
auto actionProvider = static_cast<ActionProvider*>(m_uiFactory->object(Constants::C_UI_FACTORY_OBJECT_ACTIONPROVIDER));
if (actionProvider) {
menu.addSeparator();
actionProvider->initStateMenu(m_document->rootTag(), &menu);
}
}
menu.exec(event->screenPos());
return;
}
}
QGraphicsScene::mousePressEvent(event);
}
BaseItem *GraphicsScene::findItem(const ScxmlTag *tag) const
{
if (!tag)
return nullptr;
foreach (BaseItem *it, m_baseItems) {
if (it->tag() == tag)
return it;
}
return nullptr;
}
void GraphicsScene::removeItems(const ScxmlTag *tag)
{
if (tag) {
// Find right items
QVector<BaseItem*> items;
foreach (BaseItem *it, m_baseItems) {
if (it->tag() == tag)
items << it;
}
// Then delete them
for (int i = items.count(); i--;) {
items[i]->setTag(0);
delete items[i];
}
}
}
QPair<bool, bool> GraphicsScene::checkSnapToItem(BaseItem *item, const QPointF &p, QPointF &pp)
{
if (m_selectedStateCount > 1)
return QPair<bool, bool>(false, false);
QGraphicsItem *parentItem = item->parentItem();
qreal diffX = 8;
qreal diffXdY = 2000;
qreal diffY = 8;
qreal diffYdX = 2000;
foreach (BaseItem *it, m_baseItems) {
if (!it->isSelected() && it != item && it->parentItem() == parentItem && it->type() >= InitialStateType) {
QPointF c = it->sceneCenter();
qreal dX = qAbs(c.x() - p.x());
qreal dY = qAbs(c.y() - p.y());
if (dX < 7 && dY < diffXdY) {
pp.setX(c.x());
diffX = dX;
diffXdY = dY;
m_lineY->show(c.x(), c.y(), c.x(), p.y());
}
if (dY < 7 && dX < diffYdX) {
pp.setY(c.y());
diffY = dY;
diffYdX = dX;
m_lineX->show(c.x(), c.y(), p.x(), c.y());
}
}
}
if (qFuzzyCompare(diffX, 8))
m_lineY->hideLine();
if (qFuzzyCompare(diffY, 8))
m_lineX->hideLine();
return QPair<bool, bool>(diffX < 8, diffY < 8);
}
void GraphicsScene::selectionChanged(bool para)
{
Q_UNUSED(para)
int count = 0;
int baseCount = 0;
int stateTypeCount = 0;
foreach (BaseItem *item, m_baseItems) {
if (item->isSelected()) {
if (item->type() >= TransitionType)
baseCount++;
if (item->type() >= InitialStateType)
count++;
if (item->type() >= StateType)
stateTypeCount++;
}
}
m_selectedStateTypeCount = stateTypeCount;
if (count != m_selectedStateCount) {
m_selectedStateCount = count;
emit selectedStateCountChanged(m_selectedStateCount);
}
if (baseCount != m_selectedBaseItemCount) {
m_selectedBaseItemCount = baseCount;
emit selectedBaseItemCountChanged(m_selectedBaseItemCount);
}
}
void GraphicsScene::addWarningItem(WarningItem *item)
{
if (!m_allWarnings.contains(item)) {
m_allWarnings << item;
if (!m_autoLayoutRunning && !m_initializing)
QMetaObject::invokeMethod(this, "warningVisibilityChanged", Qt::QueuedConnection, Q_ARG(int, 0));
}
}
void GraphicsScene::removeWarningItem(WarningItem *item)
{
m_allWarnings.removeAll(item);
if (!m_autoLayoutRunning && !m_initializing)
QMetaObject::invokeMethod(this, "warningVisibilityChanged", Qt::QueuedConnection, Q_ARG(int, 0));
}
void GraphicsScene::warningVisibilityChanged(int type, WarningItem *item)
{
if (!m_autoLayoutRunning && !m_initializing) {
foreach (WarningItem *it, m_allWarnings) {
if (it != item && (type == 0 || it->type() == type))
it->check();
}
}
}
ScxmlTag *GraphicsScene::tagByWarning(const ScxmlEditor::OutputPane::Warning *w) const
{
ScxmlTag *tag = nullptr;
foreach (WarningItem *it, m_allWarnings) {
if (it->warning() == w) {
tag = it->tag();
break;
}
}
return tag;
}
void GraphicsScene::highlightWarningItem(const ScxmlEditor::OutputPane::Warning *w)
{
ScxmlTag *tag = tagByWarning(w);
if (tag)
highlightItems(QVector<ScxmlTag*>() << tag);
else
unhighlightAll();
}
void GraphicsScene::selectWarningItem(const ScxmlEditor::OutputPane::Warning *w)
{
ScxmlTag *tag = tagByWarning(w);
if (tag) {
unselectAll();
m_document->setCurrentTag(tag);
}
}
QList<QGraphicsItem*> GraphicsScene::sceneItems(Qt::SortOrder order) const
{
QList<QGraphicsItem*> children;
QList<QGraphicsItem*> items = this->items(order);
for (int i = 0; i < items.count(); ++i) {
if (items[i]->parentItem() == nullptr && items[i]->type() >= InitialStateType)
children << items[i];
}
return children;
}
void GraphicsScene::addChild(BaseItem *item)
{
if (!m_baseItems.contains(item)) {
connect(item, &BaseItem::selectedStateChanged, this, &GraphicsScene::selectionChanged);
connect(item, &BaseItem::openToDifferentView, this, [=](BaseItem *item){
openStateView(item);
}, Qt::QueuedConnection);
m_baseItems << item;
}
}
void GraphicsScene::removeChild(BaseItem *item)
{
if (item)
disconnect(item, 0, this, 0);
m_baseItems.removeAll(item);
selectionChanged(false);
}
void GraphicsScene::checkItemsVisibility(double scaleFactor)
{
foreach (BaseItem *item, m_baseItems) {
item->checkVisibility(scaleFactor);
}
}
void GraphicsScene::checkInitialState()
{
if (m_document) {
QList<QGraphicsItem*> sceneItems;
foreach (BaseItem *item, m_baseItems) {
if (item->type() >= InitialStateType && !item->parentItem())
sceneItems << item;
}
if (m_uiFactory) {
auto utilsProvider = static_cast<UtilsProvider*>(m_uiFactory->object("utilsProvider"));
if (utilsProvider)
utilsProvider->checkInitialState(sceneItems, m_document->rootTag());
}
}
}
void GraphicsScene::clearAllTags()
{
foreach (BaseItem *it, m_baseItems) {
it->setTag(0);
}
}
void GraphicsScene::setBlockUpdates(bool block)
{
foreach (BaseItem *it, m_baseItems) {
it->setBlockUpdates(block);
}
}

View File

@@ -0,0 +1,152 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
#include "scxmldocument.h"
#include <QGraphicsLineItem>
#include <QGraphicsScene>
QT_FORWARD_DECLARE_CLASS(QAction)
QT_FORWARD_DECLARE_CLASS(QKeyEvent)
namespace ScxmlEditor {
namespace OutputPane {
class Warning;
class WarningModel;
} // namespace OutputPane
namespace PluginInterface {
class ActionHandler;
class ScxmlUiFactory;
class SnapLine;
class WarningItem;
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = nullptr);
~GraphicsScene() override;
QPair<bool, bool> checkSnapToItem(BaseItem *item, const QPointF &p, QPointF &pp);
void checkItemsVisibility(double scaleFactor);
void checkInitialState();
void clearAllTags();
void setBlockUpdates(bool block);
QList<QGraphicsItem*> sceneItems(Qt::SortOrder order) const;
ScxmlTag *tagByWarning(const OutputPane::Warning *w) const;
void highlightWarningItem(const OutputPane::Warning *w);
void selectWarningItem(const OutputPane::Warning *w);
QRectF selectedBoundingRect() const;
BaseItem *findItem(const ScxmlTag *tag) const;
bool topMostScene() const;
void setActionHandler(ActionHandler *mgr);
void setWarningModel(OutputPane::WarningModel *model);
void setUiFactory(ScxmlUiFactory *uifactory);
void setEditorInfo(const QString &key, const QString &value);
void setDocument(ScxmlDocument *document);
void unselectAll();
void unhighlightAll();
void highlightItems(const QVector<ScxmlTag*> &lstIds);
void addConnectableItem(ItemType type, const QPointF &pos, BaseItem *parentItem);
void runAutomaticLayout();
void runLayoutToSelectedStates();
void alignStates(int alignType);
void adjustStates(int adjustType);
void copy();
void cut();
void removeSelectedItems();
void checkPaste();
void paste(const QPointF &targetPos);
void setTopMostScene(bool topmost);
ActionHandler *actionHandler() const;
OutputPane::WarningModel *warningModel() const;
ScxmlUiFactory *uiFactory() const;
signals:
void openStateView(BaseItem *item);
protected:
void keyPressEvent(QKeyEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
signals:
void selectedStateCountChanged(int count);
void selectedBaseItemCountChanged(int count);
void pasteAvailable(bool para);
private slots:
void warningVisibilityChanged(int type, WarningItem *item = nullptr);
private:
void beginTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
void endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
void selectionChanged(bool para);
friend class BaseItem;
friend class WarningItem;
void init();
qreal selectedMaxWidth() const;
qreal selectedMaxHeight() const;
void removeItems(const ScxmlTag *tag);
void addChild(BaseItem *item);
void removeChild(BaseItem *item);
void addWarningItem(WarningItem *item);
void removeWarningItem(WarningItem *item);
void connectDocument();
void disconnectDocument();
QPointer<ActionHandler> m_actionHandler;
QPointer<OutputPane::WarningModel> m_warningModel;
QPointer<ScxmlUiFactory> m_uiFactory;
QPointer<ScxmlDocument> m_document;
QVector<BaseItem*> m_baseItems;
QVector<WarningItem*> m_allWarnings;
int m_pasteCounter = 0;
QPointer<BaseItem> m_lastPasteTargetItem;
SnapLine *m_lineX = nullptr;
SnapLine *m_lineY = nullptr;
int m_selectedStateCount = 0;
int m_selectedBaseItemCount = 0;
int m_selectedStateTypeCount = 0;
bool m_autoLayoutRunning = false;
bool m_initializing = false;
bool m_topMostScene = false;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,96 @@
/****************************************************************************
**
** 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 "highlightitem.h"
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
HighlightItem::HighlightItem(BaseItem *baseItem)
: QGraphicsObject(nullptr)
, m_baseItem(baseItem)
{
m_pen = QPen(QColor(0xff, 0x00, 0x60));
m_pen.setWidth(2);
m_pen.setStyle(Qt::DashLine);
m_pen.setCosmetic(true);
setZValue(1000);
}
void HighlightItem::advance(int phase)
{
Q_UNUSED(phase)
prepareGeometryChange();
if (m_baseItem) {
setPos(m_baseItem->scenePos());
m_boundingRect = m_baseItem->boundingRect();
}
update();
}
QRectF HighlightItem::boundingRect() const
{
return m_boundingRect;
}
void HighlightItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
if (m_baseItem) {
painter->save();
painter->setRenderHints(QPainter::Antialiasing, true);
QRectF br = m_baseItem->polygonShape().boundingRect();
switch (m_baseItem->type()) {
case StateType:
case ParallelType: {
painter->setOpacity(1.0);
painter->setPen(m_pen);
painter->setBrush(Qt::NoBrush);
painter->drawRoundedRect(br, 10, 10);
break;
}
case InitialStateType:
case HistoryType:
case FinalStateType: {
painter->setOpacity(1.0);
painter->setPen(m_pen);
painter->setBrush(Qt::NoBrush);
painter->drawEllipse(br);
break;
}
default:
break;
}
painter->restore();
}
}

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
#include <QBrush>
#include <QGraphicsObject>
#include <QPen>
#include <QPointer>
namespace ScxmlEditor {
namespace PluginInterface {
class HighlightItem : public QGraphicsObject
{
public:
HighlightItem(BaseItem *parent = nullptr);
int type() const override
{
return HighlightType;
}
QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void advance(int phase) override;
private:
QPointer<BaseItem> m_baseItem;
QRectF m_boundingRect;
QBrush m_brush;
QPen m_pen;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,86 @@
/****************************************************************************
**
** 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 "historyitem.h"
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
HistoryItem::HistoryItem(const QPointF &pos, BaseItem *parent)
: ConnectableItem(pos, parent)
{
setItemBoundingRect(QRectF(-20, -20, 40, 40));
setMinimumHeight(40);
setMinimumWidth(40);
m_pen.setColor(qRgb(0x12, 0x12, 0x12));
m_pen.setWidth(2);
}
void HistoryItem::updatePolygon()
{
QRectF r = boundingRect();
m_size = qMin(r.width() * 0.45, r.height() * 0.45);
QPointF center = r.center();
m_polygon.clear();
m_polygon << (center + QPointF(-m_size, -m_size))
<< (center + QPointF(m_size, -m_size))
<< (center + QPointF(m_size, m_size))
<< (center + QPointF(-m_size, m_size))
<< (center + QPointF(-m_size, -m_size));
}
void HistoryItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
ConnectableItem::paint(painter, option, widget);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setOpacity(getOpacity());
painter->setBrush(QColor(0xff, 0xff, 0xff));
m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
painter->setPen(m_pen);
painter->drawEllipse(boundingRect().center(), m_size, m_size);
painter->drawText(boundingRect(), Qt::AlignCenter, QLatin1String(tagValue("type") == "deep" ? "H*" : "H"));
painter->restore();
}
bool HistoryItem::canStartTransition(ItemType type) const
{
if (outputTransitionCount() > 0)
return false;
switch (type) {
case UnknownType:
case StateType:
case ParallelType:
return true;
default:
return false;
}
}

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "connectableitem.h"
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The HistoryItem class represents History-state of the SCXML-standard. It is an extended class from the ConnectableItem.
*/
class HistoryItem : public ConnectableItem
{
Q_OBJECT
public:
HistoryItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
int type() const override
{
return HistoryType;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
protected:
void updatePolygon() override;
bool canStartTransition(ItemType type) const override;
private:
qreal m_size;
QPen m_pen;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,86 @@
/****************************************************************************
**
** 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 "idwarningitem.h"
#include <QGraphicsScene>
using namespace ScxmlEditor::PluginInterface;
IdWarningItem::IdWarningItem(QGraphicsItem *parent)
: WarningItem(parent)
{
setSeverity(OutputPane::Warning::ErrorType);
setTypeName(tr("State"));
setDescription(tr("Each State has to be unique ID."));
setReason(tr("Missing ID"));
setX(-boundingRect().width());
}
void IdWarningItem::check()
{
setId(m_id);
}
void IdWarningItem::setId(const QString &text)
{
QString oldId = m_id;
m_id = text;
// Check old id
if (!oldId.isEmpty())
checkDuplicates(oldId);
// Check new id
if (m_id.isEmpty()) {
setReason(tr("Missing ID"));
setWarningActive(true);
} else
checkDuplicates(m_id);
}
void IdWarningItem::checkDuplicates(const QString &id)
{
if (scene()) {
QVector<IdWarningItem*> foundItems;
QList<QGraphicsItem*> items = scene()->items();
for (int i = 0; i < items.count(); ++i) {
if (items[i]->type() == IdWarningType) {
auto item = qgraphicsitem_cast<IdWarningItem*>(items[i]);
if (item && item->id() == id)
foundItems << item;
}
}
if (foundItems.count() == 1) {
foundItems[0]->setWarningActive(false);
} else {
for (int i = 0; i < foundItems.count(); ++i) {
foundItems[i]->setReason(tr("Duplicate ID (%1)").arg(id));
foundItems[i]->setWarningActive(true);
}
}
}
}

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "warningitem.h"
namespace ScxmlEditor {
namespace PluginInterface {
class IdWarningItem : public WarningItem
{
Q_OBJECT
public:
IdWarningItem(QGraphicsItem *parent = nullptr);
int type() const override
{
return IdWarningType;
}
QString id() const
{
return m_id;
}
void check() override;
void setId(const QString &text);
private:
void checkDuplicates(const QString &id);
QString m_id;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,33 @@
/****************************************************************************
**
** 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 "imageprovider.h"
using namespace ScxmlEditor::PluginInterface;
ImageProvider::ImageProvider(QObject *parent)
: QObject(parent)
{
}

View File

@@ -0,0 +1,47 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QObject>
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlTag;
class ImageProvider : public QObject
{
Q_OBJECT
public:
explicit ImageProvider(QObject *parent = nullptr);
virtual QImage *backgroundImage(const ScxmlTag *tag) const = 0;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,134 @@
/****************************************************************************
**
** 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 "initialstateitem.h"
#include "initialwarningitem.h"
#include "graphicsitemprovider.h"
#include "scxmleditorconstants.h"
#include "scxmluifactory.h"
#include <QByteArray>
#include <QDataStream>
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
InitialStateItem::InitialStateItem(const QPointF &pos, BaseItem *parent)
: ConnectableItem(pos, parent)
{
setItemBoundingRect(QRectF(-20, -20, 40, 40));
setMinimumHeight(40);
setMinimumWidth(40);
m_pen.setColor(qRgb(0x12, 0x12, 0x12));
m_pen.setWidth(2);
checkWarningItems();
}
void InitialStateItem::checkWarningItems()
{
ScxmlUiFactory *uifactory = uiFactory();
if (uifactory) {
auto provider = static_cast<GraphicsItemProvider*>(uifactory->object("graphicsItemProvider"));
if (provider) {
if (!m_warningItem)
m_warningItem = static_cast<InitialWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_INITIAL, this));
}
}
}
QVariant InitialStateItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
QVariant retValue = ConnectableItem::itemChange(change, value);
switch (change) {
case QGraphicsItem::ItemSceneHasChanged:
checkWarningItems();
break;
default:
break;
}
return retValue;
}
void InitialStateItem::updatePolygon()
{
QRectF r = boundingRect();
m_size = qMin(r.width() * 0.45, r.height() * 0.45);
QPointF center = r.center();
m_polygon.clear();
m_polygon << (center + QPointF(-m_size, -m_size))
<< (center + QPointF(m_size, -m_size))
<< (center + QPointF(m_size, m_size))
<< (center + QPointF(-m_size, m_size))
<< (center + QPointF(-m_size, -m_size));
if (m_warningItem)
m_warningItem->updatePos();
}
void InitialStateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
ConnectableItem::paint(painter, option, widget);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setOpacity(getOpacity());
m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
painter->setPen(m_pen);
painter->setBrush(QColor(0x4d, 0x4d, 0x4d));
painter->drawEllipse(boundingRect().center(), m_size, m_size);
painter->restore();
}
InitialWarningItem *InitialStateItem::warningItem() const
{
return m_warningItem;
}
bool InitialStateItem::canStartTransition(ItemType type) const
{
if (transitionCount() > 0)
return false;
switch (type) {
case UnknownType:
case StateType:
case ParallelType:
return true;
default:
return false;
}
}
void InitialStateItem::checkWarnings()
{
if (m_warningItem)
m_warningItem->check();
}

View File

@@ -0,0 +1,67 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "connectableitem.h"
namespace ScxmlEditor {
namespace PluginInterface {
class InitialWarningItem;
/**
* @brief The InitialStateItem class represents Initial-state of the SCXML-standard. It is a extended class from the ConnectableItem.
*/
class InitialStateItem : public ConnectableItem
{
public:
explicit InitialStateItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
int type() const override
{
return InitialStateType;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
InitialWarningItem *warningItem() const;
void checkWarnings() override;
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
void updatePolygon() override;
bool canStartTransition(ItemType type) const override;
private:
void checkWarningItems();
InitialWarningItem *m_warningItem = nullptr;
qreal m_size = 1.0;
QPen m_pen;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** 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 "initialwarningitem.h"
#include "initialstateitem.h"
#include "sceneutils.h"
using namespace ScxmlEditor::PluginInterface;
InitialWarningItem::InitialWarningItem(InitialStateItem *parent)
: WarningItem(parent)
, m_parentItem(parent)
{
setSeverity(OutputPane::Warning::ErrorType);
setTypeName(tr("Initial"));
setDescription(tr("It is possible to have max 1 initial-state in the same level."));
setReason(tr("Too many initial states in the same level"));
}
void InitialWarningItem::updatePos()
{
setPos(m_parentItem->boundingRect().topLeft());
}
void InitialWarningItem::check()
{
if (m_parentItem)
setWarningActive(SceneUtils::hasSiblingStates(m_parentItem));
}

View File

@@ -0,0 +1,55 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "warningitem.h"
namespace ScxmlEditor {
namespace PluginInterface {
class InitialStateItem;
class InitialWarningItem : public WarningItem
{
public:
InitialWarningItem(InitialStateItem *parent = nullptr);
int type() const override
{
return InitialWarningType;
}
void check() override;
void updatePos();
private:
InitialStateItem *m_parentItem;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "mytypes.h"
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlUiFactory;
class ScxmlDocument;
/**
* @brief The ISCEditor interface for the all SCEditor plugins
*
* Every SCEditor plugin must implement this interface. SCEditor application will load all plugins which have listed
* in the environment variable QT_SCEDITOR_PLUGINS and which implements this interface. The function init() will be called
* immediately after plugin has successfully loaded. See the @ScxmlUiFactory for more information how to register editor etc for the UI.
* The function refresh() will be called when it is time to refresh data. Plugin itself can decide if this is necessary or not.
* Normally this function will be called when application get focus.
*/
class ISCEditor
{
public:
/**
* @brief init - pure virtual function to init and forward the pointer of the ScxmlUiFactory to the plugin.
* @param factory - pointer to the ScxmlUiFactory
*/
virtual void init(ScxmlUiFactory *factory) = 0;
/**
* @brief documentChanged - when document will changed this function will be called
* @param type - change reason
* @param doc - pointer to ScxmlDocument
*/
virtual void documentChanged(DocumentChangeType type, ScxmlDocument *doc) = 0;
/**
* @brief refresh - tell the plugin that it is time to update all data if necessary
*/
virtual void refresh() = 0;
/**
* @brief detach - here should be unregister all objects
*/
virtual void detach() = 0;
};
} // namespace PluginInterface
} // namespace ScxmlEditor
QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(ScxmlEditor::PluginInterface::ISCEditor, "StateChartEditor.ISCEditor/1.0")
QT_END_NAMESPACE

View File

@@ -0,0 +1,53 @@
/****************************************************************************
**
** 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 "layoutitem.h"
using namespace ScxmlEditor::PluginInterface;
LayoutItem::LayoutItem(const QRectF &br, QGraphicsItem *parent)
: QGraphicsObject(parent)
, m_boundingRect(br)
{
setZValue(-100);
}
QRectF LayoutItem::boundingRect() const
{
return m_boundingRect;
}
void LayoutItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(painter)
Q_UNUSED(option)
Q_UNUSED(widget)
}
void LayoutItem::setBoundingRect(const QRectF &r)
{
prepareGeometryChange();
m_boundingRect = r;
}

View File

@@ -0,0 +1,55 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "mytypes.h"
#include <QGraphicsObject>
namespace ScxmlEditor {
namespace PluginInterface {
class LayoutItem : public QGraphicsObject
{
public:
LayoutItem(const QRectF &br, QGraphicsItem *parent = nullptr);
int type() const override
{
return LayoutType;
}
QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void setBoundingRect(const QRectF &r);
private:
QRectF m_boundingRect;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,115 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QGraphicsItem>
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The type of the graphics-items
*/
enum ItemType {
UnknownType = QGraphicsItem::UserType + 1,
// Warning items ->
HighlightType,
LayoutType,
IdWarningType,
StateWarningType,
TransitionWarningType,
InitialWarningType,
// Helper items ->
CornerGrabberType,
QuickTransitionType,
TextType,
TagTextType,
TagWrapperType,
TransitionType,
// Connectable-items ->
InitialStateType,
FinalStateType,
HistoryType,
StateType,
ParallelType
};
enum ActionType {
ActionZoomIn = 0,
ActionZoomOut,
ActionFitToView,
ActionPan,
ActionMagnifier,
ActionNavigator,
ActionCopy,
ActionCut,
ActionPaste,
ActionScreenshot,
ActionExportToImage,
ActionFullNamespace,
ActionAlignLeft,
ActionAlignRight,
ActionAlignTop,
ActionAlignBottom,
ActionAlignHorizontal,
ActionAlignVertical,
ActionAdjustWidth,
ActionAdjustHeight,
ActionAdjustSize,
ActionStatistics,
ActionLast,
ActionColorTheme
};
enum ToolButtonType {
ToolButtonStateColor,
ToolButtonFontColor,
ToolButtonAlignment,
ToolButtonAdjustment,
ToolButtonLast,
ToolButtonColorTheme
};
enum AttributeRole {
DataTypeRole = Qt::UserRole + 1,
DataRole
};
enum DocumentChangeType {
BeginSave = 0,
AfterSave,
AfterLoad,
NewDocument
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,117 @@
/****************************************************************************
**
** 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 "parallelitem.h"
#include "scxmldocument.h"
#include "scxmltagutils.h"
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
ParallelItem::ParallelItem(const QPointF &pos, BaseItem *parent)
: StateItem(pos, parent)
{
m_pixmap = QPixmap(":/scxmleditor/images/parallel_icon.png");
updatePolygon();
}
void ParallelItem::updatePolygon()
{
StateItem::updatePolygon();
int cap = m_titleRect.height() * 0.2;
m_pixmapRect = m_titleRect.adjusted(m_titleRect.width() - m_titleRect.height(), cap, -cap, -cap).toRect();
}
void ParallelItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
StateItem::paint(painter, option, widget);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setOpacity(getOpacity());
painter->drawPixmap(m_pixmapRect, m_pixmap);
painter->restore();
}
void ParallelItem::doLayout(int d)
{
if (depth() != d)
return;
// 1. Find children items
QVector<StateItem*> children;
foreach (QGraphicsItem *it, childItems()) {
if (it->type() >= StateType) {
auto itt = qgraphicsitem_cast<StateItem*>(it);
if (itt)
children << itt;
}
}
// 2. Adjust sizes
foreach (StateItem *itt, children) {
itt->shrink();
}
qreal maxw = 0;
foreach (StateItem *itt, children) {
QRectF rr = itt->boundingRect();
maxw = qMax(rr.width(), maxw);
}
foreach (StateItem *itt, children) {
QRectF rr = itt->boundingRect();
if (!qFuzzyCompare(rr.width(), maxw))
rr.setWidth(maxw);
itt->setItemBoundingRect(rr);
}
// 3. Relocate children-states
// a) sort list
QVector<StateItem*> sortedList;
while (children.count() > 0) {
qreal minTop = children.first()->boundingRect().top();
int minTopIndex = 0;
for (int i = 1; i < children.count(); ++i) {
qreal top = children[i]->boundingRect().top();
if (top < minTop) {
minTop = top;
minTopIndex = i;
}
}
sortedList << children.takeAt(minTopIndex);
}
// b) relocate items
for (int i = 1; i < sortedList.count(); ++i) {
QRectF br1 = sortedList[i - 1]->sceneBoundingRect();
QRectF br2 = sortedList[i]->sceneBoundingRect();
sortedList[i]->moveStateBy(br1.left() - br2.left(), br1.bottom() + 10 - br2.top());
}
// 4. Shrink parallel-state
shrink();
}

View File

@@ -0,0 +1,59 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "stateitem.h"
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The ParalllelItem class represents Parallel-state of the SCXML-standard. It is a extended class from the StateItem.
*/
class ParallelItem : public StateItem
{
public:
explicit ParallelItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
int type() const override
{
return ParallelType;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void doLayout(int d) override;
protected:
void updatePolygon() override;
private:
QPixmap m_pixmap;
QRect m_pixmapRect;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,93 @@
INCLUDEPATH += $$PWD
HEADERS += \
$$PWD/actionhandler.h \
$$PWD/actionprovider.h \
$$PWD/attributeitemdelegate.h \
$$PWD/attributeitemmodel.h \
$$PWD/baseitem.h \
$$PWD/connectableitem.h \
$$PWD/cornergrabberitem.h \
$$PWD/finalstateitem.h \
$$PWD/genericscxmlplugin.h \
$$PWD/graphicsitemprovider.h \
$$PWD/graphicsscene.h \
$$PWD/highlightitem.h \
$$PWD/historyitem.h \
$$PWD/idwarningitem.h \
$$PWD/imageprovider.h \
$$PWD/initialstateitem.h \
$$PWD/initialwarningitem.h \
$$PWD/isceditor.h \
$$PWD/layoutitem.h \
$$PWD/mytypes.h \
$$PWD/parallelitem.h \
$$PWD/quicktransitionitem.h \
$$PWD/scattributeitemdelegate.h \
$$PWD/scattributeitemmodel.h \
$$PWD/sceneutils.h \
$$PWD/scgraphicsitemprovider.h \
$$PWD/scshapeprovider.h \
$$PWD/scutilsprovider.h \
$$PWD/scxmldocument.h \
$$PWD/scxmlnamespace.h \
$$PWD/scxmltag.h \
$$PWD/scxmltagutils.h \
$$PWD/scxmltypes.h \
$$PWD/scxmluifactory.h \
$$PWD/serializer.h \
$$PWD/shapeprovider.h \
$$PWD/snapline.h \
$$PWD/stateitem.h \
$$PWD/statewarningitem.h \
$$PWD/tagtextitem.h \
$$PWD/textitem.h \
$$PWD/transitionitem.h \
$$PWD/transitionwarningitem.h \
$$PWD/undocommands.h \
$$PWD/utilsprovider.h \
$$PWD/warningitem.h \
$$PWD/warningprovider.h
SOURCES += \
$$PWD/actionhandler.cpp \
$$PWD/attributeitemdelegate.cpp \
$$PWD/attributeitemmodel.cpp \
$$PWD/baseitem.cpp \
$$PWD/connectableitem.cpp \
$$PWD/cornergrabberitem.cpp \
$$PWD/finalstateitem.cpp \
$$PWD/genericscxmlplugin.cpp \
$$PWD/graphicsscene.cpp \
$$PWD/highlightitem.cpp \
$$PWD/historyitem.cpp \
$$PWD/idwarningitem.cpp \
$$PWD/imageprovider.cpp \
$$PWD/initialstateitem.cpp \
$$PWD/initialwarningitem.cpp \
$$PWD/layoutitem.cpp \
$$PWD/parallelitem.cpp \
$$PWD/quicktransitionitem.cpp \
$$PWD/scattributeitemdelegate.cpp \
$$PWD/scattributeitemmodel.cpp \
$$PWD/sceneutils.cpp \
$$PWD/scgraphicsitemprovider.cpp \
$$PWD/scshapeprovider.cpp \
$$PWD/scutilsprovider.cpp \
$$PWD/scxmldocument.cpp \
$$PWD/scxmlnamespace.cpp \
$$PWD/scxmltag.cpp \
$$PWD/scxmltagutils.cpp \
$$PWD/scxmluifactory.cpp \
$$PWD/serializer.cpp \
$$PWD/shapeprovider.cpp \
$$PWD/snapline.cpp \
$$PWD/stateitem.cpp \
$$PWD/statewarningitem.cpp \
$$PWD/tagtextitem.cpp \
$$PWD/textitem.cpp \
$$PWD/transitionitem.cpp \
$$PWD/transitionwarningitem.cpp \
$$PWD/undocommands.cpp \
$$PWD/utilsprovider.cpp \
$$PWD/warningitem.cpp

View File

@@ -0,0 +1,133 @@
/****************************************************************************
**
** 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 "quicktransitionitem.h"
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
const qreal CAPX = 4;
const qreal CAPY = 3;
QuickTransitionItem::QuickTransitionItem(int index, ItemType connectionType, QGraphicsItem *parent)
: QGraphicsObject(parent)
, m_connectionType(connectionType)
{
setParentItem(parent);
installSceneEventFilter(parent);
setZValue(501);
m_rect = QRectF(index * 25, -30, 20, 20);
m_drawingRect = m_rect.adjusted(4, 4, -4, -4);
m_stateRect = m_rect.adjusted(3, 4, -3, -4);
m_brush.setStyle(Qt::SolidPattern);
m_brush.setColor(QColor(0xe8, 0xe8, 0xe8));
m_pen.setColor(QColor(0x12, 0x12, 0x12));
m_pen.setCapStyle(Qt::RoundCap);
setAcceptHoverEvents(true);
}
void QuickTransitionItem::setConnectionType(ItemType connectionType)
{
m_connectionType = connectionType;
}
void QuickTransitionItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
{
Q_UNUSED(e)
m_brush.setColor(QColor(0xe8, 0xe8, 0xe8));
update();
}
void QuickTransitionItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
{
Q_UNUSED(e)
m_brush.setColor(QColor(0xff, 0xc4, 0xff));
update();
}
void QuickTransitionItem::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);
painter->setBrush(m_brush);
painter->drawRect(m_rect);
if (m_connectionType == UnknownType) {
QPointF endPoint = m_drawingRect.topRight();
painter->drawLine(m_drawingRect.bottomLeft(), endPoint);
painter->drawLine(endPoint, endPoint + QPointF(-5, 0));
painter->drawLine(endPoint, endPoint + QPointF(0, 5));
} else if (m_connectionType >= FinalStateType) {
switch (m_connectionType) {
case StateType:
painter->setPen(m_pen);
painter->setBrush(Qt::white);
painter->drawRoundedRect(m_stateRect, 2, 2);
break;
case ParallelType:
painter->setPen(m_pen);
painter->setBrush(Qt::white);
painter->drawRoundedRect(m_stateRect, 2, 2);
painter->setPen(m_pen);
painter->drawLine(QPointF(m_stateRect.left() + CAPX, m_stateRect.center().y()), QPointF(m_stateRect.right() - CAPX, m_stateRect.center().y()));
painter->drawLine(QPointF(m_stateRect.center().x(), m_stateRect.top() + CAPY), QPointF(m_stateRect.center().x(), m_stateRect.bottom() - CAPY));
painter->drawLine(QPointF(m_stateRect.right() - CAPX, m_stateRect.top() + CAPY), QPointF(m_stateRect.center().x(), m_stateRect.top() + CAPY));
painter->drawLine(QPointF(m_stateRect.right() - CAPX, m_stateRect.bottom() - CAPY), QPointF(m_stateRect.center().x(), m_stateRect.bottom() - CAPY));
break;
case FinalStateType:
painter->setPen(m_pen);
painter->setBrush(Qt::white);
painter->drawEllipse(m_stateRect.center(), 7, 7);
painter->setPen(Qt::NoPen);
painter->setBrush(Qt::black);
painter->drawEllipse(m_stateRect.center(), 5, 5);
break;
case HistoryType:
painter->setFont(QFont("Arial", 6));
painter->setPen(m_pen);
painter->setBrush(Qt::white);
painter->drawEllipse(m_stateRect.center(), 7, 7);
painter->drawText(m_stateRect, Qt::AlignCenter, tr("H"));
break;
default:
break;
}
}
painter->restore();
}

View File

@@ -0,0 +1,79 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
#include <QBrush>
#include <QPen>
namespace ScxmlEditor {
namespace PluginInterface {
class QuickTransitionItem : public QGraphicsObject
{
Q_OBJECT
public:
explicit QuickTransitionItem(int index = 0, ItemType connectionType = UnknownType, QGraphicsItem *parent = nullptr);
/**
* @brief type of the item
*/
int type() const override
{
return QuickTransitionType;
}
ItemType connectionType() const
{
return m_connectionType;
}
void setConnectionType(ItemType type);
QRectF boundingRect() const override
{
return m_rect;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
private:
void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
ItemType m_connectionType;
QRectF m_rect;
QBrush m_brush;
QRectF m_drawingRect;
QRectF m_stateRect;
QPen m_pen;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,107 @@
/****************************************************************************
**
** 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 "scattributeitemdelegate.h"
#include "mytypes.h"
#include <QComboBox>
#include <QLineEdit>
#include <QRegExp>
#include <QRegExpValidator>
using namespace ScxmlEditor::PluginInterface;
SCAttributeItemDelegate::SCAttributeItemDelegate(QObject *parent)
: AttributeItemDelegate(parent)
{
}
QWidget *SCAttributeItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option)
switch (index.data(DataTypeRole).toInt()) {
case QVariant::StringList: {
auto combo = new QComboBox(parent);
combo->setFocusPolicy(Qt::StrongFocus);
return combo;
}
case QVariant::String: {
if (index.column() == 0) {
auto edit = new QLineEdit(parent);
edit->setFocusPolicy(Qt::StrongFocus);
QRegExp rx("^(?!xml)[_a-z][a-z0-9-._]*$");
rx.setCaseSensitivity(Qt::CaseInsensitive);
edit->setValidator(new QRegExpValidator(rx, parent));
return edit;
}
}
default:
break;
}
return QStyledItemDelegate::createEditor(parent, option, index);
}
void SCAttributeItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
if (editor)
editor->setGeometry(option.rect);
}
void SCAttributeItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
switch (index.data(DataTypeRole).toInt()) {
case QVariant::StringList: {
auto combo = qobject_cast<QComboBox*>(editor);
if (combo) {
combo->clear();
QStringList values = index.data(DataRole).toString().split(";");
foreach (QString val, values)
combo->addItem(val);
combo->setCurrentText(index.data().toString());
return;
}
break;
}
default:
break;
}
QStyledItemDelegate::setEditorData(editor, index);
}
void SCAttributeItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
auto combo = qobject_cast<QComboBox*>(editor);
if (combo) {
model->setData(index, combo->currentText());
return;
}
QStyledItemDelegate::setModelData(editor, model, index);
}

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "attributeitemdelegate.h"
namespace ScxmlEditor {
namespace PluginInterface {
class SCAttributeItemDelegate : public AttributeItemDelegate
{
public:
SCAttributeItemDelegate(QObject *parent = nullptr);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,171 @@
/****************************************************************************
**
** 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 "scattributeitemmodel.h"
#include "mytypes.h"
#include <QBrush>
using namespace ScxmlEditor::PluginInterface;
SCAttributeItemModel::SCAttributeItemModel(QObject *parent)
: AttributeItemModel(parent)
{
}
QVariant SCAttributeItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return section == 0 ? tr("Name") : tr("Value");
return QVariant();
}
bool SCAttributeItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole || m_tag == nullptr)
return false;
bool bEditable = m_tag->tagType() <= MetadataItem;
if (index.row() >= 0 && m_document != 0) {
if (!bEditable) {
if (index.row() < m_tag->info()->n_attributes)
m_document->setValue(m_tag, index.row(), value.toString());
} else {
if (index.column() == 0) {
m_tag->setAttributeName(index.row(), value.toString());
m_document->setValue(m_tag, value.toString(), m_tag->attribute(value.toString()));
} else
m_document->setValue(m_tag, m_tag->attributeName(index.row()), value.toString());
}
emit dataChanged(index, index);
emit layoutChanged();
return true;
}
return false;
}
QVariant SCAttributeItemModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || m_tag == nullptr)
return QVariant();
if (index.row() < 0)
return QVariant();
bool bEditable = m_tag->tagType() <= MetadataItem;
if (!bEditable && index.row() >= m_tag->info()->n_attributes)
return QVariant();
if (bEditable && index.row() > m_tag->attributeCount())
return QVariant();
bool bExtraRow = bEditable && m_tag->attributeCount() == index.row();
switch (role) {
case Qt::DisplayRole:
if (bExtraRow)
return index.column() == 0 ? tr("- name -") : tr(" - value -");
case Qt::EditRole: {
if (index.column() == 0) {
if (bEditable) {
return m_tag->attributeName(index.row());
} else {
if (m_tag->info()->attributes[index.row()].required)
return QString::fromLatin1("*%1").arg(QLatin1String(m_tag->info()->attributes[index.row()].name));
else
return m_tag->info()->attributes[index.row()].name;
}
} else {
if (bEditable) {
if (m_tag->tagType() > MetadataItem && m_tag->info()->attributes[index.row()].datatype == QVariant::StringList)
return QString::fromLatin1(m_tag->info()->attributes[index.row()].value).split(";");
else
return m_tag->attribute(index.row());
} else {
return m_tag->attribute(QLatin1String(m_tag->info()->attributes[index.row()].name));
}
}
}
case Qt::TextAlignmentRole:
if (bExtraRow)
return Qt::AlignHCenter;
else
break;
case Qt::ForegroundRole:
return bExtraRow ? QBrush(Qt::gray) : QBrush(Qt::black);
case DataTypeRole: {
if (m_tag->tagType() == Metadata || m_tag->tagType() == MetadataItem)
return (int)QVariant::String;
else if (index.column() == 1 && m_tag->info()->n_attributes > 0)
return m_tag->info()->attributes[index.row()].datatype;
else
return QVariant::Invalid;
}
case DataRole: {
if (m_tag->info()->n_attributes > 0)
return m_tag->info()->attributes[index.row()].value;
else
return QVariant();
}
default:
break;
}
return QVariant();
}
Qt::ItemFlags SCAttributeItemModel::flags(const QModelIndex &index) const
{
if (!index.isValid() || m_tag == nullptr)
return Qt::NoItemFlags;
if (m_tag->tagType() <= MetadataItem || (index.column() == 1 && m_tag->info()->n_attributes > 0 && m_tag->info()->attributes[index.row()].editable))
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
return Qt::NoItemFlags;
}
int SCAttributeItemModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 2;
}
int SCAttributeItemModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
if (m_tag) {
if (m_tag->tagType() <= MetadataItem)
return m_tag->attributeCount() + 1;
else
return m_tag->info()->n_attributes;
}
return 0;
}

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "attributeitemmodel.h"
namespace ScxmlEditor {
namespace PluginInterface {
class SCAttributeItemModel : public AttributeItemModel
{
public:
SCAttributeItemModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,419 @@
/****************************************************************************
**
** 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 "sceneutils.h"
#include "connectableitem.h"
#include "finalstateitem.h"
#include "graphicsscene.h"
#include "historyitem.h"
#include "initialstateitem.h"
#include "parallelitem.h"
#include "scxmldocument.h"
#include "scxmleditorconstants.h"
#include "scxmltag.h"
#include "scxmltagutils.h"
#include "stateitem.h"
#include "transitionitem.h"
#include <QGuiApplication>
#include <QGraphicsScene>
#include <QtMath>
namespace ScxmlEditor {
namespace PluginInterface {
namespace SceneUtils {
ConnectableItem *createItem(ItemType type, const QPointF &pos)
{
switch (type) {
case InitialStateType:
return new InitialStateItem(pos);
case FinalStateType:
return new FinalStateItem(pos);
case StateType:
return new StateItem(pos);
case HistoryType:
return new HistoryItem(pos);
case ParallelType:
return new ParallelItem(pos);
default:
break;
}
return nullptr;
}
ConnectableItem *createItemByTagType(TagType type, const QPointF &pos)
{
switch (type) {
case Initial:
return createItem(InitialStateType, pos);
case Final:
return createItem(FinalStateType, pos);
case State:
return createItem(StateType, pos);
case History:
return createItem(HistoryType, pos);
case Parallel:
return createItem(ParallelType, pos);
default:
return nullptr;
}
}
ScxmlTag *createTag(ItemType type, ScxmlDocument *document)
{
TagType t = UnknownTag;
switch (type) {
case InitialStateType:
t = Initial;
break;
case FinalStateType:
t = Final;
break;
case HistoryType:
t = History;
break;
case StateType:
t = State;
break;
case ParallelType:
t = Parallel;
break;
default:
break;
}
if (t != UnknownTag)
return new ScxmlTag(t, document);
return nullptr;
}
bool canDrop(int parentType, int childType)
{
switch (parentType) {
case StateType: {
switch (childType) {
case InitialStateType:
case FinalStateType:
case StateType:
case ParallelType:
case HistoryType:
return true;
default:
return false;
}
}
case ParallelType: {
switch (childType) {
case StateType:
case ParallelType:
case HistoryType:
return true;
default:
return false;
}
}
default:
return false;
}
}
QVector<ScxmlTag*> findCopyTags(const QVector<BaseItem*> &items, QPointF &minPos)
{
QPointF pp(0, 0);
QVector<ScxmlTag*> tags;
foreach (BaseItem *it, items) {
if (it->type() >= InitialStateType && it->isSelected()) {
BaseItem *parent = it->parentBaseItem();
BaseItem *lastSelectedParent = it;
while (parent) {
if (parent->isSelected())
lastSelectedParent = parent;
parent = parent->parentBaseItem();
}
if (!tags.contains(lastSelectedParent->tag())) {
QPointF p = lastSelectedParent->sceneBoundingRect().topLeft();
if (tags.isEmpty()) {
pp = p;
} else {
pp.setX(qMin(pp.x(), p.x()));
pp.setY(qMin(pp.y(), p.y()));
}
lastSelectedParent->updateUIProperties();
tags << lastSelectedParent->tag();
}
}
}
minPos = pp;
return tags;
}
QVector<ScxmlTag*> findRemovedTags(const QVector<BaseItem*> &items)
{
// Find right tags
QVector<ScxmlTag*> tags;
foreach (BaseItem *it, items) {
if (it->isSelected()) {
// Find the last selected parent
BaseItem *parent = it->parentBaseItem();
BaseItem *lastSelectedParent = it;
while (parent != 0) {
if (parent->isSelected())
lastSelectedParent = parent;
parent = parent->parentBaseItem();
}
// Add tag to the list
if (!tags.contains(lastSelectedParent->tag()))
tags << lastSelectedParent->tag();
}
}
return tags;
}
void layout(const QList<QGraphicsItem*> &items)
{
// Collect child items
QList<ConnectableItem*> childItems;
ConnectableItem *initialItem = nullptr;
ConnectableItem *finalItem = nullptr;
foreach (QGraphicsItem *item, items) {
auto connectableItem = qgraphicsitem_cast<ConnectableItem*>(item);
if (connectableItem) {
if (connectableItem->type() == InitialStateType)
initialItem = connectableItem;
else if (connectableItem->type() == FinalStateType)
finalItem = connectableItem;
else if (connectableItem->type() >= HistoryType)
childItems << connectableItem;
}
}
// Change initial-item position
ConnectableItem *firstItem = nullptr;
if (initialItem && initialItem->outputTransitionCount() == 1) {
firstItem = initialItem->outputTransitions()[0]->connectedItem(initialItem);
int index = childItems.indexOf(firstItem);
if (index > 0)
childItems.swap(index, 0);
}
// Search final-item
ConnectableItem *lastItem = nullptr;
if (finalItem && finalItem->inputTransitionCount() > 0)
lastItem = finalItem->inputTransitions()[0]->connectedItem(finalItem);
int startAngle = qrand() % 2 == 0 ? 180 : 90;
int startDistance = 40 + childItems.count() * 10;
if (childItems.count() > 0) {
// Init position of the items
int angleDiff = 360 / (childItems.count() + 1);
for (int i = 0; i < childItems.count(); ++i) {
int angle = startAngle + i * angleDiff;
QLineF line = QLineF::fromPolar(startDistance, angle);
childItems[i]->setPos(line.p2());
}
// Then grow the distances so much that there is no any overlapped items
for (int i = 0; i < childItems.count(); ++i) {
int angle = startAngle + i * angleDiff;
QLineF line = QLineF::fromPolar(startDistance, angle);
ConnectableItem *movingItem = childItems[i];
QRectF r2 = movingItem->boundingRect();
r2.moveTopLeft(r2.topLeft() + movingItem->pos());
bool intersects = true;
while (intersects) {
intersects = false;
for (int j = 0; j < childItems.count(); ++j) {
if (j != i) {
QRectF r1 = childItems[j]->boundingRect();
r1.moveTopLeft(r1.topLeft() + childItems[j]->pos());
if (r2.intersects(r1)) {
intersects = true;
break;
}
}
}
if (intersects) {
line.setLength(line.length() + 50);
movingItem->setPos(line.p2());
r2 = movingItem->boundingRect();
r2.moveTopLeft(r2.topLeft() + movingItem->pos());
}
}
}
// Then decrease the distances so much as possible
for (int i = 0; i < childItems.count(); ++i) {
ConnectableItem *movingItem = childItems[i];
QPointF p = movingItem->pos();
QLineF line(QPointF(0, 0), p);
QRectF r2 = movingItem->boundingRect();
r2.moveTopLeft(r2.topLeft() + p);
bool cont = true;
while (cont) {
bool intersects = false;
for (int j = 0; j < childItems.count(); ++j) {
if (j != i) {
QRectF r1 = childItems[j]->boundingRect();
r1.moveTopLeft(r1.topLeft() + childItems[j]->pos());
if (r2.intersects(r1)) {
intersects = true;
cont = false;
line.setLength(line.length() + 20);
movingItem->setPos(line.p2());
r2 = movingItem->boundingRect();
r2.moveTopLeft(r2.topLeft() + movingItem->pos());
break;
}
}
}
if (!intersects) {
line.setLength(line.length() - 20);
movingItem->setPos(line.p2());
r2 = movingItem->boundingRect();
r2.moveTopLeft(r2.topLeft() + movingItem->pos());
if (line.length() < 100)
cont = false;
}
}
}
// Finally set initial and final positions
foreach (ConnectableItem *item, childItems) {
if (item == firstItem)
initialItem->setPos(firstItem->pos() + firstItem->boundingRect().topLeft() - QPointF(50, 50));
else if (item == lastItem) {
int angle = startAngle + childItems.indexOf(item) * angleDiff;
QLineF line = QLineF::fromPolar(qMax(lastItem->boundingRect().width() / 2, lastItem->boundingRect().height() / 2) + 20, angle);
finalItem->setPos(lastItem->pos() + lastItem->boundingRect().center() + line.p2());
}
}
}
}
bool isChild(const QGraphicsItem *parent, const QGraphicsItem *child)
{
while (child != 0) {
if (parent == child)
return true;
child = child->parentItem();
}
return false;
}
bool isSomeSelected(QGraphicsItem *item)
{
while (item != 0) {
if (item->isSelected())
return true;
item = item->parentItem();
}
return false;
}
void moveTop(BaseItem *item, GraphicsScene *scene)
{
if (item && scene) {
// Make the current item to the topmost of all
QGraphicsItem *parentItem = item->parentItem();
QList<QGraphicsItem*> children;
if (parentItem)
children = parentItem->childItems();
else
children = scene->sceneItems(Qt::DescendingOrder);
// Remove unnecessary items
for (int i = children.count(); i--;) {
if (children[i]->type() < InitialStateType)
children.takeAt(i);
}
// Change stack order
const int ind = parentItem ? children.indexOf(item) : 0;
for (int i = ind; i < children.count(); ++i)
children[i]->stackBefore(item);
}
}
ScxmlTag *addNewTag(ScxmlTag *parent, TagType type, GraphicsScene *scene)
{
if (parent) {
ScxmlDocument *document = parent->document();
auto newTag = new ScxmlTag(type, document);
document->addTag(parent, newTag);
if (scene)
scene->unselectAll();
document->setCurrentTag(newTag);
return newTag;
}
return nullptr;
}
ScxmlTag *addChild(ScxmlTag *tag, const QVariantMap &data, GraphicsScene *scene)
{
TagType newTagType = (TagType)data.value(Constants::C_SCXMLTAG_TAGTYPE, 0).toInt();
TagType subMenuTagType = (TagType)data.value(Constants::C_SCXMLTAG_PARENTTAG, 0).toInt();
if (newTagType >= UnknownTag) {
// Check if we must create or add submenu
if (subMenuTagType > UnknownTag && subMenuTagType != tag->tagType()) {
// Check if submenu-tag is already available
ScxmlTag *subMenuTag = TagUtils::findChild(tag, subMenuTagType);
if (subMenuTag)
return addNewTag(subMenuTag, newTagType, scene);
else {
// If dont, create new submenutag and add new child
subMenuTag = addNewTag(tag, subMenuTagType, scene);
return addNewTag(subMenuTag, newTagType, scene);
}
} else
return addNewTag(tag, newTagType, scene);
}
return nullptr;
}
} // namespace SceneUtils
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
#include <QGraphicsScene>
QT_FORWARD_DECLARE_CLASS(QGraphicsItem)
namespace ScxmlEditor {
namespace PluginInterface {
class GraphicsScene;
class ConnectableItem;
class InitialStateItem;
class BaseItem;
class ScxmlTag;
/**
* Namespace SceneUtils includes some usable function to manipulate the data of the items.
*/
namespace SceneUtils {
ScxmlTag *addChild(ScxmlTag *tag, const QVariantMap &data, GraphicsScene *scene);
ScxmlTag *addNewTag(ScxmlTag *parent, TagType type, GraphicsScene *scene);
ConnectableItem *createItem(ItemType type, const QPointF &pos = QPointF());
ConnectableItem *createItemByTagType(TagType type, const QPointF &pos = QPointF());
ScxmlTag *createTag(ItemType type, ScxmlDocument *document);
bool canDrop(int parentType, int childType);
QVector<ScxmlTag*> findCopyTags(const QVector<BaseItem*> &items, QPointF &minPos);
QVector<ScxmlTag*> findRemovedTags(const QVector<BaseItem*> &items);
void layout(const QList<QGraphicsItem*> &items);
bool isSomeSelected(QGraphicsItem *item);
void moveTop(BaseItem *item, GraphicsScene *scene);
bool isChild(const QGraphicsItem *parent, const QGraphicsItem *child);
template <class T>
bool hasSiblingStates(T *item)
{
if (item) {
QList<QGraphicsItem*> children;
QGraphicsItem *parentItem = item->parentItem();
if (parentItem) {
children = parentItem->childItems();
} else if (item->scene()) {
foreach (QGraphicsItem *it, item->scene()->items()) {
if (!it->parentItem())
children << it;
}
}
foreach (QGraphicsItem *it, children) {
if (it != item && it->type() == item->type()) {
return true;
}
}
}
return false;
}
} // namespace SceneUtils
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** 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 "scgraphicsitemprovider.h"
#include "idwarningitem.h"
#include "initialstateitem.h"
#include "initialwarningitem.h"
#include "scxmleditorconstants.h"
#include "stateitem.h"
#include "transitionitem.h"
#include "transitionwarningitem.h"
using namespace ScxmlEditor::PluginInterface;
SCGraphicsItemProvider::SCGraphicsItemProvider(QObject *parent)
: GraphicsItemProvider(parent)
{
}
WarningItem *SCGraphicsItemProvider::createWarningItem(const QString &key, BaseItem *parentItem) const
{
if (key == Constants::C_STATE_WARNING_ID && parentItem)
return new IdWarningItem(parentItem);
if (key == Constants::C_STATE_WARNING_TRANSITION && parentItem && parentItem->type() == TransitionType)
return new TransitionWarningItem(static_cast<TransitionItem*>(parentItem));
if (key == Constants::C_STATE_WARNING_INITIAL && parentItem && parentItem->type() == InitialStateType)
return new InitialWarningItem(static_cast<InitialStateItem*>(parentItem));
return nullptr;
}

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "graphicsitemprovider.h"
namespace ScxmlEditor {
namespace PluginInterface {
class SCGraphicsItemProvider : public GraphicsItemProvider
{
public:
SCGraphicsItemProvider(QObject *parent = nullptr);
WarningItem *createWarningItem(const QString &key, BaseItem *parentItem) const override;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,181 @@
/****************************************************************************
**
** 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 "scshapeprovider.h"
#include "scxmltag.h"
#include <QDebug>
using namespace ScxmlEditor::PluginInterface;
SCShapeProvider::SCShapeProvider(QObject *parent)
: ShapeProvider(parent)
{
init();
}
SCShapeProvider::~SCShapeProvider()
{
clear();
}
void SCShapeProvider::clear()
{
qDeleteAll(m_groups);
m_groups.clear();
}
void SCShapeProvider::initGroups()
{
init();
}
void SCShapeProvider::init()
{
ShapeGroup *group = addGroup(tr("Common States"));
group->addShape(createShape(tr("Initial"), QIcon(":/scxmleditor/images/initial.png"), QStringList() << "scxml"
<< "state"
<< "parallel",
"<initial/>"));
group->addShape(createShape(tr("Final"), QIcon(":/scxmleditor/images/final.png"), QStringList() << "scxml"
<< "state"
<< "parallel",
"<final/>"));
group->addShape(createShape(tr("State"), QIcon(":/scxmleditor/images/state.png"), QStringList() << "scxml"
<< "state"
<< "parallel",
"<state/>"));
group->addShape(createShape(tr("Parallel"), QIcon(":/scxmleditor/images/parallel.png"), QStringList() << "scxml"
<< "state"
<< "parallel",
"<parallel/>"));
group->addShape(createShape(tr("History"), QIcon(":/scxmleditor/images/history.png"), QStringList() << "state"
<< "parallel",
"<history/>"));
}
ShapeProvider::Shape *SCShapeProvider::shape(int groupIndex, int shapeIndex)
{
if (groupIndex >= 0 && groupIndex < m_groups.count()) {
if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
return m_groups[groupIndex]->shapes[shapeIndex];
}
return nullptr;
}
ShapeProvider::ShapeGroup *SCShapeProvider::group(int groupIndex)
{
if (groupIndex >= 0 && groupIndex < m_groups.count())
return m_groups[groupIndex];
return nullptr;
}
SCShapeProvider::ShapeGroup *SCShapeProvider::addGroup(const QString &title)
{
auto group = new ShapeGroup;
group->title = title;
m_groups << group;
return group;
}
SCShapeProvider::Shape *SCShapeProvider::createShape(const QString &title, const QIcon &icon, const QStringList &filters, const QByteArray &scxmlData, const QVariant &userData)
{
auto shape = new Shape;
shape->title = title;
shape->icon = icon;
shape->filters = filters;
shape->scxmlData = scxmlData;
shape->userData = userData;
return shape;
}
int SCShapeProvider::groupCount() const
{
return m_groups.count();
}
QString SCShapeProvider::groupTitle(int groupIndex) const
{
if (groupIndex >= 0 && groupIndex < m_groups.count())
return m_groups[groupIndex]->title;
return QString();
}
int SCShapeProvider::shapeCount(int groupIndex) const
{
if (groupIndex >= 0 && groupIndex < m_groups.count())
return m_groups[groupIndex]->shapes.count();
return 0;
}
QString SCShapeProvider::shapeTitle(int groupIndex, int shapeIndex) const
{
if (groupIndex >= 0 && groupIndex < m_groups.count()) {
if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
return m_groups[groupIndex]->shapes[shapeIndex]->title;
}
return QString();
}
QIcon SCShapeProvider::shapeIcon(int groupIndex, int shapeIndex) const
{
if (groupIndex >= 0 && groupIndex < m_groups.count()) {
if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
return m_groups[groupIndex]->shapes[shapeIndex]->icon;
}
return QIcon();
}
bool SCShapeProvider::canDrop(int groupIndex, int shapeIndex, ScxmlTag *parent) const
{
QString tagName = parent ? parent->tagName(false) : "scxml";
if (groupIndex >= 0 && groupIndex < m_groups.count()) {
if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count()) {
const QStringList &filters = m_groups[groupIndex]->shapes[shapeIndex]->filters;
return filters.isEmpty() || filters.contains(tagName);
}
}
return false;
}
QByteArray SCShapeProvider::scxmlCode(int groupIndex, int shapeIndex, ScxmlTag *parent) const
{
Q_UNUSED(parent)
if (groupIndex >= 0 && groupIndex < m_groups.count()) {
if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
return m_groups[groupIndex]->shapes[shapeIndex]->scxmlData;
}
return QByteArray();
}

View File

@@ -0,0 +1,68 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "shapeprovider.h"
#include <QVector>
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlTag;
class SCShapeProvider : public ShapeProvider
{
public:
SCShapeProvider(QObject *parent = nullptr);
~SCShapeProvider() override;
int groupCount() const override;
QString groupTitle(int groupIndex) const override;
int shapeCount(int groupIndex) const override;
QString shapeTitle(int groupIndex, int shapeIndex) const override;
QIcon shapeIcon(int groupIndex, int shapeIndex) const override;
bool canDrop(int groupIndex, int shapeIndex, ScxmlTag *parent) const override;
QByteArray scxmlCode(int groupIndex, int shapeIndex, ScxmlTag *parent) const override;
protected:
virtual void clear();
virtual void initGroups();
virtual ShapeGroup *addGroup(const QString &title);
virtual Shape *createShape(const QString &title, const QIcon &icon, const QStringList &filters, const QByteArray &scxmlData, const QVariant &userData = QVariant());
Shape *shape(int groupIndex, int shapeIndex);
ShapeGroup *group(int groupIndex);
private:
void init();
QVector<ShapeGroup*> m_groups;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,87 @@
/****************************************************************************
**
** 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 "scutilsprovider.h"
#include "mytypes.h"
#include "scxmltag.h"
#include "stateitem.h"
using namespace ScxmlEditor::PluginInterface;
SCUtilsProvider::SCUtilsProvider(QObject *parent)
: UtilsProvider(parent)
{
}
void SCUtilsProvider::checkInitialState(const QList<QGraphicsItem*> &items, ScxmlTag *parentTag)
{
ScxmlTag *initialStateTag = nullptr;
if (parentTag) {
// 1. If we have initial-state, we must use it as init-state
if (parentTag->hasChild(Initial)) {
parentTag->setAttribute("initial", QString());
} else {
QString id = parentTag->attribute("initial");
// 2. If no initial-state available, try to find state with initial-attribute
if (!id.isEmpty()) {
// Find state with id
for (int i = 0; i < parentTag->childCount(); ++i) {
ScxmlTag *child = parentTag->child(i);
if ((child->tagType() == State || child->tagType() == Parallel)
&& child->attribute("id", true) == id) {
initialStateTag = child;
break;
}
}
if (!initialStateTag)
parentTag->setAttribute("initial", QString());
}
// 3. If we still cant find initial-state, we must select first
if (!initialStateTag) {
// Search first state
for (int i = 0; i < parentTag->childCount(); ++i) {
ScxmlTag *child = parentTag->child(i);
if (child->tagType() == State || child->tagType() == Parallel) {
initialStateTag = child;
break;
}
}
}
}
}
foreach (QGraphicsItem *item, items) {
if (item->type() >= StateType) {
auto stateItem = static_cast<StateItem*>(item);
if (stateItem)
stateItem->setInitial(stateItem->tag() == initialStateTag);
}
}
}

View File

@@ -0,0 +1,45 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "utilsprovider.h"
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlTag;
class SCUtilsProvider : public UtilsProvider
{
public:
SCUtilsProvider(QObject *parent = nullptr);
void checkInitialState(const QList<QGraphicsItem*> &items, ScxmlTag *parentTag) override;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,701 @@
/****************************************************************************
**
** 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 "scxmldocument.h"
#include "scxmlnamespace.h"
#include "scxmltagutils.h"
#include "undocommands.h"
#include <QBuffer>
#include <QDebug>
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <app/app_version.h>
using namespace ScxmlEditor::PluginInterface;
ScxmlDocument::ScxmlDocument(const QString &fileName, QObject *parent)
: QObject(parent)
{
initVariables();
m_fileName = fileName;
load(fileName);
}
ScxmlDocument::ScxmlDocument(const QByteArray &data, QObject *parent)
: QObject(parent)
{
initVariables();
load(QLatin1String(data));
}
ScxmlDocument::~ScxmlDocument()
{
clear(false);
}
void ScxmlDocument::initVariables()
{
m_idDelimiter = "::";
m_undoStack = new QUndoStack(this);
connect(m_undoStack, &QUndoStack::cleanChanged, this, &ScxmlDocument::documentChanged);
}
void ScxmlDocument::clear(bool createRoot)
{
m_currentTag = nullptr;
m_nextIdHash.clear();
// First clear undostack
m_undoStack->clear();
// Second delete all other available tags
// tags will call the removeChild-function -> m_tags will be cleared
for (int i = m_tags.count(); i--;)
delete m_tags[i];
m_rootTags.clear();
clearNamespaces();
if (createRoot) {
pushRootTag(createScxmlTag());
rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG));
auto ns = new ScxmlNamespace("qt", "http://www.qt.io/2015/02/scxml-ext");
ns->setTagVisibility("editorInfo", false);
addNamespace(ns);
}
m_useFullNameSpace = false;
}
QString ScxmlDocument::nameSpaceDelimiter() const
{
return m_idDelimiter;
}
bool ScxmlDocument::useFullNameSpace() const
{
return m_useFullNameSpace;
}
void ScxmlDocument::setNameSpaceDelimiter(const QString &delimiter)
{
m_idDelimiter = delimiter;
}
QString ScxmlDocument::fileName() const
{
return m_fileName;
}
void ScxmlDocument::setFileName(const QString &filename)
{
m_fileName = filename;
}
ScxmlNamespace *ScxmlDocument::scxmlNamespace(const QString &prefix)
{
return m_namespaces.value(prefix, 0);
}
void ScxmlDocument::addNamespace(ScxmlNamespace *ns)
{
if (!ns)
return;
delete m_namespaces.take(ns->prefix());
m_namespaces[ns->prefix()] = ns;
ScxmlTag *scxmlTag = scxmlRootTag();
if (scxmlTag) {
QMapIterator<QString, ScxmlNamespace*> i(m_namespaces);
while (i.hasNext()) {
i.next();
QString prefix = i.value()->prefix();
if (prefix.isEmpty())
prefix = "xmlns";
if (prefix.startsWith("xmlns"))
scxmlTag->setAttribute(prefix, i.value()->name());
else
scxmlTag->setAttribute(QString::fromLatin1("xmlns:%1").arg(prefix), i.value()->name());
}
}
}
void ScxmlDocument::clearNamespaces()
{
while (!m_namespaces.isEmpty()) {
delete m_namespaces.take(m_namespaces.firstKey());
}
}
bool ScxmlDocument::generateSCXML(QIODevice *io, ScxmlTag *tag) const
{
QXmlStreamWriter xml(io);
xml.setAutoFormatting(true);
xml.writeStartDocument();
if (tag)
tag->writeXml(xml);
else
rootTag()->writeXml(xml);
xml.writeEndDocument();
return !xml.hasError();
}
ScxmlTag *ScxmlDocument::createScxmlTag()
{
auto tag = new ScxmlTag(Scxml, this);
QMapIterator<QString, ScxmlNamespace*> i(m_namespaces);
while (i.hasNext()) {
i.next();
QString prefix = i.value()->prefix();
if (prefix.isEmpty())
prefix = "xmlns";
if (prefix.startsWith("xmlns"))
tag->setAttribute(prefix, i.value()->name());
else
tag->setAttribute(QString::fromLatin1("xmlns:%1").arg(prefix), i.value()->name());
}
return tag;
}
bool ScxmlDocument::hasLayouted() const
{
return m_hasLayouted;
}
QColor ScxmlDocument::getColor(int depth) const
{
return m_colors.isEmpty() ? QColor(Qt::gray) : m_colors[depth % m_colors.count()];
}
void ScxmlDocument::setLevelColors(const QVector<QColor> &colors)
{
m_colors = colors;
emit colorThemeChanged();
}
bool ScxmlDocument::load(QIODevice *io)
{
m_currentTag = nullptr;
clearNamespaces();
bool ok = true;
clear(false);
QXmlStreamReader xml(io);
while (!xml.atEnd()) {
QXmlStreamReader::TokenType token = xml.readNext();
if (token == QXmlStreamReader::StartDocument)
continue;
if (token == QXmlStreamReader::StartElement) {
if (xml.name() == "scxml") {
// Get and add namespaces
QXmlStreamNamespaceDeclarations ns = xml.namespaceDeclarations();
for (int i = 0; i < ns.count(); ++i)
addNamespace(new ScxmlNamespace(ns[i].prefix().toString(), ns[i].namespaceUri().toString()));
// create root tag
pushRootTag(createScxmlTag());
// and read other tags also
rootTag()->readXml(xml);
// Check editorversion
m_hasLayouted = rootTag()->hasAttribute("qt:editorversion");
rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG));
}
}
if (token == QXmlStreamReader::Invalid)
break;
}
if (xml.hasError()) {
m_hasError = true;
initErrorMessage(xml, io);
m_fileName.clear();
ok = false;
clear();
} else {
m_hasError = false;
m_lastError.clear();
}
m_undoStack->setClean();
return ok;
}
void ScxmlDocument::initErrorMessage(const QXmlStreamReader &xml, QIODevice *io)
{
QString errorString;
switch (xml.error()) {
case QXmlStreamReader::Error::UnexpectedElementError:
errorString = tr("UnexpectedElementError");
break;
case QXmlStreamReader::Error::NotWellFormedError:
errorString = tr("NotWellFormedError");
break;
case QXmlStreamReader::Error::PrematureEndOfDocumentError:
errorString = tr("PrematureEndOfDocumentError");
break;
case QXmlStreamReader::Error::CustomError:
errorString = tr("CustomError");
break;
default:
break;
}
QString lineString;
io->seek(0);
for (int i = 0; i < xml.lineNumber() - 1; ++i)
io->readLine();
lineString = QLatin1String(io->readLine());
m_lastError = tr("Error in reading XML.\nType: %1 (%2)\nDescription: %3\n\nRow: %4, Column: %5\n%6")
.arg(xml.error())
.arg(errorString)
.arg(xml.errorString())
.arg(xml.lineNumber())
.arg(xml.columnNumber())
.arg(lineString);
}
bool ScxmlDocument::pasteData(const QByteArray &data, const QPointF &minPos, const QPointF &pastePos)
{
if (!m_currentTag)
m_currentTag = rootTag();
if (!m_currentTag) {
m_hasError = true;
m_lastError = tr("Current tag not selected");
return false;
}
if (data.trimmed().isEmpty()) {
m_hasError = true;
m_lastError = tr("Pasted data is empty.");
return false;
}
bool ok = true;
m_undoStack->beginMacro(tr("Paste item(s)"));
QByteArray d(data);
QBuffer buffer(&d);
buffer.open(QIODevice::ReadOnly);
QXmlStreamReader xml(&buffer);
foreach (ScxmlNamespace *ns, m_namespaces) {
xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration(ns->prefix(), ns->name()));
}
m_idMap.clear();
QVector<ScxmlTag*> addedTags;
while (!xml.atEnd()) {
QXmlStreamReader::TokenType token = xml.readNext();
if (token == QXmlStreamReader::StartDocument)
continue;
if (token == QXmlStreamReader::StartElement) {
if (xml.name().toString() == "scxml")
continue;
ScxmlTag *childTag = nullptr;
if ((m_currentTag->tagType() == Initial || m_currentTag->tagType() == History) && xml.name().toString() == "transition")
childTag = new ScxmlTag(InitialTransition, this);
else
childTag = new ScxmlTag(xml.prefix().toString(), xml.name().toString(), this);
childTag->readXml(xml, true);
addedTags << childTag;
}
if (token == QXmlStreamReader::Invalid)
break;
}
if (xml.error()) {
m_hasError = true;
qDeleteAll(addedTags);
addedTags.clear();
initErrorMessage(xml, &buffer);
ok = false;
} else {
m_hasError = false;
m_lastError.clear();
// Fine-tune names and coordinates
for (int i = 0; i < addedTags.count(); ++i)
TagUtils::modifyPosition(addedTags[i], minPos, pastePos);
// Update targets and initial-attributes
for (int i = 0; i < addedTags.count(); ++i)
addedTags[i]->finalizeTagNames();
// Add tags to the document
addTags(m_currentTag, addedTags);
}
m_undoStack->endMacro();
return ok;
}
void ScxmlDocument::load(const QString &fileName)
{
if (QFile::exists(fileName)) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
if (load(&file)) {
m_fileName = fileName;
}
}
}
// If loading doesn't work, create root tag here
if (m_rootTags.isEmpty()) {
pushRootTag(createScxmlTag());
rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG));
}
auto ns = new ScxmlNamespace("qt", "http://www.qt.io/2015/02/scxml-ext");
ns->setTagVisibility("editorInfo", false);
addNamespace(ns);
}
void ScxmlDocument::printSCXML()
{
qDebug() << content();
}
QByteArray ScxmlDocument::content(const QVector<ScxmlTag*> &tags) const
{
QByteArray result;
if (tags.count() > 0) {
QBuffer buffer(&result);
buffer.open(QIODevice::WriteOnly);
bool writeScxml = tags.count() > 1 || tags[0]->tagType() != Scxml;
QXmlStreamWriter xml(&buffer);
xml.setAutoFormatting(true);
xml.writeStartDocument();
if (writeScxml)
xml.writeStartElement("scxml");
foreach (ScxmlTag *tag, tags) {
tag->writeXml(xml);
}
xml.writeEndDocument();
if (writeScxml)
xml.writeEndElement();
}
return result;
}
QByteArray ScxmlDocument::content(ScxmlTag *tag) const
{
QByteArray byteArray;
QBuffer buffer(&byteArray);
buffer.open(QIODevice::WriteOnly);
generateSCXML(&buffer, tag);
return byteArray;
}
bool ScxmlDocument::save()
{
return save(m_fileName);
}
bool ScxmlDocument::save(const QString &fileName)
{
QString name(fileName);
if (!name.endsWith(".scxml", Qt::CaseInsensitive))
name.append(".scxml");
bool ok = true;
QFile file(name);
if (file.open(QIODevice::WriteOnly)) {
ok = generateSCXML(&file, scxmlRootTag());
if (ok) {
m_fileName = name;
m_undoStack->setClean();
}
file.close();
if (!ok)
m_lastError = tr("Cannot save xml to the file %1.").arg(fileName);
} else {
ok = false;
m_lastError = tr("Cannot open file %1.").arg(fileName);
}
return ok;
}
void ScxmlDocument::setContent(ScxmlTag *tag, const QString &content)
{
if (tag && !m_undoRedoRunning)
m_undoStack->push(new SetContentCommand(this, tag, content));
}
void ScxmlDocument::setValue(ScxmlTag *tag, const QString &key, const QString &value)
{
if (tag && !m_undoRedoRunning)
m_undoStack->push(new SetAttributeCommand(this, tag, key, value));
}
void ScxmlDocument::setValue(ScxmlTag *tag, int attributeIndex, const QString &value)
{
if (tag && attributeIndex >= 0 && attributeIndex < tag->info()->n_attributes)
m_undoStack->push(new SetAttributeCommand(this, tag, QLatin1String(tag->info()->attributes[attributeIndex].name), value));
}
void ScxmlDocument::setEditorInfo(ScxmlTag *tag, const QString &key, const QString &value)
{
if (tag && !m_undoRedoRunning)
m_undoStack->push(new SetEditorInfoCommand(this, tag, key, value));
}
void ScxmlDocument::setCurrentTag(ScxmlTag *tag)
{
if (tag != m_currentTag) {
emit beginTagChange(TagCurrentChanged, tag, QVariant());
m_currentTag = tag;
emit endTagChange(TagCurrentChanged, tag, QVariant());
}
}
ScxmlTag *ScxmlDocument::currentTag() const
{
return m_currentTag;
}
void ScxmlDocument::changeParent(ScxmlTag *child, ScxmlTag *newParent, int tagIndex)
{
if (child && child->parentTag() != newParent && !m_undoRedoRunning)
m_undoStack->push(new ChangeParentCommand(this, child, newParent == nullptr ? rootTag() : newParent, tagIndex));
}
void ScxmlDocument::changeOrder(ScxmlTag *child, int newPos)
{
if (child && !m_undoRedoRunning) {
ScxmlTag *parentTag = child->parentTag();
if (parentTag) {
m_undoStack->push(new ChangeOrderCommand(this, child, parentTag, newPos));
}
}
}
void ScxmlDocument::addTags(ScxmlTag *parent, const QVector<ScxmlTag*> tags)
{
if (m_undoRedoRunning)
return;
if (!parent)
parent = rootTag();
m_undoStack->push(new AddRemoveTagsBeginCommand(this, parent));
for (int i = 0; i < tags.count(); ++i)
addTag(parent, tags[i]);
m_undoStack->push(new AddRemoveTagsEndCommand(this, parent));
}
void ScxmlDocument::addTag(ScxmlTag *parent, ScxmlTag *child)
{
if (m_undoRedoRunning)
return;
if (!parent)
parent = rootTag();
if (parent && child) {
m_undoStack->beginMacro(tr("Add Tag"));
addTagRecursive(parent, child);
m_undoStack->endMacro();
}
}
void ScxmlDocument::removeTag(ScxmlTag *tag)
{
if (tag && !m_undoRedoRunning) {
// Create undo/redo -macro, because state can includes lot of child-states
m_undoStack->beginMacro(tr("Remove Tag"));
removeTagRecursive(tag);
m_undoStack->endMacro();
}
}
void ScxmlDocument::addTagRecursive(ScxmlTag *parent, ScxmlTag *tag)
{
if (tag && !m_undoRedoRunning) {
m_undoStack->push(new AddRemoveTagCommand(this, parent, tag, TagAddChild));
// First create AddRemoveTagCommands for the all children recursive
for (int i = 0; i < tag->childCount(); ++i)
addTagRecursive(tag, tag->child(i));
}
}
void ScxmlDocument::removeTagRecursive(ScxmlTag *tag)
{
if (tag && !m_undoRedoRunning) {
// First create AddRemoveTagCommands for the all children recursive
int childCount = tag->childCount();
for (int i = childCount; i--;)
removeTagRecursive(tag->child(i));
m_undoStack->push(new AddRemoveTagCommand(this, tag->parentTag(), tag, TagRemoveChild));
}
}
void ScxmlDocument::setUseFullNameSpace(bool use)
{
if (m_useFullNameSpace != use)
m_undoStack->push(new ChangeFullNameSpaceCommand(this, scxmlRootTag(), use));
}
QString ScxmlDocument::nextUniqueId(const QString &key)
{
QString name;
bool bFound = false;
int id = 0;
while (true) {
id = m_nextIdHash.value(key, 0) + 1;
m_nextIdHash[key] = id;
bFound = false;
name = QString::fromLatin1("%1_%2").arg(key).arg(id);
// Check duplicate
foreach (const ScxmlTag *tag, m_tags) {
if (tag->attribute("id") == name) {
bFound = true;
break;
}
}
if (!bFound)
break;
}
return name;
}
QString ScxmlDocument::getUniqueCopyId(const ScxmlTag *tag)
{
const QString key = tag->attribute("id");
QString name = key;
int counter = 1;
bool bFound = false;
while (true) {
bFound = false;
// Check duplicate
foreach (const ScxmlTag *t, m_tags) {
if (t->attribute("id") == name && t != tag) {
name = QString::fromLatin1("%1_Copy(%2)").arg(key).arg(counter);
bFound = true;
counter++;
}
}
if (!bFound)
break;
}
return name;
}
bool ScxmlDocument::changed() const
{
return !m_undoStack->isClean();
}
ScxmlTag *ScxmlDocument::scxmlRootTag() const
{
ScxmlTag *tag = rootTag();
while (tag && tag->tagType() != Scxml) {
tag = tag->parentTag();
}
return tag;
}
ScxmlTag *ScxmlDocument::rootTag() const
{
return m_rootTags.isEmpty() ? 0 : m_rootTags.last();
}
void ScxmlDocument::pushRootTag(ScxmlTag *tag)
{
m_rootTags << tag;
}
ScxmlTag *ScxmlDocument::popRootTag()
{
return m_rootTags.takeLast();
}
void ScxmlDocument::deleteRootTags()
{
while (m_rootTags.count() > 0)
delete m_rootTags.takeLast();
}
QUndoStack *ScxmlDocument::undoStack() const
{
return m_undoStack;
}
void ScxmlDocument::addChild(ScxmlTag *tag)
{
if (!m_tags.contains(tag))
m_tags << tag;
}
void ScxmlDocument::removeChild(ScxmlTag *tag)
{
m_tags.removeAll(tag);
}
void ScxmlDocument::setUndoRedoRunning(bool para)
{
m_undoRedoRunning = para;
}
QFileInfo ScxmlDocument::qtBinDir() const
{
return m_qtBinDir;
}

View File

@@ -0,0 +1,296 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QFileInfo>
#include <QMap>
#include <QObject>
#include <QPointF>
#include <QString>
#include <QVector>
QT_FORWARD_DECLARE_CLASS(QUndoStack)
QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlNamespace;
class ScxmlTag;
/**
* @brief The ScxmlDocument class represents an SCXML document.
*
* This is the root of the document tree, and provides the access to the document's data.
* The tag-element is ScxmlTag. You can create ScxmlTag-objects outside this class, but you have to call data-manipulation
* functions from this class to be sure that all views can update their tags. When calling data-manipulation functions, this class
* will send beginTagChange and endTagChange signals with the correspond values. This class also keep the undo-stack of the changes.
*
* When document has changed, the signal documentChanged(bool) will be emitted.
*/
class ScxmlDocument : public QObject
{
Q_OBJECT
public:
/**
* @brief The TagChange enum
*/
enum TagChange {
TagAddChild = 0,
TagAddTags,
TagRemoveChild,
TagRemoveTags,
TagCurrentChanged,
TagAttributesChanged,
TagEditorInfoChanged,
TagChangeParent,
TagChangeParentRemoveChild,
TagChangeParentAddChild,
TagChangeOrder,
TagContentChanged,
TagChangeFullNameSpace
};
explicit ScxmlDocument(const QString &fileName = QString(), QObject *parent = nullptr);
explicit ScxmlDocument(const QByteArray &data, QObject *parent = nullptr);
~ScxmlDocument() override;
void addNamespace(ScxmlNamespace *ns);
ScxmlNamespace *scxmlNamespace(const QString &prefix);
/**
* @brief setCurrentTag - inform views that current selected tag has changed
* @param tag - current tag
*/
void setCurrentTag(ScxmlTag *tag);
ScxmlTag *currentTag() const;
/**
* @brief setContent - inform views that tag-content has changed
* @param tag
* @param content
*/
void setContent(ScxmlTag *tag, const QString &content);
/**
* @brief setValue - inform views that tag-value has changed
* @param tag
* @param key
* @param value
*/
void setValue(ScxmlTag *tag, const QString &key, const QString &value);
/**
* @brief setValue - inform views that tag-value has changed
* @param tag
* @param attributeIndex
* @param value
*/
void setValue(ScxmlTag *tag, int attributeIndex, const QString &value);
/**
* @brief setEditorInfo - inform views (and save undo-command) that editorinfo has changed
* @param tag
* @param key
* @param value
*/
void setEditorInfo(ScxmlTag *tag, const QString &key, const QString &value);
/**
* @brief addTag - inform views that new child will be added to the tree
* @param parent
* @param child
*/
void addTag(ScxmlTag *parent, ScxmlTag *child);
void addTags(ScxmlTag *parent, const QVector<ScxmlTag*> tags);
/**
* @brief removeTag - inform views that tag will be removed
* @param tag
*/
void removeTag(ScxmlTag *tag);
/**
* @brief changeParent - inform views that tag parent will be changed
* @param child
* @param newParent
* @param tagIndex
*/
void changeParent(ScxmlTag *child, ScxmlTag *newParent, int tagIndex = -1);
/**
* @brief changeOrder - inform vies that tag's child-position will be changed
* @param child
* @param newPos
*/
void changeOrder(ScxmlTag *child, int newPos);
/**
* @brief changed - holds the changes-status of the document
* @return - true if changed, false otherwise
*/
bool changed() const;
/**
* @brief rootTag - return rootTag of the document
* @return
*/
ScxmlTag *rootTag() const;
ScxmlTag *scxmlRootTag() const;
void pushRootTag(ScxmlTag *tag);
ScxmlTag *popRootTag();
/**
* @brief fileName - return current filename of the document
* @return
*/
QString fileName() const;
void setFileName(const QString &filename);
/**
* @brief undoStack - return undo-stack of the document (see UndoStack)
* @return
*/
QUndoStack *undoStack() const;
/**
* @brief save - save document with given filename
* @param fileName -
* @return return true if file has successfully wrote
*/
bool save(const QString &fileName);
bool save();
void load(const QString &fileName);
bool pasteData(const QByteArray &data, const QPointF &minPos = QPointF(0, 0), const QPointF &pastePos = QPointF(0, 0));
bool load(QIODevice *io);
/**
* @brief nextId - generate and return next id depends on the given key
* @param key
* @return return value will be the form key_nextId
*/
QString nextUniqueId(const QString &key);
QString getUniqueCopyId(const ScxmlTag *tag);
/**
* @brief hasLayouted - tells is there qt-namespace available in the current loaded document
* @return true or false
*/
bool hasLayouted() const;
void setLevelColors(const QVector<QColor> &colors);
QColor getColor(int depth) const;
bool hasError() const
{
return m_hasError;
}
QString lastError() const
{
return m_lastError;
}
QByteArray content(const QVector<ScxmlTag*> &tags) const;
QByteArray content(ScxmlTag *tag = nullptr) const;
void clear(bool createRoot = true);
// Full NameSpace functions
QString nameSpaceDelimiter() const;
bool useFullNameSpace() const;
void setNameSpaceDelimiter(const QString &delimiter);
void setUseFullNameSpace(bool use);
void setUndoRedoRunning(bool para);
QFileInfo qtBinDir() const;
signals:
/**
* @brief documentChanged - this signal will be emitted just after document has changed and is not in clean state
* @param changed
*/
void documentChanged(bool changed);
/**
* @brief beginTagChange - this signal will be emitted just before some tagchange will be made (see TagChange)
* @param change
* @param tag
* @param value
*/
void beginTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
/**
* @brief beginTagChange - this signal will be emitted just after some tagchange has made (see TagChange)
* @param change
* @param tag
* @param value
*/
void endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
/**
* @brief colorThemeChanged - this signal will be emitted just after color theme has changed
*/
void colorThemeChanged();
private:
friend class ScxmlTag;
friend class ChangeFullNameSpaceCommand;
void initErrorMessage(const QXmlStreamReader &xml, QIODevice *io);
bool generateSCXML(QIODevice *io, ScxmlTag *tag = nullptr) const;
void addChild(ScxmlTag *tag);
void removeChild(ScxmlTag *tag);
void printSCXML();
void deleteRootTags();
void clearNamespaces();
void initVariables();
void addTagRecursive(ScxmlTag *parent, ScxmlTag *tag);
void removeTagRecursive(ScxmlTag *tag);
ScxmlTag *createScxmlTag();
QString m_fileName;
QUndoStack *m_undoStack;
QVector<ScxmlTag*> m_tags;
QHash<QString, int> m_nextIdHash;
QHash<QString, QString> m_idMap;
bool m_hasError = false;
QString m_lastError;
QVector<ScxmlTag*> m_rootTags;
QMap<QString, ScxmlNamespace*> m_namespaces;
QVector<QColor> m_colors;
bool m_hasLayouted = false;
QString m_idDelimiter;
bool m_useFullNameSpace = false;
ScxmlTag *m_currentTag = nullptr;
bool m_undoRedoRunning = false;
QFileInfo m_qtBinDir;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,55 @@
/****************************************************************************
**
** 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 "scxmlnamespace.h"
using namespace ScxmlEditor::PluginInterface;
ScxmlNamespace::ScxmlNamespace(const QString &prefix, const QString &name, QObject *parent)
: QObject(parent)
, m_prefix(prefix)
, m_name(name)
{
}
QString ScxmlNamespace::prefix() const
{
return m_prefix;
}
QString ScxmlNamespace::name() const
{
return m_name;
}
bool ScxmlNamespace::isTagVisible(const QString &tag) const
{
return m_tagVisibility.value(tag, true);
}
void ScxmlNamespace::setTagVisibility(const QString &tag, bool visible)
{
m_tagVisibility[tag] = visible;
}

View File

@@ -0,0 +1,52 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QMap>
#include <QObject>
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlNamespace : public QObject
{
public:
ScxmlNamespace(const QString &prefix, const QString &name, QObject *parent = nullptr);
QString prefix() const;
QString name() const;
bool isTagVisible(const QString &tag) const;
void setTagVisibility(const QString &tag, bool visible);
private:
QString m_prefix, m_name;
QMap<QString, bool> m_tagVisibility;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,679 @@
/****************************************************************************
**
** 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 "scxmltag.h"
#include "scxmldocument.h"
#include "scxmleditorconstants.h"
#include "scxmltagutils.h"
#include <QCoreApplication>
#include <QDebug>
using namespace ScxmlEditor::PluginInterface;
ScxmlTag::ScxmlTag(TagType type, ScxmlDocument *document)
: m_prefix(
QLatin1String(type == Metadata || type == MetadataItem ? "qt" : ""))
{
setDocument(document);
init(type);
m_tagName = QLatin1String(m_info->name);
}
ScxmlTag::ScxmlTag(const QString &prefix, const QString &name,
ScxmlDocument *document)
: m_tagName(name)
, m_prefix(prefix)
{
setDocument(document);
TagType type = UnknownTag;
for (int i = 0; i < Finalize; ++i) {
if (QLatin1String(scxml_tags[i].name) == name) {
type = (TagType)i;
break;
}
}
init(type);
}
ScxmlTag::ScxmlTag(const ScxmlTag *other, bool copyChildren)
{
setDocument(other->m_document);
m_tagType = other->m_tagType;
m_tagName = other->m_tagName;
m_content = other->m_content;
m_prefix = other->m_prefix;
m_info = &scxml_tags[m_tagType];
m_attributeNames = other->m_attributeNames;
m_attributeValues = other->m_attributeValues;
m_editorInfo = other->m_editorInfo;
if (copyChildren) {
for (int i = 0; i < other->m_childTags.count(); ++i)
appendChild(new ScxmlTag(other->m_childTags[i], copyChildren));
}
}
ScxmlTag::~ScxmlTag()
{
if (m_document)
m_document->removeChild(this);
m_attributeNames.clear();
m_attributeValues.clear();
m_childTags.clear();
// m_parentTag = 0;
m_document = nullptr;
m_info = nullptr;
m_tagType = UnknownTag;
}
void ScxmlTag::finalizeTagNames()
{
switch (m_tagType) {
case State: {
if (hasAttribute("initial")) {
QString oldInitial = attribute("initial");
QString newInitial = m_document->m_idMap.value(oldInitial);
setAttribute("initial", newInitial);
}
break;
}
default:
break;
}
for (int i = m_childTags.count(); i--;) {
ScxmlTag *t = m_childTags[i];
switch (t->tagType()) {
case InitialTransition:
case Transition: {
QString oldTarget = t->attribute("target");
QString newTarget = m_document->m_idMap.value(oldTarget);
if (!oldTarget.isEmpty() && newTarget.isEmpty())
delete m_childTags.takeAt(i);
else
t->setAttribute("target", newTarget);
break;
}
default:
t->finalizeTagNames();
break;
}
}
}
void ScxmlTag::print()
{
qDebug() << "type " << m_tagType;
qDebug() << "name " << m_tagName;
qDebug() << "parent " << (m_parentTag ? m_parentTag->m_tagName : "");
qDebug() << "attributeNames " << m_attributeNames;
qDebug() << "attributeValues " << m_attributeValues;
qDebug() << "childcount " << m_childTags.count();
for (int i = 0; i < m_childTags.count(); ++i)
qDebug() << " child " << i << m_childTags[i]->m_tagName;
}
void ScxmlTag::init(TagType type)
{
m_tagType = type;
m_info = &scxml_tags[type];
for (int i = 0; i < m_info->n_attributes; ++i) {
if (m_info->attributes[i].value)
setAttribute(
QLatin1String(m_info->attributes[i].name),
QString::fromLatin1(m_info->attributes[i].value).split(";").first());
}
initId();
}
void ScxmlTag::initId()
{
// Init state IDs
if (m_document) {
switch (m_tagType) {
case State:
setAttribute("id", m_document->nextUniqueId("State"));
break;
case Parallel:
setAttribute("id", m_document->nextUniqueId("Parallel"));
break;
case History:
setAttribute("id", m_document->nextUniqueId("History"));
break;
case Transition:
setAttribute("event", m_document->nextUniqueId("Transition"));
break;
case Final:
setAttribute("id", m_document->nextUniqueId("Final"));
break;
default:
break;
}
}
}
void ScxmlTag::setDocument(ScxmlDocument *document)
{
if (m_document != document) {
if (m_document)
m_document->removeChild(this);
m_document = document;
if (m_document)
m_document->addChild(this);
}
}
ScxmlDocument *ScxmlTag::document() const
{
return m_document;
}
const scxmltag_type_t *ScxmlTag::info() const
{
return m_info;
}
TagType ScxmlTag::tagType() const
{
return m_tagType;
}
QString ScxmlTag::prefix() const
{
return m_prefix;
}
void ScxmlTag::setTagName(const QString &name)
{
m_tagName = name;
}
QString ScxmlTag::tagName(bool addPrefix) const
{
if (m_prefix.isEmpty() || !addPrefix)
return m_tagName;
else
return QString::fromLatin1("%1:%2").arg(m_prefix).arg(m_tagName);
}
QString ScxmlTag::displayName() const
{
switch (m_tagType) {
case State:
case Parallel:
case Final:
return attribute("id");
case Transition:
case InitialTransition:
return attribute("event");
break;
default:
return QString();
}
}
QString ScxmlTag::stateNameSpace() const
{
if (m_parentTag) {
switch (m_parentTag->m_tagType) {
case State:
case Parallel:
return QString::fromLatin1("%1%2%3")
.arg(m_parentTag->stateNameSpace())
.arg(m_parentTag->attribute(
m_parentTag->m_attributeNames.indexOf("id")))
.arg(m_document->nameSpaceDelimiter());
default:
break;
}
}
return QString();
}
QString ScxmlTag::content() const
{
return m_content;
}
void ScxmlTag::setContent(const QString &content)
{
m_content = content.trimmed();
}
bool ScxmlTag::hasData() const
{
if (m_attributeNames.count() > 0 || !m_content.isEmpty())
return true;
foreach (ScxmlTag *tag, m_childTags) {
if (tag->hasData())
return true;
}
return false;
}
bool ScxmlTag::hasChild(TagType type) const
{
foreach (ScxmlTag *tag, m_childTags) {
if (tag->tagType() == type)
return true;
}
return false;
}
bool ScxmlTag::hasChild(const QString &name) const
{
foreach (ScxmlTag *tag, m_childTags) {
if (tag->tagName() == name)
return true;
}
return false;
}
void ScxmlTag::insertChild(int index, ScxmlTag *child)
{
if (index >= 0 && index < m_childTags.count()) {
m_childTags.insert(index, child);
child->setParentTag(this);
} else
appendChild(child);
}
void ScxmlTag::appendChild(ScxmlTag *child)
{
if (!m_childTags.contains(child)) {
m_childTags.append(child);
child->setParentTag(this);
}
}
void ScxmlTag::removeChild(ScxmlTag *child)
{
m_childTags.removeAll(child);
}
void ScxmlTag::moveChild(int oldPos, int newPos)
{
m_childTags.insert(newPos, m_childTags.takeAt(oldPos));
}
QString ScxmlTag::attributeName(int ind) const
{
if (ind >= 0 && ind < m_attributeNames.count())
return m_attributeNames[ind];
return QString();
}
QString ScxmlTag::attribute(int ind, const QString &defaultValue) const
{
if (ind >= 0 && ind < m_attributeValues.count())
return m_attributeValues[ind];
return defaultValue;
}
void ScxmlTag::setEditorInfo(const QString &key, const QString &value)
{
if (value.isEmpty())
m_editorInfo.remove(key);
else
m_editorInfo[key] = value;
}
QString ScxmlTag::editorInfo(const QString &key) const
{
return m_editorInfo.value(key);
}
bool ScxmlTag::hasEditorInfo(const QString &key) const
{
return m_editorInfo.keys().contains(key);
}
void ScxmlTag::setAttributeName(int ind, const QString &name)
{
if (m_attributeNames.contains(name))
return;
if (ind >= 0 && ind < m_attributeValues.count()) {
m_attributeNames[ind] = name;
} else {
m_attributeNames << name;
m_attributeValues << QCoreApplication::translate(
"SXCMLTag::UnknownAttributeValue", "Unknown");
}
}
void ScxmlTag::setAttribute(int ind, const QString &value)
{
if (ind >= 0 && ind < m_attributeNames.count())
setAttribute(m_attributeNames[ind], value);
else {
m_attributeNames << QCoreApplication::translate(
"SXCMLTag::UnknownAttributeName", "Unknown");
m_attributeValues << value;
}
}
void ScxmlTag::setAttribute(const QString &name, const QString &value)
{
if (value.isEmpty()) {
int ind = m_attributeNames.indexOf(name);
if (ind >= 0 && ind < m_attributeNames.count()) {
m_attributeNames.removeAt(ind);
m_attributeValues.removeAt(ind);
}
} else if (name.isEmpty()) {
int ind = m_attributeValues.indexOf(value);
if (ind >= 0 && ind < m_attributeValues.count()) {
m_attributeNames.removeAt(ind);
m_attributeValues.removeAt(ind);
}
} else {
int ind = m_attributeNames.indexOf(name);
if (ind >= 0 && ind < m_attributeNames.count()) {
m_attributeNames[ind] = name;
m_attributeValues[ind] = value;
} else {
m_attributeNames << name;
m_attributeValues << value;
}
}
}
QStringList ScxmlTag::attributeNames() const
{
return m_attributeNames;
}
QStringList ScxmlTag::attributeValues() const
{
return m_attributeValues;
}
int ScxmlTag::attributeCount() const
{
return m_attributeNames.count();
}
QString ScxmlTag::attribute(const QString &attr, bool useNameSpace,
const QString &defaultValue) const
{
QString value = attribute(m_attributeNames.indexOf(attr), defaultValue);
if (useNameSpace && m_document->useFullNameSpace())
return QString::fromLatin1("%1%2").arg(stateNameSpace()).arg(value);
return value;
}
bool ScxmlTag::hasAttribute(const QString &key) const
{
return m_attributeNames.contains(key);
}
int ScxmlTag::childCount() const
{
return m_childTags.count();
}
int ScxmlTag::childIndex(const ScxmlTag *child) const
{
return m_childTags.indexOf(const_cast<ScxmlTag*>(child));
}
void ScxmlTag::setParentTag(ScxmlTag *parentTag)
{
m_parentTag = parentTag;
}
bool ScxmlTag::hasParentTag() const
{
return m_parentTag != nullptr;
}
ScxmlTag *ScxmlTag::child(int index) const
{
return index >= 0 && index < m_childTags.count()
? m_childTags.value(index)
: 0;
}
ScxmlTag *ScxmlTag::parentTag() const
{
return m_parentTag;
}
int ScxmlTag::index() const
{
return m_parentTag ? m_parentTag->childIndex(const_cast<ScxmlTag*>(this))
: 0;
}
bool ScxmlTag::isRootTag() const
{
return m_document->rootTag() == this;
}
QVector<ScxmlTag*> ScxmlTag::allChildren() const
{
return m_childTags;
}
QVector<ScxmlTag*> ScxmlTag::children(const QString &name) const
{
QVector<ScxmlTag*> children;
foreach (ScxmlTag *tag, m_childTags) {
if (tag->tagName() == name)
children << tag;
}
return children;
}
ScxmlTag *ScxmlTag::child(const QString &name) const
{
foreach (ScxmlTag *tag, m_childTags) {
if (tag->tagName() == name)
return tag;
}
return nullptr;
}
void ScxmlTag::writeXml(QXmlStreamWriter &xml)
{
// Start tag
xml.writeStartElement(tagName());
// Don't write attribute if type is Script and content is available
if (m_tagType != Script || m_content.isEmpty()) {
for (int i = 0; i < m_attributeNames.count(); ++i) {
if (m_attributeNames[i] == "id" && m_document->useFullNameSpace())
xml.writeAttribute("id", attribute("id", true));
else
xml.writeAttribute(m_attributeNames[i], m_attributeValues[i]);
}
}
// If tag is Scxml (root), we need to fine-tune initial-state
if (m_tagType == Scxml) {
setEditorInfo("initialGeometry", QString());
setEditorInfo("transitionGeometry", QString());
ScxmlTag *initialTag = child("initial");
if (initialTag) {
setEditorInfo("initialGeometry", initialTag->editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY));
ScxmlTag *initialTransitionTag = initialTag->child("transition");
if (initialTransitionTag) {
xml.writeAttribute("initial",
initialTransitionTag->attribute("target"));
setEditorInfo("transitionGeometry",
initialTransitionTag->editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY));
}
}
}
// Write content if necessary
if (m_info->canIncludeContent && !m_content.isEmpty())
xml.writeCharacters(m_content);
// Write editorinfo if necessary
if (!m_editorInfo.isEmpty()) {
xml.writeStartElement("qt:editorinfo");
QHashIterator<QString, QString> i(m_editorInfo);
while (i.hasNext()) {
i.next();
xml.writeAttribute(i.key(), i.value());
}
xml.writeEndElement();
}
// Write children states
for (int i = 0; i < m_childTags.count(); ++i) {
if (m_tagType == Scxml && m_childTags[i]->tagType() == Initial)
continue;
if ((m_childTags[i]->tagType() == Metadata || m_childTags[i]->tagType() == MetadataItem) && !m_childTags[i]->hasData())
continue;
m_childTags[i]->writeXml(xml);
}
// End tag
xml.writeEndElement();
}
void ScxmlTag::readXml(QXmlStreamReader &xml, bool checkCopyId)
{
QString scxmlInitial;
// Read and set attributes
QXmlStreamAttributes attributes = xml.attributes();
for (int i = 0; i < attributes.count(); ++i) {
if (m_tagType == Scxml && attributes[i].qualifiedName() == "initial")
scxmlInitial = attributes[i].value().toString();
else {
QString key = attributes[i].qualifiedName().toString();
QString value = attributes[i].value().toString();
// Modify id-attribute if necessary
switch (m_tagType) {
case History:
case Final:
case State:
case Parallel:
case Initial: {
if (key == "id") {
setAttribute(key, value);
QString oldId = value;
QString parentNS = stateNameSpace();
if (value.startsWith(parentNS))
value.remove(parentNS);
if (checkCopyId)
value = m_document->getUniqueCopyId(this);
if (m_document->useFullNameSpace())
m_document->m_idMap[oldId] = QString::fromLatin1("%1%2").arg(stateNameSpace()).arg(value);
else
m_document->m_idMap[oldId] = value;
}
break;
}
default:
break;
}
setAttribute(key, value);
}
}
// Read children states
QXmlStreamReader::TokenType token = xml.readNext();
while (token != QXmlStreamReader::EndElement) {
if (token == QXmlStreamReader::Characters)
m_content = xml.text().toString().trimmed();
else if (token == QXmlStreamReader::StartElement) {
if (m_tagType != Metadata && m_tagType != MetadataItem && xml.qualifiedName().toString() == "qt:editorinfo") {
// Read editorinfos
QXmlStreamAttributes attributes = xml.attributes();
foreach (QXmlStreamAttribute attr, attributes) {
m_editorInfo[attr.name().toString()] = attr.value().toString();
}
// Update fullnamespace-type of the scxmldocument
if (m_tagType == Scxml) {
m_document->m_useFullNameSpace = m_editorInfo.value("fullnamespace", "false") == "true";
m_document->m_idDelimiter = m_editorInfo.value("fullnamespacedelimiter", "::");
}
xml.readNext();
} else {
ScxmlTag *childTag = nullptr;
if ((m_tagType == Initial || m_tagType == History) && xml.name().toString() == "transition")
childTag = new ScxmlTag(InitialTransition, m_document);
else
childTag = new ScxmlTag(xml.prefix().toString(),
xml.name().toString(), m_document);
appendChild(childTag);
childTag->readXml(xml, checkCopyId);
}
} else if (token == QXmlStreamReader::Invalid) {
qDebug() << ScxmlTag::tr("Error in reading XML ") << xml.error() << ":"
<< xml.errorString();
break;
}
token = xml.readNext();
}
// Create initial state of the scxml (root)
if (m_tagType == Scxml && !scxmlInitial.isEmpty()) {
auto initialTag = new ScxmlTag(Initial, m_document);
auto initialTransitionTag = new ScxmlTag(InitialTransition, m_document);
initialTransitionTag->setAttribute("target", scxmlInitial);
initialTag->setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY,
m_editorInfo.value("initialGeometry"));
initialTransitionTag->setEditorInfo(
Constants::C_SCXML_EDITORINFO_GEOMETRY, m_editorInfo.value("transitionGeometry"));
appendChild(initialTag);
initialTag->appendChild(initialTransitionTag);
}
}

View File

@@ -0,0 +1,148 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "scxmltypes.h"
#include <QHash>
#include <QObject>
#include <QPointer>
#include <QStringList>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlDocument;
/**
* @brief The ScxmlTag class represents one element in the SCXML document tree.
*
* Tag have a tagType, info and zero or more attributes associated with them. TagType must be given in the constructor and
* it is not possible to change it. You can get and set attributes with the functions attribute(), setAttribute() and hasAttribute().
*
* ScxmlTag has one parent-tag and zero or more child. Tag doesn't take ownerships of the children.
* It is user responsibility to delete all tags. Normally this will be done via the ScxmlDocument inside the undo-commands.
*/
class ScxmlTag : public QObject
{
public:
ScxmlTag(TagType type, ScxmlDocument *document = nullptr);
ScxmlTag(const QString &prefix, const QString &name, ScxmlDocument *document = nullptr);
ScxmlTag(const ScxmlTag *other, bool copyChildren = true);
~ScxmlTag() override;
void setDocument(ScxmlDocument *document);
ScxmlDocument *document() const;
// Functions for handling tagType/tagName
TagType tagType() const;
QString prefix() const;
QString tagName(bool addPrefix = true) const;
void setTagName(const QString &name);
const scxmltag_type_t *info() const;
QString displayName() const;
QString stateNameSpace() const;
// Get/set content
QString content() const;
void setContent(const QString &content);
// Handling editorInfo
void setEditorInfo(const QString &key, const QString &value);
QString editorInfo(const QString &key) const;
bool hasEditorInfo(const QString &key) const;
// Functions for handling attributes
int attributeCount() const;
QStringList attributeNames() const;
QStringList attributeValues() const;
void setAttributeName(int ind, const QString &name);
void setAttribute(int ind, const QString &name);
QString attributeName(int ind) const;
QString attribute(const QString &attribute, bool useNameSpace = false, const QString &defaultValue = QString()) const;
QString attribute(int ind, const QString &defaultValue = QString()) const;
void setAttribute(const QString &attribute, const QString &value);
bool hasAttribute(const QString &key) const;
// Functions for handling parent
void setParentTag(ScxmlTag *parentTag);
ScxmlTag *parentTag() const;
bool hasParentTag() const;
// Functions for handling child tags
bool hasData() const;
bool hasChild(TagType type) const;
bool hasChild(const QString &name) const;
void insertChild(int index, ScxmlTag *child);
void appendChild(ScxmlTag *child);
void removeChild(ScxmlTag *child);
void moveChild(int oldPos, int newPos);
int childCount() const;
QVector<ScxmlTag*> allChildren() const;
QVector<ScxmlTag*> children(const QString &name) const;
ScxmlTag *child(const QString &name) const;
ScxmlTag *child(int row) const;
int childIndex(const ScxmlTag *child) const;
int index() const;
bool isRootTag() const;
/**
* @brief writeXml - write tag's content with the QXMLStreamWriter. Call writeXml-function for all children too.
* @param xml
*/
void writeXml(QXmlStreamWriter &xml);
/**
* @brief readXml - read tag's information from the QXMLStreamReader and create child tags if necessary.
* @param xml
*/
void readXml(QXmlStreamReader &xml, bool checkCopyId = false);
void finalizeTagNames();
void print();
private:
void init(TagType type);
void initId();
const scxmltag_type_t *m_info = nullptr;
QStringList m_attributeNames;
QStringList m_attributeValues;
QPointer<ScxmlTag> m_parentTag;
QVector<ScxmlTag*> m_childTags;
QPointer<ScxmlDocument> m_document;
TagType m_tagType = UnknownTag;
QString m_tagName;
QString m_content;
QString m_prefix;
QHash<QString, QString> m_editorInfo;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,386 @@
/****************************************************************************
**
** 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 "scxmltagutils.h"
#include "scxmldocument.h"
#include "scxmleditorconstants.h"
#include "serializer.h"
#include <utils/qtcassert.h>
#include <QAction>
#include <QPointF>
#include <QRectF>
namespace ScxmlEditor {
namespace PluginInterface {
namespace TagUtils {
bool checkPaste(const QString &copiedTagTypes, const ScxmlTag *currentTag)
{
if (copiedTagTypes.isEmpty() || currentTag == nullptr)
return false;
QVector<TagType> tagTypes;
for (int i = 0; i < Finalize; ++i) {
if (copiedTagTypes.contains(QLatin1String(scxml_tags[i].name)))
tagTypes << TagType(i);
}
tagTypes.removeAll(InitialTransition);
if (tagTypes.isEmpty())
return false;
QVector<TagType> childTags = allowedChildTypes(currentTag->tagType());
foreach (const TagType &type, tagTypes) {
if (!childTags.contains(type))
return false;
}
return true;
}
void createChildMenu(const ScxmlTag *tag, QMenu *menu, bool addRemove)
{
QTC_ASSERT(tag, return);
initChildMenu(tag->tagType(), menu);
QVariantMap data;
data[Constants::C_SCXMLTAG_PARENTTAG] = tag->tagType();
data[Constants::C_SCXMLTAG_ACTIONTYPE] = AddChild;
if (tag->tagType() == UnknownTag) {
data[Constants::C_SCXMLTAG_TAGTYPE] = UnknownTag;
menu->addAction(ScxmlTag::tr("New Tag"))->setData(data);
} else if (tag->tagType() == Metadata) {
data[Constants::C_SCXMLTAG_TAGTYPE] = MetadataItem;
menu->addAction(ScxmlTag::tr("Item"))->setData(data);
} else {
data[Constants::C_SCXMLTAG_PARENTTAG] = Metadata;
data[Constants::C_SCXMLTAG_TAGTYPE] = MetadataItem;
menu->addAction(ScxmlTag::tr("Metadata"))->setData(data);
}
if (addRemove) {
menu->addSeparator();
data[Constants::C_SCXMLTAG_ACTIONTYPE] = Remove;
QAction *act = menu->addAction(ScxmlTag::tr("Remove"));
act->setData(data);
act->setEnabled(!tag->isRootTag());
}
}
QVector<TagType> allowedChildTypes(TagType tagType)
{
QVector<TagType> childTags;
switch (tagType) {
case Scxml:
childTags << DataModel;
childTags << Script;
childTags << Initial;
childTags << State;
childTags << Parallel;
childTags << Final;
break;
case State:
childTags << Initial;
childTags << Final;
case Parallel:
childTags << OnEntry;
childTags << OnExit;
childTags << Transition;
childTags << DataModel;
childTags << Invoke;
childTags << State;
childTags << Parallel;
childTags << History;
break;
case Initial:
case History:
childTags << Transition;
break;
case Final:
childTags << OnEntry;
childTags << OnExit;
childTags << Donedata;
break;
case If:
childTags << ElseIf;
childTags << Else;
case Transition:
case OnEntry:
case OnExit:
case ElseIf:
case Else:
case Foreach:
// Executable content
childTags << Raise;
childTags << Send;
childTags << Script;
childTags << Assign;
childTags << Cancel;
childTags << Log;
childTags << If;
childTags << Foreach;
break;
case DataModel:
childTags << Data;
break;
case Data:
// PENDING
break;
case Assign:
// PENDING
break;
case Content:
// PENDING
break;
case Script:
// PENDING
break;
case Invoke:
childTags << Finalize;
case Donedata:
case Send:
childTags << Param;
childTags << Content;
break;
default:
break;
}
return childTags;
}
QVector<TagType> childTypes(TagType tagType)
{
QVector<TagType> childTags;
switch (tagType) {
case Scxml:
childTags << DataModel;
childTags << Script;
break;
case State:
case Parallel:
childTags << OnEntry;
childTags << OnExit;
childTags << Transition;
childTags << DataModel;
childTags << Invoke;
break;
case Initial:
case History:
//childTags << Transition;
break;
case Final:
childTags << OnEntry;
childTags << OnExit;
childTags << Donedata;
break;
case If:
childTags << ElseIf;
childTags << Else;
case Transition:
case OnEntry:
case OnExit:
case ElseIf:
case Else:
case Foreach:
// Executable content
childTags << Raise;
childTags << Send;
childTags << Script;
childTags << Assign;
childTags << Cancel;
childTags << Log;
childTags << If;
childTags << Foreach;
break;
case DataModel:
childTags << Data;
break;
case Data:
// PENDING
break;
case Assign:
// PENDING
break;
case Content:
// PENDING
break;
case Script:
// PENDING
break;
case Invoke:
childTags << Finalize;
case Donedata:
case Send:
childTags << Param;
childTags << Content;
break;
default:
break;
}
return childTags;
}
void initChildMenu(TagType tagType, QMenu *menu)
{
menu->setTitle(QLatin1String(scxml_tags[tagType].name));
QVector<TagType> childTags = childTypes(tagType);
if (childTags.count() > 0) {
for (int i = 0; i < childTags.count(); ++i) {
if (childTags[i] == OnEntry || childTags[i] == OnExit)
initChildMenu(childTags[i], menu->addMenu(QLatin1String(scxml_tags[childTags[i]].name)));
else {
QVariantMap data;
data[Constants::C_SCXMLTAG_PARENTTAG] = tagType;
data[Constants::C_SCXMLTAG_TAGTYPE] = childTags[i];
data[Constants::C_SCXMLTAG_ACTIONTYPE] = AddChild;
menu->addAction(QLatin1String(scxml_tags[childTags[i]].name))->setData(data);
}
}
}
}
ScxmlTag *metadataTag(ScxmlTag *tag, const QString &key, bool blockUpdates)
{
QTC_ASSERT(tag, return nullptr);
ScxmlTag *info = nullptr;
ScxmlDocument *document = tag->document();
if (document) {
ScxmlTag *metaData = tag->child("qt:metadata");
if (!metaData) {
metaData = new ScxmlTag(Metadata, document);
if (!blockUpdates)
document->addTag(tag, metaData);
else
tag->appendChild(metaData);
}
info = metaData->child(QString::fromLatin1("qt:%1").arg(key));
if (!info) {
info = new ScxmlTag(Metadata, document);
info->setTagName(key);
if (!blockUpdates)
document->addTag(metaData, info);
else
metaData->appendChild(info);
}
}
return info;
}
ScxmlTag *findChild(const ScxmlTag *tag, TagType childType)
{
QTC_ASSERT(tag, return nullptr);
for (int i = 0; i < tag->childCount(); ++i) {
if (tag->child(i)->tagType() == childType)
return tag->child(i);
}
return nullptr;
}
void findAllChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children)
{
QTC_ASSERT(tag, return);
for (int i = 0; i < tag->childCount(); ++i) {
ScxmlTag *child = tag->child(i);
children << child;
findAllChildren(child, children);
}
}
void findAllTransitionChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children)
{
QTC_ASSERT(tag, return);
for (int i = 0; i < tag->childCount(); ++i) {
ScxmlTag *child = tag->child(i);
TagType t = child->tagType();
if (t == Transition || t == InitialTransition)
children << child;
else
findAllTransitionChildren(child, children);
}
}
void modifyPosition(ScxmlTag *tag, const QPointF &minPos, const QPointF &targetPos)
{
QTC_ASSERT(tag, return);
const QString sceneData = tag->editorInfo(Constants::C_SCXML_EDITORINFO_SCENEGEOMETRY);
const QString localData = tag->editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY);
Serializer s;
if (!localData.isEmpty() && !sceneData.isEmpty()) {
QPointF localPos, scenePos;
QRectF localRect, sceneRect;
s.setData(sceneData);
s.read(scenePos);
s.read(sceneRect);
s.clear();
s.setData(localData);
s.read(localPos);
s.read(localRect);
localPos = targetPos - localRect.topLeft() - (minPos - sceneRect.topLeft());
s.clear();
s.append(localPos);
s.append(localRect);
tag->document()->setEditorInfo(tag, Constants::C_SCXML_EDITORINFO_GEOMETRY, s.data());
} else {
s.append(targetPos);
if (tag->tagType() == State || tag->tagType() == Parallel)
s.append(QRectF(-60, -50, 120, 100));
else if (tag->tagType() == Initial || tag->tagType() == Final || tag->tagType() == History)
s.append(QRectF(-20, -20, 40, 40));
else
s.append(QRectF());
tag->document()->setEditorInfo(tag, Constants::C_SCXML_EDITORINFO_GEOMETRY, s.data());
}
}
} // namespace TagUtils
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "scxmltag.h"
#include <QMenu>
#include <QVector>
namespace ScxmlEditor {
namespace PluginInterface {
namespace TagUtils {
enum MenuAction {
AddChild = 0,
SetAsInitial,
Relayout,
ZoomToState,
RemovePoint,
Remove
};
bool checkPaste(const QString &copiedTagType, const ScxmlTag *currentTag);
QVector<TagType> allowedChildTypes(TagType tagType);
QVector<TagType> childTypes(TagType type);
void createChildMenu(const ScxmlTag *tag, QMenu *menu, bool addRemove = true);
void initChildMenu(TagType tagType, QMenu *menu);
ScxmlTag *findChild(const ScxmlTag *tag, TagType childType);
ScxmlTag *metadataTag(ScxmlTag *tag, const QString &tagname, bool blockUpdates = false);
void findAllChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children);
void findAllTransitionChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children);
void modifyPosition(ScxmlTag *tag, const QPointF &minPos, const QPointF &targetPos);
} // namespace TagUtils
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,449 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QVariant>
#include <QVector>
namespace ScxmlEditor {
namespace PluginInterface {
enum TagType {
UnknownTag = 0,
Metadata,
MetadataItem,
Scxml,
State,
Parallel,
Transition,
InitialTransition,
Initial,
Final,
OnEntry,
OnExit,
History,
Raise,
If,
ElseIf,
Else,
Foreach,
Log,
DataModel,
Data,
Assign,
Donedata,
Content,
Param,
Script,
Send,
Cancel,
Invoke,
Finalize
};
struct scxmltag_attribute_t;
struct scxmltag_type_t;
struct scxmltag_attribute_t
{
const char *name; // scxml attribute name
const char *value; // default value
bool required;
bool editable;
int datatype;
};
struct scxmltag_type_t
{
const char *name; // scxml output name
bool canIncludeContent;
const scxmltag_attribute_t *attributes;
int n_attributes;
};
// Define tag-attributes
const scxmltag_attribute_t scxml_scxml_attributes[] = {
{ "initial", nullptr, false, false, QVariant::String },
{ "name", nullptr, false, true, QVariant::String },
{ "xmlns", "http://www.w3.org/2005/07/scxml", true, false, QVariant::String },
{ "version", "1.0", true, false, QVariant::String },
{ "datamodel", nullptr, false, true, QVariant::String },
{ "binding", "early;late", false, true, QVariant::StringList }
};
const scxmltag_attribute_t scxml_state_attributes[] = {
{ "id", nullptr, false, true, QVariant::String },
{ "initial", nullptr, false, false, QVariant::String }
};
const scxmltag_attribute_t scxml_parallel_attributes[] = {
{ "id", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_transition_attributes[] = {
{ "event", nullptr, false, true, QVariant::String },
{ "cond", nullptr, false, true, QVariant::String },
{ "target", nullptr, false, true, QVariant::String },
{ "type", "internal;external", false, true, QVariant::StringList }
};
const scxmltag_attribute_t scxml_initialtransition_attributes[] = {
{ "target", nullptr, false, false, QVariant::String }
};
const scxmltag_attribute_t scxml_final_attributes[] = {
{ "id", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_history_attributes[] = {
{ "id", nullptr, false, true, QVariant::String },
{ "type", "shallow;deep", false, true, QVariant::StringList }
};
const scxmltag_attribute_t scxml_raise_attributes[] = {
{ "event", nullptr, true, true, QVariant::String }
};
const scxmltag_attribute_t scxml_if_attributes[] = {
{ "cond", nullptr, true, true, QVariant::String },
};
const scxmltag_attribute_t scxml_elseif_attributes[] = {
{ "cond", nullptr, true, true, QVariant::String }
};
const scxmltag_attribute_t scxml_foreach_attributes[] = {
{ "array", nullptr, true, true, QVariant::String },
{ "item", nullptr, true, true, QVariant::String },
{ "index", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_log_attributes[] = {
{ "label", "", false, true, QVariant::String },
{ "expr", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_data_attributes[] = {
{ "id", nullptr, true, true, QVariant::String },
{ "src", nullptr, false, true, QVariant::String },
{ "expr", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_assign_attributes[] = {
{ "location", nullptr, true, true, QVariant::String },
{ "expr", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_content_attributes[] = {
{ "expr", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_param_attributes[] = {
{ "name", nullptr, true, true, QVariant::String },
{ "expr", nullptr, false, true, QVariant::String },
{ "location", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_script_attributes[] = {
{ "src", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_send_attributes[] = {
{ "event", nullptr, false, true, QVariant::String },
{ "eventexpr", nullptr, false, true, QVariant::String },
{ "target", nullptr, false, true, QVariant::String },
{ "targetexpr", nullptr, false, true, QVariant::String },
{ "type", nullptr, false, true, QVariant::String },
{ "typeexpr", nullptr, false, true, QVariant::String },
{ "id", nullptr, false, true, QVariant::String },
{ "idiocation", nullptr, false, true, QVariant::String },
{ "delay", nullptr, false, true, QVariant::String },
{ "delayexpr", nullptr, false, true, QVariant::String },
{ "namelist", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_cancel_attributes[] = {
{ "sendid", nullptr, false, true, QVariant::String },
{ "sendidexpr", nullptr, false, true, QVariant::String }
};
const scxmltag_attribute_t scxml_invoke_attributes[] = {
{ "type", nullptr, false, true, QVariant::String },
{ "typeexpr", nullptr, false, true, QVariant::String },
{ "src", nullptr, false, true, QVariant::String },
{ "srcexpr", nullptr, false, true, QVariant::String },
{ "id", nullptr, false, true, QVariant::String },
{ "idiocation", nullptr, false, true, QVariant::String },
{ "namelist", nullptr, false, true, QVariant::String },
{ "autoforward", ";true;false", false, true, QVariant::StringList }
};
const scxmltag_type_t scxml_unknown = {
"unknown",
true,
nullptr,
0
};
const scxmltag_type_t scxml_metadata = {
"metadata",
true,
nullptr,
0
};
const scxmltag_type_t scxml_metadataitem = {
"item",
true,
nullptr,
0
};
const scxmltag_type_t scxml_scxml = {
"scxml",
false,
scxml_scxml_attributes,
sizeof(scxml_scxml_attributes) / sizeof(scxml_scxml_attributes[0])
};
const scxmltag_type_t scxml_state = {
"state",
false,
scxml_state_attributes,
sizeof(scxml_state_attributes) / sizeof(scxml_state_attributes[0])
};
const scxmltag_type_t scxml_parallel = {
"parallel",
false,
scxml_parallel_attributes,
sizeof(scxml_parallel_attributes) / sizeof(scxml_parallel_attributes[0])
};
const scxmltag_type_t scxml_transition = {
"transition",
false,
scxml_transition_attributes,
sizeof(scxml_transition_attributes) / sizeof(scxml_transition_attributes[0])
};
const scxmltag_type_t scxml_initialtransition = {
"transition",
false,
scxml_initialtransition_attributes,
sizeof(scxml_initialtransition_attributes) / sizeof(scxml_initialtransition_attributes[0])
};
const scxmltag_type_t scxml_initial = {
"initial",
false,
nullptr,
0
};
const scxmltag_type_t scxml_final = {
"final",
false,
scxml_final_attributes,
sizeof(scxml_final_attributes) / sizeof(scxml_final_attributes[0])
};
const scxmltag_type_t scxml_onentry = {
"onentry",
false,
nullptr,
0
};
const scxmltag_type_t scxml_onexit = {
"onexit",
false,
nullptr,
0
};
const scxmltag_type_t scxml_history = {
"history",
false,
scxml_history_attributes,
sizeof(scxml_history_attributes) / sizeof(scxml_history_attributes[0])
};
const scxmltag_type_t scxml_raise = {
"raise",
false,
scxml_raise_attributes,
sizeof(scxml_raise_attributes) / sizeof(scxml_raise_attributes[0])
};
const scxmltag_type_t scxml_if = {
"if",
false,
scxml_if_attributes,
sizeof(scxml_if_attributes) / sizeof(scxml_if_attributes[0])
};
const scxmltag_type_t scxml_elseif = {
"elseif",
false,
scxml_elseif_attributes,
sizeof(scxml_elseif_attributes) / sizeof(scxml_elseif_attributes[0])
};
const scxmltag_type_t scxml_else = {
"else",
false,
nullptr,
0
};
const scxmltag_type_t scxml_foreach = {
"foreach",
false,
scxml_foreach_attributes,
sizeof(scxml_foreach_attributes) / sizeof(scxml_foreach_attributes[0])
};
const scxmltag_type_t scxml_log = {
"log",
false,
scxml_log_attributes,
sizeof(scxml_log_attributes) / sizeof(scxml_log_attributes[0])
};
const scxmltag_type_t scxml_datamodel = {
"datamodel",
false,
nullptr,
0
};
const scxmltag_type_t scxml_data = {
"data",
false,
scxml_data_attributes,
sizeof(scxml_data_attributes) / sizeof(scxml_data_attributes[0])
};
const scxmltag_type_t scxml_assign = {
"assign",
false,
scxml_assign_attributes,
sizeof(scxml_assign_attributes) / sizeof(scxml_assign_attributes[0])
};
const scxmltag_type_t scxml_donedata = {
"donedata",
false,
nullptr,
0
};
const scxmltag_type_t scxml_content = {
"content",
false,
scxml_content_attributes,
sizeof(scxml_content_attributes) / sizeof(scxml_content_attributes[0])
};
const scxmltag_type_t scxml_param = {
"param",
false,
scxml_param_attributes,
sizeof(scxml_param_attributes) / sizeof(scxml_param_attributes[0])
};
const scxmltag_type_t scxml_script = {
"script",
true,
scxml_script_attributes,
sizeof(scxml_script_attributes) / sizeof(scxml_script_attributes[0])
};
const scxmltag_type_t scxml_send = {
"send",
false,
scxml_send_attributes,
sizeof(scxml_send_attributes) / sizeof(scxml_send_attributes[0])
};
const scxmltag_type_t scxml_cancel = {
"cancel",
false,
scxml_cancel_attributes,
sizeof(scxml_cancel_attributes) / sizeof(scxml_cancel_attributes[0])
};
const scxmltag_type_t scxml_invoke = {
"invoke",
false,
scxml_invoke_attributes,
sizeof(scxml_invoke_attributes) / sizeof(scxml_invoke_attributes[0])
};
const scxmltag_type_t scxml_finalize = {
"finalize",
false,
nullptr,
0
};
const scxmltag_type_t scxml_tags[] = {
scxml_unknown,
scxml_metadata,
scxml_metadataitem,
scxml_scxml,
scxml_state,
scxml_parallel,
scxml_transition,
scxml_initialtransition,
scxml_initial,
scxml_final,
scxml_onentry,
scxml_onexit,
scxml_history,
scxml_raise,
scxml_if,
scxml_elseif,
scxml_else,
scxml_foreach,
scxml_log,
scxml_datamodel,
scxml_data,
scxml_assign,
scxml_donedata,
scxml_content,
scxml_param,
scxml_script,
scxml_send,
scxml_cancel,
scxml_invoke,
scxml_finalize
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,126 @@
/****************************************************************************
**
** 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 "scxmluifactory.h"
#include "genericscxmlplugin.h"
#include "isceditor.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QGenericPlugin>
#include <QGenericPluginFactory>
#include <QPluginLoader>
using namespace ScxmlEditor::PluginInterface;
ScxmlUiFactory::ScxmlUiFactory(QObject *parent)
: QObject(parent)
{
initPlugins();
}
ScxmlUiFactory::~ScxmlUiFactory()
{
for (int i = m_plugins.count(); i--;)
m_plugins[i]->detach();
}
void ScxmlUiFactory::documentChanged(DocumentChangeType type, ScxmlDocument *doc)
{
for (int i = 0; i < m_plugins.count(); ++i)
m_plugins[i]->documentChanged(type, doc);
}
void ScxmlUiFactory::refresh()
{
for (int i = 0; i < m_plugins.count(); ++i)
m_plugins[i]->refresh();
}
QObject *ScxmlUiFactory::object(const QString &type) const
{
return m_objects.value(type, 0);
}
void ScxmlUiFactory::unregisterObject(const QString &type, QObject *obj)
{
if (obj && m_objects[type] == obj)
m_objects.take(type);
}
void ScxmlUiFactory::registerObject(const QString &type, QObject *obj)
{
if (obj)
m_objects[type] = obj;
}
bool ScxmlUiFactory::isActive(const QString &type, const QObject *obj) const
{
return obj && m_objects.value(type, 0) == obj;
}
void ScxmlUiFactory::initPlugins()
{
// First init general plugin
m_plugins << new GenericScxmlPlugin;
// Get additional plugins
QDir pluginDir(QCoreApplication::applicationDirPath() + QDir::separator() + "SCEPlugins");
QStringList nameFilters;
nameFilters << "*.dll" << "*.so";
foreach (QFileInfo dllFileInfo, pluginDir.entryInfoList(nameFilters)) {
QPluginLoader loader(dllFileInfo.absoluteFilePath());
loader.load();
if (!loader.isLoaded())
break;
auto plugin = qobject_cast<QGenericPlugin*>(loader.instance());
if (!plugin)
break;
QObject *instance = plugin->create(QString(), QString());
if (instance) {
auto scEditorInstance = qobject_cast<ISCEditor*>(instance);
if (scEditorInstance) {
qDebug() << tr("Created editor-instance.");
m_plugins << scEditorInstance;
} else {
qWarning() << tr("Editor-instance is not of the type ISCEditor.");
loader.unload();
}
}
}
// Last init plugins
for (int i = 0; i < m_plugins.count(); ++i)
m_plugins[i]->init(this);
}

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "mytypes.h"
#include <QObject>
namespace ScxmlEditor {
namespace PluginInterface {
class ISCEditor;
class ScxmlDocument;
class ScxmlUiFactory : public QObject
{
Q_OBJECT
public:
ScxmlUiFactory(QObject *parent = nullptr);
~ScxmlUiFactory() override;
// Inform document changes
void documentChanged(DocumentChangeType type, ScxmlDocument *doc);
// Inform plugins to refresh
void refresh();
// Get registered objects
QObject *object(const QString &type) const;
// Register objects
void unregisterObject(const QString &type, QObject *object);
void registerObject(const QString &type, QObject *object);
// Check is active
bool isActive(const QString &type, const QObject *object) const;
private:
void initPlugins();
QVector<ISCEditor*> m_plugins;
QMap<QString, QObject*> m_objects;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,121 @@
/****************************************************************************
**
** 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 "serializer.h"
using namespace ScxmlEditor::PluginInterface;
Serializer::Serializer()
: m_separator(";")
{
}
void Serializer::seek(int pos)
{
m_index = qBound(0, pos, m_data.count() - 1);
}
void Serializer::clear()
{
m_data.clear();
m_index = 0;
}
void Serializer::append(double d)
{
m_data.append(QString::fromLatin1("%1").arg(d, 0, 'f', 2).remove(".00"));
m_index = m_data.count() - 1;
}
QString Serializer::data() const
{
return m_data.join(m_separator);
}
void Serializer::append(const QPolygonF &d)
{
appendPolygon(d);
}
void Serializer::append(const QPolygon &d)
{
appendPolygon(d);
}
void Serializer::append(const QRectF &d)
{
appendRect(d);
}
void Serializer::append(const QRect &d)
{
appendRect(d);
}
void Serializer::append(const QPointF &d)
{
appendPoint(d);
}
void Serializer::append(const QPoint &d)
{
appendPoint(d);
}
void Serializer::read(QPolygonF &d)
{
readPolygon<QPointF>(d);
}
void Serializer::read(QPolygon &d)
{
readPolygon<QPoint>(d);
}
void Serializer::read(QRectF &d)
{
readRect(d);
}
void Serializer::read(QRect &d)
{
readRect(d);
}
void Serializer::read(QPointF &d)
{
readPoint(d);
}
void Serializer::read(QPoint &d)
{
readPoint(d);
}
void Serializer::setData(const QString &d)
{
m_data = d.split(m_separator, QString::SkipEmptyParts);
m_index = 0;
}
double Serializer::readNext()
{
double data = 0.0;
if (m_index >= 0 && m_index < m_data.count())
data = m_data[m_index].toDouble();
m_index++;
return data;
}

View File

@@ -0,0 +1,133 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QPoint>
#include <QPointF>
#include <QPolygon>
#include <QPolygonF>
#include <QRect>
#include <QRectF>
#include <QStringList>
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The Serializer class provides serialization of item-data to the string.
*
* For example items inside the graphicsview uses this class when they need to save ui-properties to the scxmltag-attribute.
*/
class Serializer
{
public:
Serializer();
void seek(int pos);
void clear();
void append(const QPolygonF &d);
void append(const QPolygon &d);
void append(const QRectF &d);
void append(const QRect &d);
void append(const QPointF &d);
void append(const QPoint &d);
void read(QPolygonF &d);
void read(QPolygon &d);
void read(QRectF &d);
void read(QRect &d);
void read(QPointF &d);
void read(QPoint &d);
void setSeparator(const QString &sep);
void setData(const QString &d);
QString data() const;
private:
void append(double d);
double readNext();
int m_index = 0;
const QString m_separator;
QStringList m_data;
template <class T, class P>
void readPolygon(P &d)
{
int count = (m_data.count() - m_index) / 2;
for (int i = 0; i < count; ++i) {
T p;
read(p);
d << p;
}
}
template <class T>
void readRect(T &d)
{
d.setLeft(readNext());
d.setTop(readNext());
d.setWidth(readNext());
d.setHeight(readNext());
}
template <class T>
void readPoint(T &d)
{
d.setX(readNext());
d.setY(readNext());
}
template <class T>
void appendPolygon(const T &d)
{
for (int i = 0; i < d.count(); ++i) {
append(d[i].x());
append(d[i].y());
}
}
template <class T>
void appendPoint(const T &d)
{
append(d.x());
append(d.y());
}
template <class T>
void appendRect(const T &d)
{
append(d.left());
append(d.top());
append(d.width());
append(d.height());
}
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,33 @@
/****************************************************************************
**
** 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 "shapeprovider.h"
using namespace ScxmlEditor::PluginInterface;
ShapeProvider::ShapeProvider(QObject *parent)
: QObject(parent)
{
}

View File

@@ -0,0 +1,85 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "scxmltag.h"
#include <QByteArray>
#include <QIcon>
#include <QObject>
#include <QString>
namespace ScxmlEditor {
namespace PluginInterface {
class ShapeProvider : public QObject
{
Q_OBJECT
public:
struct Shape
{
QString title;
QIcon icon;
QStringList filters;
QByteArray scxmlData;
QVariant userData;
};
struct ShapeGroup
{
~ShapeGroup()
{
qDeleteAll(shapes);
shapes.clear();
}
QString title;
QVector<Shape*> shapes;
void addShape(Shape *shape)
{
shapes << shape;
}
};
explicit ShapeProvider(QObject *parent = nullptr);
virtual int groupCount() const = 0;
virtual QString groupTitle(int groupIndex) const = 0;
virtual int shapeCount(int groupIndex) const = 0;
virtual QString shapeTitle(int groupIndex, int shapeIndex) const = 0;
virtual QIcon shapeIcon(int groupIndex, int shapeIndex) const = 0;
virtual bool canDrop(int groupIndex, int shapeIndex, ScxmlTag *parent) const = 0;
virtual QByteArray scxmlCode(int groupIndex, int shapeIndex, ScxmlTag *parent) const = 0;
signals:
void changed();
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,57 @@
/****************************************************************************
**
** 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 "snapline.h"
#include <QPen>
using namespace ScxmlEditor::PluginInterface;
SnapLine::SnapLine(QGraphicsItem *parent)
: QGraphicsLineItem(parent)
{
QPen pen;
pen.setBrush(QColor(0x22, 0xcc, 0x22));
pen.setStyle(Qt::DashLine);
setPen(pen);
setZValue(502);
m_visibilityTimer.setInterval(1000);
m_visibilityTimer.setSingleShot(true);
QObject::connect(&m_visibilityTimer, &QTimer::timeout, this, &SnapLine::hideLine);
hideLine();
}
void SnapLine::show(qreal x1, qreal y1, qreal x2, qreal y2)
{
setLine(x1, y1, x2, y2);
setVisible(true);
m_visibilityTimer.start();
}
void SnapLine::hideLine()
{
setVisible(false);
}

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QGraphicsLineItem>
#include <QTimer>
namespace ScxmlEditor {
namespace PluginInterface {
class SnapLine : public QObject, public QGraphicsLineItem
{
Q_OBJECT
public:
SnapLine(QGraphicsItem *parent = nullptr);
void show(qreal x1, qreal y1, qreal x2, qreal y2);
void hideLine();
private:
QTimer m_visibilityTimer;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,581 @@
/****************************************************************************
**
** 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 "stateitem.h"
#include "finalstateitem.h"
#include "graphicsitemprovider.h"
#include "graphicsscene.h"
#include "idwarningitem.h"
#include "imageprovider.h"
#include "initialstateitem.h"
#include "parallelitem.h"
#include "sceneutils.h"
#include "scxmleditorconstants.h"
#include "scxmltagutils.h"
#include "scxmluifactory.h"
#include "statewarningitem.h"
#include "textitem.h"
#include "transitionitem.h"
#include "utilsprovider.h"
#include <QBrush>
#include <QCoreApplication>
#include <QDebug>
#include <QPainter>
#include <QTextOption>
#include <QUndoStack>
#include <QtMath>
using namespace ScxmlEditor::PluginInterface;
const qreal TEXT_ITEM_HEIGHT = 20;
const qreal TEXT_ITEM_CAP = 10;
const qreal STATE_RADIUS = 10;
StateItem::StateItem(const QPointF &pos, BaseItem *parent)
: ConnectableItem(pos, parent)
{
m_stateNameItem = new TextItem(this);
m_stateNameItem->setParentItem(this);
checkWarningItems();
connect(m_stateNameItem, &TextItem::selected, this, [=](bool sel){
setItemSelected(sel);
});
connect(m_stateNameItem, &TextItem::textChanged, this, &StateItem::updateTextPositions);
connect(m_stateNameItem, &TextItem::textReady, this, &StateItem::titleHasChanged);
m_pen = QPen(QColor(0x45, 0x45, 0x45));
updateColors();
updatePolygon();
}
void StateItem::checkWarningItems()
{
ScxmlUiFactory *uifactory = uiFactory();
if (uifactory) {
auto provider = static_cast<GraphicsItemProvider*>(uifactory->object("graphicsItemProvider"));
if (provider) {
if (!m_idWarningItem)
m_idWarningItem = static_cast<IdWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_ID, this));
if (!m_stateWarningItem)
m_stateWarningItem = static_cast<StateWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_STATE, this));
if (m_idWarningItem && m_stateWarningItem)
m_stateWarningItem->setIdWarning(m_idWarningItem);
checkWarnings();
if (m_idWarningItem || m_stateWarningItem)
updateAttributes();
}
}
}
QVariant StateItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
QVariant retValue = ConnectableItem::itemChange(change, value);
switch (change) {
case QGraphicsItem::ItemSceneHasChanged:
checkWarningItems();
break;
default:
break;
}
return retValue;
}
void StateItem::updateAttributes()
{
if (tag()) {
ConnectableItem::updateAttributes();
// Check initial attribute
QString strNewId = tagValue("id", true);
if (!m_parallelState) {
QStringList NSIDs = strNewId.split(tag()->document()->nameSpaceDelimiter(), QString::SkipEmptyParts);
if (NSIDs.count() > 0) {
NSIDs[NSIDs.count() - 1] = m_stateNameItem->toPlainText();
QString strOldId = NSIDs.join(tag()->document()->nameSpaceDelimiter());
ScxmlTag *parentTag = tag()->parentTag();
if (parentTag && !strOldId.isEmpty() && parentTag->attribute("initial") == strOldId)
parentTag->setAttribute("initial", strNewId);
}
}
m_stateNameItem->setText(tagValue("id"));
if (m_idWarningItem)
m_idWarningItem->setId(strNewId);
updateTextPositions();
if (m_parallelState)
checkInitial(true);
}
updateBoundingRect();
}
void StateItem::updateEditorInfo(bool allChildren)
{
ConnectableItem::updateEditorInfo(allChildren);
QString color = editorInfo(Constants::C_SCXML_EDITORINFO_FONTCOLOR);
m_stateNameItem->setDefaultTextColor(color.isEmpty() ? QColor(Qt::black) : QColor(color));
// Update child too if necessary
if (allChildren) {
QList<QGraphicsItem*> children = childItems();
for (int i = 0; i < children.count(); ++i) {
if (children[i]->type() >= InitialStateType) {
auto child = qgraphicsitem_cast<BaseItem*>(children[i]);
if (child)
child->updateEditorInfo(allChildren);
}
}
}
}
void StateItem::updateColors()
{
updateDepth();
m_parallelState = parentItem() && parentItem()->type() == ParallelType;
if (m_parallelState)
m_pen.setStyle(Qt::DashLine);
else
m_pen.setStyle(Qt::SolidLine);
// Update child color-indices too
QList<QGraphicsItem*> children = childItems();
for (int i = 0; i < children.count(); ++i) {
if (children[i]->type() >= StateType) {
auto child = qgraphicsitem_cast<StateItem*>(children[i]);
if (child)
child->updateColors();
}
}
update();
}
void StateItem::updateBoundingRect()
{
QRectF r2 = childItemsBoundingRect();
// Check if we need to increase parent boundingrect
if (!r2.isNull()) {
QRectF r = boundingRect();
QRectF r3 = r.united(r2);
if (r != r3) {
setItemBoundingRect(r3);
updateTransitions();
updateUIProperties();
checkOverlapping();
}
}
}
void StateItem::shrink()
{
QRectF trect;
foreach (TransitionItem *item, outputTransitions()) {
if (item->targetType() == TransitionItem::InternalSameTarget || item->targetType() == TransitionItem::InternalNoTarget) {
trect = trect.united(item->wholeBoundingRect());
}
}
QRectF r = boundingRect();
QRectF r2 = childItemsBoundingRect();
double minWidth = qMax(120.0, r2.width());
double minH = qMax((double)minHeight(), r2.height());
minWidth = qMax(minWidth, m_stateNameItem->boundingRect().width() + (double)WARNING_ITEM_SIZE + (double)TEXT_ITEM_CAP);
if (!m_backgroundImage.isNull()) {
minWidth = qMax(minWidth, (double)m_backgroundImage.width() + 3 * STATE_RADIUS + trect.width());
minH = qMax(minH, ((double)m_backgroundImage.height() + 3 * STATE_RADIUS + TEXT_ITEM_HEIGHT) / 0.94);
}
r2 = QRectF(0, 0, minWidth, minH);
r2.moveCenter(r.center());
// Check if we need to increase parent boundingrect
if (r != r2) {
setItemBoundingRect(r2);
updateTransitions();
updateUIProperties();
}
}
void StateItem::transitionsChanged()
{
QRectF rr = boundingRect();
QRectF rectInternalTransitions;
QVector<TransitionItem*> internalTransitions = outputTransitions();
foreach (TransitionItem *item, internalTransitions) {
if (item->targetType() <= TransitionItem::InternalNoTarget) {
QRectF br = mapFromItem(item, item->boundingRect()).boundingRect();
br.setLeft(rr.left() + 20);
br.setTop(br.top() + 4);
br.setWidth(br.width() + item->textWidth());
rectInternalTransitions = rectInternalTransitions.united(br);
}
}
m_transitionRect = rectInternalTransitions;
updateBoundingRect();
}
void StateItem::transitionCountChanged()
{
ConnectableItem::transitionsChanged();
checkWarnings();
transitionsChanged();
}
QRectF StateItem::childItemsBoundingRect() const
{
QRectF r;
QRectF rr = boundingRect();
QList<QGraphicsItem*> children = childItems();
for (int i = 0; i < children.count(); ++i) {
if (children[i]->type() >= InitialStateType) {
QRectF br = children[i]->boundingRect();
QPointF p = children[i]->pos() + br.topLeft();
br.moveTopLeft(p);
r = r.united(br);
}
}
if (m_transitionRect.isValid()) {
r.setLeft(r.left() - m_transitionRect.width());
r.setHeight(qMax(r.height(), m_transitionRect.height()));
r.moveBottom(qMax(r.bottom(), m_transitionRect.bottom()));
}
if (!r.isNull())
r.adjust(-20, -(rr.height() * 0.06 + 40), 20, 20);
return r;
}
void StateItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
ConnectableItem::mouseDoubleClickEvent(event);
shrink();
updateUIProperties();
}
void StateItem::createContextMenu(QMenu *menu)
{
QVariantMap data;
if (!m_parallelState) {
data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::SetAsInitial;
menu->addAction(tr("Set as Initial"))->setData(data);
}
data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::ZoomToState;
menu->addAction(tr("Zoom to State"))->setData(data);
if (type() == ParallelType) {
data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::Relayout;
menu->addAction(tr("Relayout"))->setData(data);
}
menu->addSeparator();
ConnectableItem::createContextMenu(menu);
}
void StateItem::selectedMenuAction(const QAction *action)
{
if (!action)
return;
const ScxmlTag *tag = this->tag();
if (tag) {
const QVariantMap data = action->data().toMap();
const int actionType = data.value(Constants::C_SCXMLTAG_ACTIONTYPE, -1).toInt();
ScxmlDocument *document = tag->document();
switch (actionType) {
case TagUtils::SetAsInitial: {
ScxmlTag *parentTag = tag->parentTag();
if (parentTag) {
document->undoStack()->beginMacro(tr("Change initial-state"));
ScxmlTag *initialTag = parentTag->child("initial");
if (initialTag) {
ScxmlTag *initialTransitionTag = initialTag->child("transition");
if (initialTransitionTag)
document->setValue(initialTransitionTag, "target", tag->attribute("id", true));
else {
auto newTransition = new ScxmlTag(Transition, document);
newTransition->setAttribute("target", tag->attribute("id", true));
document->addTag(initialTag, newTransition);
}
} else
document->setValue(parentTag, "initial", tag->attribute("id", true));
checkInitial(true);
document->undoStack()->endMacro();
}
break;
}
case TagUtils::Relayout: {
document->undoStack()->beginMacro(tr("Relayout"));
doLayout(depth());
document->undoStack()->endMacro();
break;
}
case TagUtils::ZoomToState: {
emit BaseItem::openToDifferentView(this);
break;
}
default:
ConnectableItem::selectedMenuAction(action);
break;
}
}
}
void StateItem::checkInitial(bool parent)
{
QList<QGraphicsItem*> items;
ScxmlTag *tag = nullptr;
if (parent) {
if (parentItem()) {
items = parentItem()->childItems();
if (parentBaseItem())
tag = parentBaseItem()->tag();
} else {
auto sc = static_cast<GraphicsScene*>(scene());
if (sc) {
sc->checkInitialState();
return;
}
}
} else {
items = childItems();
tag = this->tag();
}
if (items.count() > 0 && tag && uiFactory()) {
auto utilsProvider = static_cast<UtilsProvider*>(uiFactory()->object("utilsProvider"));
if (utilsProvider)
utilsProvider->checkInitialState(items, tag);
}
}
void StateItem::titleHasChanged(const QString &text)
{
QString strOldId = tagValue("id", true);
setTagValue("id", text);
// Check initial attribute
if (tag() && !m_parallelState) {
ScxmlTag *parentTag = tag()->parentTag();
if (!strOldId.isEmpty() && parentTag->attribute("initial") == strOldId)
parentTag->setAttribute("initial", tagValue("id", true));
}
}
void StateItem::updateTextPositions()
{
if (m_parallelState) {
m_stateNameItem->setPos(m_titleRect.x(), m_titleRect.top());
m_stateNameItem->setItalic(true);
} else {
m_stateNameItem->setPos(m_titleRect.center().x() - m_stateNameItem->boundingRect().width() / 2, m_titleRect.top());
m_stateNameItem->setItalic(false);
}
QPointF p(m_stateNameItem->pos().x() - WARNING_ITEM_SIZE, m_titleRect.center().y() - WARNING_ITEM_SIZE / 2);
if (m_idWarningItem)
m_idWarningItem->setPos(p);
if (m_stateWarningItem)
m_stateWarningItem->setPos(p);
}
void StateItem::updatePolygon()
{
m_drawingRect = boundingRect().adjusted(5, 5, -5, -5);
m_polygon.clear();
m_polygon << m_drawingRect.topLeft()
<< m_drawingRect.topRight()
<< m_drawingRect.bottomRight()
<< m_drawingRect.bottomLeft()
<< m_drawingRect.topLeft();
m_titleRect = QRectF(m_drawingRect.left(), m_drawingRect.top(), m_drawingRect.width(), TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
QFont f = m_stateNameItem->font();
f.setPixelSize(m_titleRect.height() * 0.65);
m_stateNameItem->setFont(f);
updateTextPositions();
}
bool StateItem::canStartTransition(ItemType /*type*/) const
{
return !m_parallelState;
}
void StateItem::connectToParent(BaseItem *parentItem)
{
ConnectableItem::connectToParent(parentItem);
updateTextPositions();
checkInitial(true);
}
void StateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
ConnectableItem::paint(painter, option, widget);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
// Set opacity and color
painter->setOpacity(getOpacity());
m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
painter->setPen(m_pen);
QColor stateColor(editorInfo(Constants::C_SCXML_EDITORINFO_STATECOLOR));
if (!stateColor.isValid())
stateColor = tag() ? tag()->document()->getColor(depth()) : QColor(0x12, 0x34, 0x56);
// Draw basic frame
QRectF r = boundingRect();
QLinearGradient grad(r.topLeft(), r.bottomLeft());
grad.setColorAt(0, stateColor.lighter(115));
grad.setColorAt(1, stateColor);
painter->setBrush(QBrush(grad));
painter->drawRoundedRect(m_drawingRect, STATE_RADIUS, STATE_RADIUS);
// Background image
ScxmlUiFactory *uifactory = uiFactory();
if (uifactory) {
auto imageProvider = static_cast<ImageProvider*>(uifactory->object("imageProvider"));
if (imageProvider) {
const QImage *image = imageProvider->backgroundImage(tag());
if (image) {
int w = m_drawingRect.width() - 2 * STATE_RADIUS;
int h = m_drawingRect.height() - m_titleRect.height() - 2 * STATE_RADIUS;
int left = m_drawingRect.left() + STATE_RADIUS;
int top = m_drawingRect.top() + m_titleRect.height() + STATE_RADIUS;
if (m_transitionRect.isValid()) {
w = m_drawingRect.right() - m_transitionRect.right() - 2 * STATE_RADIUS;
left = m_transitionRect.right() + STATE_RADIUS;
}
m_backgroundImage = image->scaled(w, h, Qt::KeepAspectRatio);
painter->drawImage(QPointF(left + (w - m_backgroundImage.width()) / 2, top + (h - m_backgroundImage.height()) / 2), m_backgroundImage);
} else
m_backgroundImage = QImage();
}
}
// Draw title rect
if (!m_parallelState) {
painter->drawLine(QLineF(m_titleRect.bottomLeft(), m_titleRect.bottomRight()));
if (m_initial) {
double size = m_titleRect.height() * 0.3;
painter->setBrush(QColor(0x4d, 0x4d, 0x4d));
painter->drawEllipse(QPointF(m_titleRect.left() + 2 * size, m_titleRect.center().y()), size, size);
}
}
// Transition part
if (m_transitionRect.isValid()) {
qreal tx = m_transitionRect.right();
painter->drawLine(QLineF(QPointF(tx, m_titleRect.bottom() + STATE_RADIUS), QPointF(tx, m_drawingRect.bottom() - STATE_RADIUS)));
}
painter->restore();
}
void StateItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, bool blockUpdates)
{
setBlockUpdates(blockUpdates);
ConnectableItem::init(tag, parentItem);
if (initChildren) {
for (int i = 0; i < tag->childCount(); ++i) {
ScxmlTag *child = tag->child(i);
ConnectableItem *newItem = SceneUtils::createItemByTagType(child->tagType(), QPointF());
if (newItem) {
newItem->init(child, this, initChildren, blockUpdates);
newItem->finalizeCreation();
}
}
}
if (blockUpdates)
setBlockUpdates(false);
}
QString StateItem::itemId() const
{
return m_stateNameItem ? m_stateNameItem->toPlainText() : QString();
}
void StateItem::doLayout(int d)
{
if (depth() != d)
return;
SceneUtils::layout(childItems());
updateBoundingRect();
shrink();
}
void StateItem::setInitial(bool initial)
{
m_initial = initial;
update();
checkWarnings();
}
void StateItem::checkWarnings()
{
if (m_idWarningItem)
m_idWarningItem->check();
if (m_stateWarningItem)
m_stateWarningItem->check();
if (parentItem() && parentItem()->type() == StateType)
qgraphicsitem_cast<StateItem*>(parentItem())->checkWarnings();
}
bool StateItem::isInitial() const
{
return m_initial;
}

View File

@@ -0,0 +1,105 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "connectableitem.h"
#include <QPen>
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
namespace ScxmlEditor {
namespace PluginInterface {
class TransitionItem;
class TextItem;
class IdWarningItem;
class StateWarningItem;
/**
* @brief The StateItem class represents the SCXML-State.
*/
class StateItem : public ConnectableItem
{
Q_OBJECT
public:
explicit StateItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
int type() const override
{
return StateType;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QString itemId() const;
void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false) override;
void updateBoundingRect();
void updateAttributes() override;
void updateEditorInfo(bool allChildren = false) override;
void updateColors() override;
virtual void doLayout(int d) override;
void shrink();
void setInitial(bool initial);
bool isInitial() const;
void checkWarnings() override;
void checkInitial(bool parent = false) override;
QRectF childItemsBoundingRect() const;
void connectToParent(BaseItem *parentItem) override;
protected:
void updatePolygon() override;
void transitionsChanged() override;
void transitionCountChanged() override;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
bool canStartTransition(ItemType type) const override;
void createContextMenu(QMenu *menu) override;
void selectedMenuAction(const QAction *action) override;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
void writeStateProperties(const QString &tag, QXmlStreamWriter &xml);
QRectF m_drawingRect;
QRectF m_titleRect;
QRectF m_transitionRect;
private:
void titleHasChanged(const QString &text);
void updateTextPositions();
void checkParentBoundingRect();
void checkWarningItems();
TextItem *m_stateNameItem;
StateWarningItem *m_stateWarningItem = nullptr;
IdWarningItem *m_idWarningItem = nullptr;
QPen m_pen;
bool m_initial = false;
bool m_parallelState = false;
QImage m_backgroundImage;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,74 @@
/****************************************************************************
**
** 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 "statewarningitem.h"
#include "idwarningitem.h"
#include <utils/utilsicons.h>
using namespace ScxmlEditor::PluginInterface;
StateWarningItem::StateWarningItem(StateItem *parent)
: WarningItem(parent)
{
setSeverity(OutputPane::Warning::InfoType);
setTypeName(tr("State"));
setDescription(tr("Draw some transitions to state."));
setPixmap(Utils::Icons::WARNING.pixmap());
setReason(tr("No input connection"));
}
void StateWarningItem::setIdWarning(IdWarningItem *idwarning)
{
m_idWarningItem = idwarning;
}
void StateWarningItem::check()
{
if (m_parentItem) {
if (m_idWarningItem && m_idWarningItem->isVisible())
setWarningActive(false);
else {
bool outputProblem = !m_parentItem->hasOutputTransitions(m_parentItem, true);
bool inputProblem = !m_parentItem->isInitial() && !m_parentItem->hasInputTransitions(m_parentItem, true);
if (outputProblem && inputProblem) {
setReason(tr("No input or output connections (%1)").arg(m_parentItem->itemId()));
setDescription(tr("Draw some transitions to or from state."));
setWarningActive(true);
} else if (outputProblem) {
setReason(tr("No output connections (%1)").arg(m_parentItem->itemId()));
setDescription(tr("Draw some transitions from state."));
setWarningActive(true);
} else if (inputProblem) {
setReason(tr("No input connections (%1)").arg(m_parentItem->itemId()));
setDescription(tr("Draw some transitions to state."));
setWarningActive(true);
} else
setWarningActive(false);
}
}
}

View File

@@ -0,0 +1,63 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "stateitem.h"
#include "warningitem.h"
#include <QPointer>
namespace ScxmlEditor {
namespace PluginInterface {
class IdWarningItem;
/**
* @brief The StateWarningItem class
*/
class StateWarningItem : public WarningItem
{
Q_OBJECT
public:
StateWarningItem(StateItem *parent = nullptr);
void setIdWarning(IdWarningItem *idwarning);
int type() const override
{
return StateWarningType;
}
void check() override;
private:
QPointer<IdWarningItem> m_idWarningItem;
StateItem *m_parentItem = nullptr;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,143 @@
/****************************************************************************
**
** 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 "tagtextitem.h"
#include <QGraphicsScene>
#include <QGraphicsSceneHoverEvent>
#include <QLabel>
#include <QPainter>
using namespace ScxmlEditor::PluginInterface;
TagTextItem::TagTextItem(QGraphicsItem *parent)
: QGraphicsObject(parent)
{
setFlag(ItemIsMovable, true);
setFlag(ItemIsFocusable, true);
setFlag(ItemIsSelectable, true);
m_textItem = new TextItem(this);
connect(m_textItem, &TextItem::textChanged, this, [=](){
emit textChanged();
});
connect(m_textItem, &TextItem::textReady, this, [=](const QString &text){
emit textReady(text);
});
connect(m_textItem, &TextItem::selected, this, [=](bool sel){
emit selected(sel);
});
setAcceptHoverEvents(true);
}
QRectF TagTextItem::boundingRect() const
{
return m_textItem->boundingRect().adjusted(-8, -8, 8, 8);
}
void TagTextItem::setText(const QString &text)
{
m_textItem->setText(text);
}
void TagTextItem::setDefaultTextColor(const QColor &col)
{
m_textItem->setDefaultTextColor(col);
}
bool TagTextItem::needIgnore(const QPointF sPos)
{
// 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(sPos)) {
if (item->type() == QuickTransitionType || (item->type() == CornerGrabberType && item->parentItem() != this))
return true;
}
return false;
}
void TagTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
{
if (needIgnore(e->scenePos())) {
e->ignore();
return;
}
setCursor(Qt::SizeAllCursor);
QGraphicsObject::hoverEnterEvent(e);
}
void TagTextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *e)
{
if (needIgnore(e->scenePos())) {
setCursor(Qt::ArrowCursor);
e->ignore();
return;
}
setCursor(Qt::SizeAllCursor);
QGraphicsObject::hoverEnterEvent(e);
}
void TagTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
{
setCursor(Qt::ArrowCursor);
QGraphicsObject::hoverLeaveEvent(e);
}
void TagTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
if (needIgnore(e->scenePos())) {
e->ignore();
return;
}
m_startPos = pos();
QGraphicsObject::mousePressEvent(e);
}
void TagTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
{
m_movePoint += (pos() - m_startPos);
emit movePointChanged();
QGraphicsObject::mouseReleaseEvent(e);
}
void TagTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(painter)
Q_UNUSED(option)
Q_UNUSED(widget)
}
void TagTextItem::resetMovePoint(const QPointF &point)
{
m_movePoint = point;
}
QPointF TagTextItem::movePoint() const
{
return m_movePoint;
}

View File

@@ -0,0 +1,82 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "textitem.h"
#include <QGraphicsObject>
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The TagTextItem class provides the movable and editable text-item.
*/
class TagTextItem : public QGraphicsObject
{
Q_OBJECT
public:
explicit TagTextItem(QGraphicsItem *parent = nullptr);
int type() const override
{
return TagTextType;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QRectF boundingRect() const override;
void setText(const QString &text);
void setDefaultTextColor(const QColor &col);
void resetMovePoint(const QPointF &p = QPointF(0, 0));
QPointF movePoint() const;
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
void hoverMoveEvent(QGraphicsSceneHoverEvent *e) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *e) override;
signals:
void textChanged();
void textReady(const QString &text);
void selected(bool sel);
void movePointChanged();
private:
bool needIgnore(const QPointF sPos);
QPointF m_movePoint;
QPointF m_startPos;
TextItem *m_textItem;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,194 @@
/****************************************************************************
**
** 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 "textitem.h"
#include <QCursor>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextOption>
#include <qmath.h>
using namespace ScxmlEditor::PluginInterface;
TextItem::TextItem(QGraphicsItem *parent)
: QGraphicsTextItem(parent)
{
init();
}
TextItem::TextItem(const QString &id, QGraphicsItem *parent)
: QGraphicsTextItem(id, parent)
{
init();
}
void TextItem::init()
{
setTextInteractionFlags(Qt::TextEditorInteraction);
setFlag(ItemIsSelectable, true);
setFlag(ItemIsFocusable, true);
QTextOption options;
options.setWrapMode(QTextOption::NoWrap);
options.setAlignment(Qt::AlignCenter);
document()->setDefaultTextOption(options);
connect(document(), &QTextDocument::contentsChanged, this, &TextItem::checkText);
QFont f = font();
f.setPixelSize(13);
setFont(f);
}
void TextItem::setItalic(bool ital)
{
QFont f = font();
f.setItalic(ital);
setFont(f);
}
void TextItem::checkText()
{
if (document()->textWidth() <= 40)
document()->setTextWidth(40);
else
document()->setTextWidth(-1);
emit textChanged();
}
void TextItem::setText(const QString &t)
{
blockSignals(true);
setPlainText(t);
blockSignals(false);
}
void TextItem::focusInEvent(QFocusEvent *event)
{
QGraphicsTextItem::focusInEvent(event);
emit selected(true);
}
void TextItem::focusOutEvent(QFocusEvent *event)
{
emit textReady(toPlainText());
QGraphicsTextItem::focusOutEvent(event);
}
void TextItem::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Escape:
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Tab:
event->accept();
clearFocus();
return;
default:
break;
}
QGraphicsTextItem::keyPressEvent(event);
}
bool TextItem::needIgnore(const QPointF sPos) const
{
// 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(sPos)) {
if (item->type() == QuickTransitionType || (item->type() == CornerGrabberType && item->parentItem() != this))
return true;
}
return false;
}
void TextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (needIgnore(event->scenePos())) {
event->ignore();
return;
}
QGraphicsTextItem::mousePressEvent(event);
setFocus();
}
void TextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (needIgnore(event->scenePos())) {
event->ignore();
return;
}
QGraphicsTextItem::mouseReleaseEvent(event);
setFocus();
}
void TextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (needIgnore(event->scenePos())) {
event->ignore();
return;
}
setFocus();
QGraphicsTextItem::mouseDoubleClickEvent(event);
emit selected(true);
}
void TextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
{
if (needIgnore(e->scenePos())) {
e->ignore();
return;
}
setCursor(Qt::IBeamCursor);
QGraphicsTextItem::hoverEnterEvent(e);
}
void TextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *e)
{
if (needIgnore(e->scenePos())) {
setCursor(Qt::ArrowCursor);
e->ignore();
return;
}
setCursor(Qt::IBeamCursor);
QGraphicsTextItem::hoverEnterEvent(e);
}
void TextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
{
setCursor(Qt::ArrowCursor);
QGraphicsTextItem::hoverLeaveEvent(e);
}

View File

@@ -0,0 +1,76 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
#include <QFocusEvent>
#include <QGraphicsTextItem>
#include <QKeyEvent>
namespace ScxmlEditor {
namespace PluginInterface {
class TextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
explicit TextItem(QGraphicsItem *parent = nullptr);
explicit TextItem(const QString &id, QGraphicsItem *parent = nullptr);
int type() const override
{
return TextType;
}
void setItalic(bool ital);
void setText(const QString &t);
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
void hoverMoveEvent(QGraphicsSceneHoverEvent *e) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
void keyPressEvent(QKeyEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
signals:
void textChanged();
void textReady(const QString &text);
void selected(bool sel);
private:
void checkText();
void init();
bool needIgnore(const QPointF sPos) const;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,181 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "transitionwarningitem.h"
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsTextItem>
#include <QKeyEvent>
#include <QPen>
#include <QPointF>
#include <QPolygon>
#include <QRectF>
#include <QVector>
namespace ScxmlEditor {
namespace PluginInterface {
class CornerGrabberItem;
class TagTextItem;
class ConnectableItem;
/**
* @brief The TransitionItem class provides the item to connect two Connectable-items.
*/
class TransitionItem : public BaseItem
{
Q_OBJECT
public:
explicit TransitionItem(BaseItem *parent = nullptr);
~TransitionItem() override;
enum TransitionTargetType {
InternalSameTarget = 0,
InternalNoTarget,
ExternalNoTarget,
ExternalTarget
};
enum TransitionPoint {
Start = 0,
End
};
/**
* @brief type - return tye type of the item.
*/
int type() const override
{
return TransitionType;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override;
void disconnectItem(ConnectableItem *item);
void setStartItem(ConnectableItem *item);
void setEndItem(ConnectableItem *item);
void startTransitionFrom(ConnectableItem *item, const QPointF &mouseScenePos);
void setEndPos(const QPointF &endPos, bool snap = true);
void connectToTopItem(const QPointF &pos, TransitionPoint p, ItemType targetType);
bool isStartItem(const ConnectableItem *item) const;
bool isEndItem(const ConnectableItem *item) const;
ConnectableItem *connectedItem(const ConnectableItem *other) const;
bool hasStartItem() const;
bool hasEndItem() const;
void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false) override;
void updateEditorInfo(bool allChilds = true) override;
void updateAttributes() override;
void updateTarget();
void finalizeCreation() override;
void checkVisibility(double scaleFactor) override;
bool containsScenePoint(const QPointF &p) const override;
void updateUIProperties() override;
void updateTargetType();
void setTag(ScxmlTag *tag) override;
qreal textWidth() const;
QRectF wholeBoundingRect() const;
TransitionTargetType targetType() const
{
return m_targetType;
}
void grabMouse(ItemType targetType);
void storeGeometry(bool block = false);
void storeValues(bool block = false);
void updateComponents();
void textItemPositionChanged();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void updateToolTip();
void readUISpecifiedProperties(const ScxmlTag *tag) override;
void checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e) override;
void createContextMenu(QMenu *menu) override;
void selectedMenuAction(const QAction *action) override;
private:
void textHasChanged(const QString &text);
void checkWarningItems();
void storeMovePoint(bool block = false);
void storeTargetFactors(bool block = false);
void updateZValue();
void removeUnnecessaryPoints();
void findEndItem();
void updateEventName();
void createGrabbers();
void removeGrabbers();
void updateGrabberPositions();
void removeTransition(TransitionPoint p);
void snapToAnyPoint(int index, const QPointF &newPoint, int diff = 8);
void snapPointToPoint(int index, const QPointF &newPoint, int diff = 8);
QPointF loadPoint(const QString &name);
void savePoint(const QPointF &p, const QString &name);
QPointF calculateTargetFactor(ConnectableItem *item, const QPointF &pos);
QPointF sceneTargetPoint(TransitionPoint p);
QPointF findIntersectionPoint(ConnectableItem *item, const QLineF &line, const QPointF &defaultPoint);
QVector<CornerGrabberItem*> m_cornerGrabbers;
CornerGrabberItem *m_selectedCornerGrabber = nullptr;
QPolygonF m_cornerPoints;
ConnectableItem *m_startItem = nullptr;
ConnectableItem *m_oldStartItem = nullptr;
ConnectableItem *m_endItem = nullptr;
QPolygonF m_arrow;
qreal m_arrowSize = 10;
qreal m_arrowAngle;
QPen m_pen;
QPen m_highlightPen;
bool m_lineSelected = false;
TagTextItem *m_eventTagItem;
TransitionWarningItem *m_warningItem = nullptr;
TransitionTargetType m_targetType = ExternalTarget;
bool m_movingFirstPoint = false;
bool m_movingLastPoint = false;
bool m_mouseGrabbed = false;
ItemType m_grabbedTargetType = UnknownType;
int m_selectedGrabberIndex = -1;
QPointF m_startTargetFactor;
QPointF m_endTargetFactor;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,53 @@
/****************************************************************************
**
** 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 "transitionwarningitem.h"
#include "transitionitem.h"
#include <utils/utilsicons.h>
using namespace ScxmlEditor::PluginInterface;
TransitionWarningItem::TransitionWarningItem(TransitionItem *parent)
: WarningItem(parent)
, m_parentItem(parent)
{
setSeverity(OutputPane::Warning::WarningType);
setTypeName(tr("Transition"));
setDescription(tr("Transitions should be connected."));
setPixmap(Utils::Icons::WARNING.pixmap());
}
void TransitionWarningItem::check()
{
if (m_parentItem) {
if (m_parentItem->targetType() == TransitionItem::ExternalNoTarget) {
setReason(tr("Not Connected (%1)").arg(m_parentItem->tagValue("event")));
setWarningActive(true);
} else
setWarningActive(false);
}
}

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "warningitem.h"
namespace ScxmlEditor {
namespace PluginInterface {
class TransitionItem;
/**
* @brief The TransitionWarningItem class provides the warning item for the check Transition connections.
*/
class TransitionWarningItem : public WarningItem
{
Q_OBJECT
public:
TransitionWarningItem(TransitionItem *parent = nullptr);
int type() const override
{
return TransitionWarningType;
}
void check() override;
private:
TransitionItem *m_parentItem;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,403 @@
/****************************************************************************
**
** 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 "undocommands.h"
using namespace ScxmlEditor::PluginInterface;
BaseUndoCommand::BaseUndoCommand(ScxmlDocument *doc, QUndoCommand *parent)
: QUndoCommand(parent)
, m_doc(doc)
{
}
void BaseUndoCommand::undo()
{
m_doc->setUndoRedoRunning(true);
doUndo();
m_doc->setUndoRedoRunning(false);
}
void BaseUndoCommand::redo()
{
m_doc->setUndoRedoRunning(!m_firstTime);
doRedo();
if (m_firstTime)
m_firstTime = false;
m_doc->setUndoRedoRunning(false);
}
// AddRemoveTagsBeginCommand
AddRemoveTagsBeginCommand::AddRemoveTagsBeginCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
{
}
void AddRemoveTagsBeginCommand::doUndo()
{
emit m_document->endTagChange(ScxmlDocument::TagRemoveTags, m_tag, m_tag->index());
}
void AddRemoveTagsBeginCommand::doRedo()
{
emit m_document->beginTagChange(ScxmlDocument::TagAddTags, m_tag, m_tag->index());
}
// AddRemoveTagsEndCommand
AddRemoveTagsEndCommand::AddRemoveTagsEndCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
{
}
void AddRemoveTagsEndCommand::doUndo()
{
emit m_document->beginTagChange(ScxmlDocument::TagRemoveTags, m_tag, m_tag->index());
}
void AddRemoveTagsEndCommand::doRedo()
{
emit m_document->endTagChange(ScxmlDocument::TagAddTags, m_tag, m_tag->index());
}
// AddRemoveTagCommand
AddRemoveTagCommand::AddRemoveTagCommand(ScxmlDocument *doc, ScxmlTag *parentTag, ScxmlTag *tag, ScxmlDocument::TagChange change, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
, m_parentTag(parentTag)
, m_change(change)
{
m_tag->setDocument(m_document);
}
AddRemoveTagCommand::~AddRemoveTagCommand()
{
// If type was ADD, then delete tag
if (m_change == ScxmlDocument::TagAddChild)
delete m_tag;
}
void AddRemoveTagCommand::doAction(bool add)
{
if (add) {
int r = m_parentTag->childIndex(m_tag);
if (r < 0)
r = m_parentTag->childCount();
emit m_document->beginTagChange(ScxmlDocument::TagAddChild, m_parentTag, QVariant(r));
m_parentTag->appendChild(m_tag);
emit m_document->endTagChange(ScxmlDocument::TagAddChild, m_parentTag, QVariant(r));
} else {
int r = m_parentTag->childIndex(m_tag);
if (r >= 0) {
emit m_document->beginTagChange(ScxmlDocument::TagRemoveChild, m_parentTag, QVariant(r));
m_parentTag->removeChild(m_tag);
emit m_document->endTagChange(ScxmlDocument::TagRemoveChild, m_parentTag, QVariant(r));
}
}
}
void AddRemoveTagCommand::doUndo()
{
doAction(m_change != ScxmlDocument::TagAddChild);
}
void AddRemoveTagCommand::doRedo()
{
doAction(m_change == ScxmlDocument::TagAddChild);
}
// SetAttributeCommand
SetAttributeCommand::SetAttributeCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
, m_key(key)
, m_value(value)
{
m_oldValue = m_tag->attribute(m_key);
}
void SetAttributeCommand::doAction(const QString &key, const QString &value)
{
emit m_document->beginTagChange(ScxmlDocument::TagAttributesChanged, m_tag, m_tag->attribute(key));
m_tag->setAttribute(key, value);
emit m_document->endTagChange(ScxmlDocument::TagAttributesChanged, m_tag, value);
}
int SetAttributeCommand::id() const
{
return (int)ScxmlDocument::TagAttributesChanged;
}
bool SetAttributeCommand::mergeWith(const QUndoCommand *other)
{
if (other->id() == id()) {
QString key = static_cast<const SetAttributeCommand*>(other)->m_key;
auto tag = static_cast<const SetAttributeCommand*>(other)->m_tag;
if (tag == m_tag && key == m_key) {
m_value = static_cast<const SetAttributeCommand*>(other)->m_value;
return true;
}
}
return false;
}
void SetAttributeCommand::doUndo()
{
doAction(m_key, m_oldValue);
}
void SetAttributeCommand::doRedo()
{
doAction(m_key, m_value);
}
// SetContentCommand
SetContentCommand::SetContentCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &content, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
, m_content(content)
{
m_oldContent = m_tag->content();
}
void SetContentCommand::doAction(const QString &content)
{
emit m_document->beginTagChange(ScxmlDocument::TagContentChanged, m_tag, m_tag->content());
m_tag->setContent(content);
emit m_document->endTagChange(ScxmlDocument::TagContentChanged, m_tag, content);
}
int SetContentCommand::id() const
{
return (int)ScxmlDocument::TagContentChanged;
}
bool SetContentCommand::mergeWith(const QUndoCommand *other)
{
if (other->id() == id()) {
auto tag = static_cast<const SetContentCommand*>(other)->m_tag;
if (tag == m_tag) {
m_content = static_cast<const SetContentCommand*>(other)->m_content;
return true;
}
}
return false;
}
void SetContentCommand::doUndo()
{
doAction(m_oldContent);
}
void SetContentCommand::doRedo()
{
doAction(m_content);
}
// SetEditorInfoCommand
SetEditorInfoCommand::SetEditorInfoCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
, m_key(key)
, m_value(value)
{
m_oldValue = m_tag->editorInfo(m_key);
}
void SetEditorInfoCommand::doAction(const QString &key, const QString &value)
{
emit m_document->beginTagChange(ScxmlDocument::TagEditorInfoChanged, m_tag, m_tag->editorInfo(key));
m_tag->setEditorInfo(key, value);
emit m_document->endTagChange(ScxmlDocument::TagEditorInfoChanged, m_tag, value);
}
int SetEditorInfoCommand::id() const
{
return (int)ScxmlDocument::TagEditorInfoChanged;
}
bool SetEditorInfoCommand::mergeWith(const QUndoCommand *other)
{
if (other->id() == id()) {
QString key = static_cast<const SetEditorInfoCommand*>(other)->m_key;
auto tag = static_cast<const SetEditorInfoCommand*>(other)->m_tag;
if (tag == m_tag && key == m_key) {
m_value = static_cast<const SetEditorInfoCommand*>(other)->m_value;
return true;
}
}
return false;
}
void SetEditorInfoCommand::doUndo()
{
doAction(m_key, m_oldValue);
}
void SetEditorInfoCommand::doRedo()
{
doAction(m_key, m_value);
}
// ChangeParentCommand
ChangeParentCommand::ChangeParentCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *newParentTag, int tagIndex, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
, m_newParentTag(newParentTag)
, m_tagIndex(tagIndex)
{
m_oldParentTag = m_tag->parentTag();
}
void ChangeParentCommand::doAction(ScxmlTag *oldParent, ScxmlTag *newParent)
{
emit m_document->beginTagChange(ScxmlDocument::TagChangeParent, m_tag, m_tag->index());
int r = oldParent->childIndex(m_tag);
emit m_document->beginTagChange(ScxmlDocument::TagChangeParentRemoveChild, oldParent, QVariant(r));
oldParent->removeChild(m_tag);
emit m_document->endTagChange(ScxmlDocument::TagChangeParentRemoveChild, oldParent, QVariant(r));
r = newParent->childCount();
emit m_document->beginTagChange(ScxmlDocument::TagChangeParentAddChild, newParent, QVariant(r));
newParent->insertChild(m_tagIndex, m_tag);
emit m_document->endTagChange(ScxmlDocument::TagChangeParentAddChild, newParent, QVariant(r));
emit m_document->endTagChange(ScxmlDocument::TagChangeParent, m_tag, m_tag->index());
}
void ChangeParentCommand::doUndo()
{
doAction(m_newParentTag, m_oldParentTag);
}
void ChangeParentCommand::doRedo()
{
doAction(m_oldParentTag, m_newParentTag);
}
ChangeOrderCommand::ChangeOrderCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *parentTag, int newPos, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_tag(tag)
, m_parentTag(parentTag)
, m_newPos(newPos)
{
m_oldPos = m_tag->index();
}
void ChangeOrderCommand::doAction(int newPos)
{
emit m_document->beginTagChange(ScxmlDocument::TagChangeOrder, m_tag, newPos);
m_parentTag->moveChild(m_tag->index(), newPos);
emit m_document->endTagChange(ScxmlDocument::TagChangeOrder, m_tag, m_tag->index());
}
void ChangeOrderCommand::doUndo()
{
doAction(m_oldPos);
}
void ChangeOrderCommand::doRedo()
{
doAction(m_newPos);
}
ChangeFullNameSpaceCommand::ChangeFullNameSpaceCommand(ScxmlDocument *doc, ScxmlTag *tag, bool state, QUndoCommand *parent)
: BaseUndoCommand(doc, parent)
, m_document(doc)
, m_rootTag(tag)
, m_newState(state)
{
m_oldState = !state;
}
void ChangeFullNameSpaceCommand::makeIdMap(ScxmlTag *tag, QHash<QString, QString> &map, bool use)
{
switch (tag->tagType()) {
case History:
case Final:
case State:
case Parallel: {
QString strId = tag->attribute("id");
QString strIdWithNS = QString::fromLatin1("%1%2").arg(tag->stateNameSpace()).arg(strId);
map[use ? strId : strIdWithNS] = use ? strIdWithNS : strId;
break;
}
default:
break;
}
foreach (ScxmlTag *child, tag->allChildren()) {
makeIdMap(child, map, use);
}
}
void ChangeFullNameSpaceCommand::updateNameSpace(ScxmlTag *tag, const QHash<QString, QString> &map)
{
QString name;
switch (tag->tagType()) {
case Scxml:
case State:
name = "initial";
break;
case Transition:
name = "target";
break;
default:
break;
}
if (!name.isEmpty()) {
QString attr = tag->attribute(name);
if (map.contains(attr))
tag->setAttribute(name, map[attr]);
}
foreach (ScxmlTag *child, tag->allChildren()) {
updateNameSpace(child, map);
}
}
void ChangeFullNameSpaceCommand::doAction(bool newState)
{
emit m_document->beginTagChange(ScxmlDocument::TagChangeFullNameSpace, m_rootTag, newState);
QHash<QString, QString> keyMap;
makeIdMap(m_rootTag, keyMap, newState);
updateNameSpace(m_rootTag, keyMap);
m_document->m_useFullNameSpace = newState;
emit m_document->endTagChange(ScxmlDocument::TagChangeFullNameSpace, m_rootTag, newState);
}
void ChangeFullNameSpaceCommand::doUndo()
{
doAction(m_oldState);
}
void ChangeFullNameSpaceCommand::doRedo()
{
doAction(m_newState);
}

View File

@@ -0,0 +1,232 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "scxmldocument.h"
#include "scxmltag.h"
#include <QUndoCommand>
namespace ScxmlEditor {
namespace PluginInterface {
class BaseUndoCommand : public QUndoCommand
{
public:
BaseUndoCommand(ScxmlDocument *doc, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
protected:
virtual void doUndo() = 0;
virtual void doRedo() = 0;
private:
ScxmlDocument *m_doc;
bool m_firstTime = true;
};
class AddRemoveTagsBeginCommand : public BaseUndoCommand
{
public:
AddRemoveTagsBeginCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
private:
ScxmlDocument *m_document;
ScxmlTag *m_tag;
};
class AddRemoveTagsEndCommand : public BaseUndoCommand
{
public:
AddRemoveTagsEndCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
private:
ScxmlDocument *m_document;
ScxmlTag *m_tag;
};
/**
* @brief The AddRemoveTagCommand class provides the undo-command to add/remove tag to/from SCXML-document.
*/
class AddRemoveTagCommand : public BaseUndoCommand
{
public:
AddRemoveTagCommand(ScxmlDocument *doc, ScxmlTag *parentTag, ScxmlTag *tag, ScxmlDocument::TagChange change, QUndoCommand *parent = nullptr);
~AddRemoveTagCommand() override;
void doUndo() override;
void doRedo() override;
private:
void doAction(bool add);
ScxmlDocument *m_document;
QPointer<ScxmlTag> m_tag;
QPointer<ScxmlTag> m_parentTag;
ScxmlDocument::TagChange m_change;
};
/**
* @brief The SetAttributeCommand class provides the undo-command to set attribute-value for the given ScxmlTag.
*/
class SetAttributeCommand : public BaseUndoCommand
{
public:
SetAttributeCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
bool mergeWith(const QUndoCommand *other) override;
int id() const override;
private:
void doAction(const QString &key, const QString &value);
ScxmlDocument *m_document;
QPointer<ScxmlTag> m_tag;
QString m_key;
QString m_value;
QString m_oldValue;
};
/**
* @brief The SetContentCommand class
*/
class SetContentCommand : public BaseUndoCommand
{
public:
SetContentCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &content, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
bool mergeWith(const QUndoCommand *other) override;
int id() const override;
private:
void doAction(const QString &content);
ScxmlDocument *m_document;
QPointer<ScxmlTag> m_tag;
QString m_content;
QString m_oldContent;
};
/**
* @brief The SetEditorInfoCommand class provides the undo-command to set editorinfo-value for the given ScxmlTag.
*/
class SetEditorInfoCommand : public BaseUndoCommand
{
public:
SetEditorInfoCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
bool mergeWith(const QUndoCommand *other) override;
int id() const override;
private:
void doAction(const QString &key, const QString &value);
ScxmlDocument *m_document;
QPointer<ScxmlTag> m_tag;
QString m_key;
QString m_value;
QString m_oldValue;
};
/**
* @brief The ChangeParentCommand class provides the undo-commant to change parent of the ScxmlTag.
*/
class ChangeParentCommand : public BaseUndoCommand
{
public:
ChangeParentCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *newParentTag, int tagIndex, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
private:
void doAction(ScxmlTag *oldParent, ScxmlTag *newParent);
ScxmlDocument *m_document;
QPointer<ScxmlTag> m_tag;
QPointer<ScxmlTag> m_newParentTag;
QPointer<ScxmlTag> m_oldParentTag;
int m_tagIndex;
};
/**
* @brief The ChangeOrderCommand class
*/
class ChangeOrderCommand : public BaseUndoCommand
{
public:
ChangeOrderCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *parentTag, int newPos, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
private:
void doAction(int newPos);
ScxmlDocument *m_document;
QPointer<ScxmlTag> m_tag;
QPointer<ScxmlTag> m_parentTag;
int m_oldPos;
int m_newPos;
};
/**
* @brief The ChangeNameSpaceCommand class
*/
class ChangeFullNameSpaceCommand : public BaseUndoCommand
{
public:
ChangeFullNameSpaceCommand(ScxmlDocument *doc, ScxmlTag *tag, bool state, QUndoCommand *parent = nullptr);
void doUndo() override;
void doRedo() override;
private:
void doAction(bool state);
void makeIdMap(ScxmlTag *tag, QHash<QString, QString> &map, bool use);
void updateNameSpace(ScxmlTag *tag, const QHash<QString, QString> &map);
ScxmlDocument *m_document;
QPointer<ScxmlTag> m_rootTag;
bool m_oldState;
bool m_newState;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,33 @@
/****************************************************************************
**
** 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 "utilsprovider.h"
using namespace ScxmlEditor::PluginInterface;
UtilsProvider::UtilsProvider(QObject *parent)
: QObject(parent)
{
}

View File

@@ -0,0 +1,49 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QGraphicsItem>
#include <QList>
#include <QObject>
namespace ScxmlEditor {
namespace PluginInterface {
class ScxmlTag;
class UtilsProvider : public QObject
{
Q_OBJECT
public:
explicit UtilsProvider(QObject *parent = nullptr);
virtual void checkInitialState(const QList<QGraphicsItem*> &items, ScxmlTag *tag) = 0;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,171 @@
/****************************************************************************
**
** 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 "warningitem.h"
#include "graphicsscene.h"
#include "warningmodel.h"
#include <utils/utilsicons.h>
#include <QPainter>
#include <QToolTip>
using namespace ScxmlEditor::PluginInterface;
WarningItem::WarningItem(QGraphicsItem *parent)
: QGraphicsObject(parent)
, m_parentItem(qgraphicsitem_cast<BaseItem*>(parent))
{
setPixmap(Utils::Icons::WARNING.pixmap());
auto sc = static_cast<GraphicsScene*>(scene());
if (sc) {
sc->addWarningItem(this);
m_warningModel = sc->warningModel();
connect(m_warningModel, &OutputPane::WarningModel::modelCleared, this, &WarningItem::check);
}
setWarningActive(false);
}
WarningItem::~WarningItem()
{
auto sc = static_cast<GraphicsScene*>(scene());
if (sc) {
sc->removeWarningItem(this);
delete m_warning;
m_warning = nullptr;
}
}
void WarningItem::check()
{
}
QRectF WarningItem::boundingRect() const
{
return QRectF(QPointF(), m_pixmap.size() * m_pixmap.devicePixelRatio());
}
void WarningItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
QToolTip::showText(e->screenPos(), toolTip(), 0);
QGraphicsObject::mousePressEvent(e);
}
QVariant WarningItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
switch (change) {
case ItemSceneHasChanged: {
auto sc = static_cast<GraphicsScene*>(scene());
if (sc) {
sc->addWarningItem(this);
m_warningModel = sc->warningModel();
connect(m_warningModel, &OutputPane::WarningModel::modelCleared, this, &WarningItem::check);
}
break;
}
case QGraphicsItem::ItemVisibleHasChanged: {
auto sc = static_cast<GraphicsScene*>(scene());
if (sc)
sc->warningVisibilityChanged(m_severity, this);
break;
}
default:
break;
}
return QGraphicsObject::itemChange(change, value);
}
void WarningItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
painter->drawPixmap(0, 0, m_pixmap);
}
void WarningItem::setReason(const QString &reason)
{
m_reason = reason;
if (m_warning)
m_warning->setReason(reason);
setToolTip(m_reason);
}
void WarningItem::setDescription(const QString &description)
{
m_description = description;
if (m_warning)
m_warning->setDescription(description);
}
void WarningItem::setSeverity(ScxmlEditor::OutputPane::Warning::Severity type)
{
m_severity = type;
if (m_warning)
m_warning->setSeverity(type);
}
void WarningItem::setTypeName(const QString &name)
{
m_typeName = name;
if (m_warning)
m_warning->setTypeName(name);
}
void WarningItem::setWarningActive(bool active)
{
if (active && m_warning == nullptr && m_warningModel) {
m_warning = m_warningModel->createWarning(m_severity, m_typeName, m_reason, m_description);
connect(m_warning, &OutputPane::Warning::dataChanged, this, &WarningItem::checkVisibility);
} else if (!active && m_warning) {
m_warning->deleteLater();
m_warning = nullptr;
}
checkVisibility();
}
void WarningItem::checkVisibility()
{
setVisible(m_warning && m_warning->isActive());
}
void WarningItem::setPixmap(const QPixmap &pixmap)
{
m_pixmap = pixmap.scaled(QSize(25, 25) * pixmap.devicePixelRatio());
}
ScxmlTag *WarningItem::tag() const
{
return m_parentItem ? m_parentItem->tag() : 0;
}
ScxmlEditor::OutputPane::Warning *WarningItem::warning() const
{
return m_warning;
}

View File

@@ -0,0 +1,85 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "baseitem.h"
#include "warning.h"
#include "warningmodel.h"
#include <QGraphicsObject>
#include <QGraphicsSceneMouseEvent>
#include <QPixmap>
#include <QPointer>
namespace ScxmlEditor {
namespace PluginInterface {
/**
* @brief The WarningItem class is a base class for the all scene-based Warning-items.
*
* When status changes, warning must inform it to scene. Scene will check the other warnings too.
*/
class WarningItem : public QGraphicsObject
{
Q_OBJECT
public:
explicit WarningItem(QGraphicsItem *parent = nullptr);
~WarningItem() override;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QRectF boundingRect() const override;
ScxmlTag *tag() const;
void setSeverity(OutputPane::Warning::Severity type);
void setTypeName(const QString &name);
void setDescription(const QString &description);
OutputPane::Warning *warning() const;
void setReason(const QString &reason);
void setPixmap(const QPixmap &pixmap);
void setWarningActive(bool active);
virtual void check();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
private:
void checkVisibility();
OutputPane::Warning::Severity m_severity = OutputPane::Warning::ErrorType;
QString m_typeName;
QString m_description;
QString m_reason;
QPixmap m_pixmap;
BaseItem *m_parentItem;
QPointer<OutputPane::Warning> m_warning;
QPointer<OutputPane::WarningModel> m_warningModel;
};
} // namespace PluginInterface
} // namespace ScxmlEditor

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <QObject>
namespace ScxmlEditor {
namespace OutputPane {
class WarningModel;
} // namespace OutputPane
namespace PluginInterface {
class ScxmlTag;
class WarningProvider : public QObject
{
Q_OBJECT
public:
explicit WarningProvider(QObject *parent = nullptr)
: QObject(parent)
{
}
virtual void init(OutputPane::WarningModel *model) = 0;
};
} // namespace PluginInterface
} // namespace ScxmlEditor