ui: bundling nodes in the graph view
This change bundles similar nodes in the graph view together. When the bundled node or it's aggregation edge are clicked, then the bundle is split. This change also adds an arrow to the aggregation edge based on the direction of it's edges.
This commit is contained in:
@@ -59,6 +59,8 @@ add_files(
|
||||
qt/view/graphElements/QtGraphNode.h
|
||||
qt/view/graphElements/QtGraphNodeAccess.cpp
|
||||
qt/view/graphElements/QtGraphNodeAccess.h
|
||||
qt/view/graphElements/QtGraphNodeBundle.cpp
|
||||
qt/view/graphElements/QtGraphNodeBundle.h
|
||||
qt/view/graphElements/QtGraphNodeData.cpp
|
||||
qt/view/graphElements/QtGraphNodeData.h
|
||||
qt/view/graphElements/QtGraphNodeExpandToggle.cpp
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "utility/messaging/type/MessageShowFile.h"
|
||||
#include "utility/messaging/type/MessageFocusIn.h"
|
||||
#include "utility/messaging/type/MessageFocusOut.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include "data/location/TokenLocation.h"
|
||||
#include "data/location/TokenLocationFile.h"
|
||||
@@ -121,15 +122,8 @@ void QtCodeArea::lineNumberAreaPaintEvent(QPaintEvent *event)
|
||||
|
||||
int QtCodeArea::lineNumberDigits() const
|
||||
{
|
||||
int digits = 1;
|
||||
int max = qMax(1, int(m_startLineNumber) + blockCount());
|
||||
|
||||
while (max >= 10)
|
||||
{
|
||||
max /= 10;
|
||||
digits++;
|
||||
}
|
||||
return digits;
|
||||
return utility::digits(max);
|
||||
}
|
||||
|
||||
int QtCodeArea::lineNumberAreaWidth() const
|
||||
|
||||
@@ -26,14 +26,18 @@ QtStraightLineItem::QtStraightLineItem(QGraphicsItem* parent)
|
||||
|
||||
m_number = new QGraphicsSimpleTextItem(this);
|
||||
m_number->setFont(font);
|
||||
|
||||
m_arrowLeft = new QGraphicsLineItem(this);
|
||||
m_arrowRight = new QGraphicsLineItem(this);
|
||||
}
|
||||
|
||||
QtStraightLineItem::~QtStraightLineItem()
|
||||
{
|
||||
}
|
||||
|
||||
void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style)
|
||||
{
|
||||
void QtStraightLineItem::updateLine(
|
||||
Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style, bool showArrow
|
||||
){
|
||||
prepareGeometryChange();
|
||||
|
||||
const Vec4i& o = ownerRect;
|
||||
@@ -42,7 +46,6 @@ void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int numbe
|
||||
Vec2f oc((o.x + o.z) / 2, (o.y + o.w) / 2);
|
||||
Vec2f tc((t.x + t.z) / 2, (t.y + t.w) / 2);
|
||||
|
||||
Vec2f mid;
|
||||
Vec2f op;
|
||||
Vec2f tp;
|
||||
|
||||
@@ -58,6 +61,11 @@ void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int numbe
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects)
|
||||
{
|
||||
op = oc;
|
||||
}
|
||||
|
||||
intersects = false;
|
||||
for (int i = 0; !intersects && i < 4; i++)
|
||||
{
|
||||
@@ -70,12 +78,41 @@ void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int numbe
|
||||
}
|
||||
}
|
||||
|
||||
mid = (op + tp) / 2;
|
||||
if (!intersects)
|
||||
{
|
||||
tp = tc;
|
||||
}
|
||||
|
||||
this->setLine(oc.x, oc.y, tc.x, tc.y);
|
||||
|
||||
Vec2f mid = (op + tp) / 2;
|
||||
|
||||
if (showArrow)
|
||||
{
|
||||
Vec2f unit = (tp - op).normalize();
|
||||
Vec2f nUnit(-unit.y, unit.x);
|
||||
Vec2f arrow = mid + unit * 22;
|
||||
|
||||
Vec2f arrowSide = mid + unit * 15 + nUnit * 7;
|
||||
m_arrowRight->setLine(arrow.x, arrow.y, arrowSide.x, arrowSide.y);
|
||||
|
||||
arrowSide -= nUnit * 14;
|
||||
m_arrowLeft->setLine(arrow.x, arrow.y, arrowSide.x, arrowSide.y);
|
||||
|
||||
m_arrowLeft->setPen(QPen(QBrush(QColor("#E0E0E0")), 2, Qt::SolidLine, Qt::RoundCap));
|
||||
m_arrowRight->setPen(QPen(QBrush(QColor("#E0E0E0")), 2, Qt::SolidLine, Qt::RoundCap));
|
||||
|
||||
m_arrowLeft->show();
|
||||
m_arrowRight->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arrowLeft->hide();
|
||||
m_arrowRight->hide();
|
||||
}
|
||||
|
||||
m_circle->setRect(mid.x - 10, mid.y - 10, 20, 20);
|
||||
m_circle->setPen(QPen(QColor(style.color.c_str()), 2));
|
||||
m_circle->setPen(QPen(QColor("#E0E0E0"), 2));
|
||||
|
||||
QString numberStr = QString::number(number);
|
||||
m_number->setText(numberStr);
|
||||
|
||||
@@ -16,11 +16,14 @@ public:
|
||||
QtStraightLineItem(QGraphicsItem* parent);
|
||||
virtual ~QtStraightLineItem();
|
||||
|
||||
void updateLine(Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style);
|
||||
void updateLine(Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style, bool showArrow);
|
||||
|
||||
private:
|
||||
QtRoundedRectItem* m_circle;
|
||||
QGraphicsSimpleTextItem* m_number;
|
||||
|
||||
QGraphicsLineItem* m_arrowLeft;
|
||||
QGraphicsLineItem* m_arrowRight;
|
||||
};
|
||||
|
||||
#endif // QT_STRAIGHT_LINE_ITEM_H
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
#include "qt/view/graphElements/nodeComponents/QtGraphNodeComponentClickable.h"
|
||||
#include "qt/view/graphElements/nodeComponents/QtGraphNodeComponentMoveable.h"
|
||||
#include "qt/view/graphElements/QtGraphEdge.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeData.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeAccess.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeBundle.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeData.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeExpandToggle.h"
|
||||
|
||||
QtGraphView::QtGraphView(ViewLayout* viewLayout)
|
||||
@@ -275,6 +276,10 @@ std::shared_ptr<QtGraphNode> QtGraphView::createNodeRecursive(
|
||||
{
|
||||
newNode = std::make_shared<QtGraphNodeExpandToggle>(node.isExpanded(), node.invisibleSubNodeCount);
|
||||
}
|
||||
else if (node.isBundleNode())
|
||||
{
|
||||
newNode = std::make_shared<QtGraphNodeBundle>(node.tokenId, node.bundledNodes.size(), node.name);
|
||||
}
|
||||
|
||||
newNode->setPosition(node.position);
|
||||
newNode->setSize(node.size);
|
||||
@@ -319,8 +324,9 @@ std::shared_ptr<QtGraphEdge> QtGraphView::createEdge(QGraphicsView* view, const
|
||||
|
||||
if (owner != NULL && target != NULL)
|
||||
{
|
||||
std::shared_ptr<QtGraphEdge> qtEdge = std::make_shared<QtGraphEdge>(owner, target, edge.data);
|
||||
std::shared_ptr<QtGraphEdge> qtEdge = std::make_shared<QtGraphEdge>(owner, target, edge.data, edge.getWeight());
|
||||
qtEdge->setIsActive(edge.active);
|
||||
qtEdge->setDirection(edge.getDirection());
|
||||
|
||||
owner->addOutEdge(qtEdge);
|
||||
target->addInEdge(qtEdge);
|
||||
@@ -331,7 +337,7 @@ std::shared_ptr<QtGraphEdge> QtGraphView::createEdge(QGraphicsView* view, const
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING_STREAM(<< "Couldn't find owner or target node for edge: " << edge.data->getName());
|
||||
LOG_WARNING_STREAM(<< "Couldn't find owner or target node for edge: " << (edge.data ? edge.data->getName() : "<no data>"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -364,11 +370,12 @@ void QtGraphView::compareNodesRecursive(
|
||||
|
||||
for (std::list<std::shared_ptr<QtGraphNode>>::iterator it2 = oldSubNodes.begin(); it2 != oldSubNodes.end(); it2++)
|
||||
{
|
||||
if (((*it)->getTokenId() && (*it)->getTokenId() == (*it2)->getTokenId()) ||
|
||||
if (((*it)->isDataNode() && (*it2)->isDataNode() && (*it)->getTokenId() == (*it2)->getTokenId()) ||
|
||||
((*it)->isAccessNode() && (*it2)->isAccessNode() &&
|
||||
dynamic_cast<QtGraphNodeAccess*>((*it).get())->getAccessType() ==
|
||||
dynamic_cast<QtGraphNodeAccess*>((*it2).get())->getAccessType()) ||
|
||||
((*it)->isExpandToggleNode() && (*it2)->isExpandToggleNode()))
|
||||
((*it)->isExpandToggleNode() && (*it2)->isExpandToggleNode()) ||
|
||||
((*it)->isBundleNode() && (*it2)->isBundleNode() && (*it)->getTokenId() == (*it2)->getTokenId()))
|
||||
{
|
||||
remainingNodes->push_back(std::pair<QtGraphNode*, QtGraphNode*>((*it).get(), (*it2).get()));
|
||||
compareNodesRecursive((*it)->getSubNodes(), (*it2)->getSubNodes(), appearingNodes, vanishingNodes, remainingNodes);
|
||||
@@ -545,14 +552,15 @@ void QtGraphView::focusToken(Id tokenId)
|
||||
void QtGraphView::doFocusIn(Id tokenId)
|
||||
{
|
||||
std::shared_ptr<QtGraphNode> node = findNodeRecursive(m_oldNodes, tokenId);
|
||||
if(node)
|
||||
if (node && node->isDataNode())
|
||||
{
|
||||
node->focusIn();
|
||||
return;
|
||||
}
|
||||
for(std::shared_ptr<QtGraphEdge> edge : m_oldEdges)
|
||||
|
||||
for (std::shared_ptr<QtGraphEdge> edge : m_oldEdges)
|
||||
{
|
||||
if(edge->getData()->getId() == tokenId)
|
||||
if (edge->getData() && edge->getData()->getId() == tokenId)
|
||||
{
|
||||
edge->focusIn();
|
||||
return;
|
||||
@@ -568,17 +576,18 @@ void QtGraphView::defocusToken(Id tokenId)
|
||||
void QtGraphView::doFocusOut(Id tokenId)
|
||||
{
|
||||
std::shared_ptr<QtGraphNode> node = findNodeRecursive(m_oldNodes, tokenId);
|
||||
if(node)
|
||||
if (node && node->isDataNode())
|
||||
{
|
||||
node->focusOut();
|
||||
return;
|
||||
}
|
||||
for(std::shared_ptr<QtGraphEdge> edge : m_oldEdges)
|
||||
|
||||
for (std::shared_ptr<QtGraphEdge> edge : m_oldEdges)
|
||||
{
|
||||
if(edge->getData()->getId() == tokenId)
|
||||
if (edge->getData() && edge->getData()->getId() == tokenId)
|
||||
{
|
||||
edge->focusOut();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "utility/messaging/type/MessageActivateTokens.h"
|
||||
#include "utility/messaging/type/MessageFocusIn.h"
|
||||
#include "utility/messaging/type/MessageFocusOut.h"
|
||||
#include "utility/messaging/type/MessageGraphNodeBundleSplit.h"
|
||||
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
#include "data/graph/Edge.h"
|
||||
@@ -13,12 +14,16 @@
|
||||
#include "qt/graphics/QtStraightLineItem.h"
|
||||
#include "qt/view/graphElements/QtGraphNode.h"
|
||||
|
||||
QtGraphEdge::QtGraphEdge(const std::weak_ptr<QtGraphNode>& owner, const std::weak_ptr<QtGraphNode>& target, const Edge* data)
|
||||
QtGraphEdge::QtGraphEdge(
|
||||
const std::weak_ptr<QtGraphNode>& owner, const std::weak_ptr<QtGraphNode>& target, const Edge* data, size_t weight
|
||||
)
|
||||
: m_data(data)
|
||||
, m_owner(owner)
|
||||
, m_target(target)
|
||||
, m_child(nullptr)
|
||||
, m_isActive(false)
|
||||
, m_weight(weight)
|
||||
, m_direction(TokenComponentAggregation::DIRECTION_NONE)
|
||||
, m_mousePos(0.0f, 0.0f)
|
||||
, m_mouseMoved(false)
|
||||
{
|
||||
@@ -55,7 +60,17 @@ void QtGraphEdge::updateLine()
|
||||
return;
|
||||
}
|
||||
|
||||
GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(getData()->getType(), m_isActive, false);
|
||||
Edge::EdgeType type;
|
||||
if (getData())
|
||||
{
|
||||
type = getData()->getType();
|
||||
}
|
||||
else
|
||||
{
|
||||
type = Edge::EDGE_AGGREGATION;
|
||||
}
|
||||
|
||||
GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(type, m_isActive, false);
|
||||
|
||||
if (style.isStraight)
|
||||
{
|
||||
@@ -64,14 +79,15 @@ void QtGraphEdge::updateLine()
|
||||
m_child = new QtStraightLineItem(this);
|
||||
}
|
||||
|
||||
int number = 0;
|
||||
if (getData()->isType(Edge::EDGE_AGGREGATION))
|
||||
bool showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE;
|
||||
|
||||
if (m_direction == TokenComponentAggregation::DIRECTION_BACKWARD)
|
||||
{
|
||||
number = getData()->getComponent<TokenComponentAggregation>()->getAggregationCount();
|
||||
owner.swap(target);
|
||||
}
|
||||
|
||||
dynamic_cast<QtStraightLineItem*>(m_child)->updateLine(
|
||||
owner->getBoundingRect(), target->getBoundingRect(), number, style);
|
||||
owner->getBoundingRect(), target->getBoundingRect(), m_weight, style, showArrow);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -103,12 +119,14 @@ void QtGraphEdge::setIsActive(bool isActive)
|
||||
|
||||
void QtGraphEdge::onClick()
|
||||
{
|
||||
if (getData()->isType(Edge::EDGE_AGGREGATION))
|
||||
if (!getData())
|
||||
{
|
||||
MessageGraphNodeBundleSplit(m_target.lock()->getTokenId()).dispatch();
|
||||
}
|
||||
else if (getData()->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
const std::set<Id>& ids = getData()->getComponent<TokenComponentAggregation>()->getAggregationIds();
|
||||
MessageActivateTokens message(std::vector<Id>(ids.begin(), ids.end()));
|
||||
message.isAggregation = true;
|
||||
message.dispatch();
|
||||
MessageActivateTokens(std::vector<Id>(ids.begin(), ids.end())).dispatch();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -156,10 +174,31 @@ void QtGraphEdge::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
|
||||
|
||||
void QtGraphEdge::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
|
||||
{
|
||||
if (!getData())
|
||||
{
|
||||
focusIn();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageFocusIn(getData()->getId()).dispatch();
|
||||
}
|
||||
|
||||
void QtGraphEdge::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
|
||||
{
|
||||
if (!getData())
|
||||
{
|
||||
focusOut();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageFocusOut(getData()->getId()).dispatch();
|
||||
}
|
||||
|
||||
void QtGraphEdge::setDirection(TokenComponentAggregation::Direction direction)
|
||||
{
|
||||
if (m_direction != direction)
|
||||
{
|
||||
m_direction = direction;
|
||||
updateLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "utility/math/Vector2.h"
|
||||
|
||||
#include "data/graph/token_component/TokenComponentAggregation.h"
|
||||
|
||||
class Edge;
|
||||
class QtGraphNode;
|
||||
|
||||
@@ -18,15 +20,15 @@ class QtGraphEdge
|
||||
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)
|
||||
|
||||
public:
|
||||
QtGraphEdge(const std::weak_ptr<QtGraphNode>& owner, const std::weak_ptr<QtGraphNode>& target, const Edge* data);
|
||||
QtGraphEdge(const std::weak_ptr<QtGraphNode>& owner, const std::weak_ptr<QtGraphNode>& target, const Edge* data, size_t weight);
|
||||
virtual ~QtGraphEdge();
|
||||
|
||||
const Edge* getData() const;
|
||||
|
||||
virtual std::weak_ptr<QtGraphNode> getOwner();
|
||||
virtual std::weak_ptr<QtGraphNode> getTarget();
|
||||
std::weak_ptr<QtGraphNode> getOwner();
|
||||
std::weak_ptr<QtGraphNode> getTarget();
|
||||
|
||||
virtual void updateLine();
|
||||
void updateLine();
|
||||
|
||||
bool getIsActive() const;
|
||||
void setIsActive(bool isActive);
|
||||
@@ -36,6 +38,8 @@ public:
|
||||
void focusIn();
|
||||
void focusOut();
|
||||
|
||||
void setDirection(TokenComponentAggregation::Direction direction);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
|
||||
@@ -53,6 +57,9 @@ private:
|
||||
QGraphicsLineItem* m_child;
|
||||
|
||||
bool m_isActive;
|
||||
size_t m_weight;
|
||||
|
||||
TokenComponentAggregation::Direction m_direction;
|
||||
|
||||
Vec2i m_mousePos;
|
||||
bool m_mouseMoved;
|
||||
|
||||
@@ -241,6 +241,11 @@ bool QtGraphNode::isExpandToggleNode() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QtGraphNode::isBundleNode() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Id QtGraphNode::getTokenId() const
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -73,6 +73,7 @@ public:
|
||||
virtual bool isDataNode() const;
|
||||
virtual bool isAccessNode() const;
|
||||
virtual bool isExpandToggleNode() const;
|
||||
virtual bool isBundleNode() const;
|
||||
|
||||
virtual Id getTokenId() const;
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "qt/view/graphElements/QtGraphNodeBundle.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <QBrush>
|
||||
#include <QPen>
|
||||
|
||||
#include "utility/messaging/type/MessageGraphNodeBundleSplit.h"
|
||||
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
#include "qt/graphics/QtRoundedRectItem.h"
|
||||
|
||||
QtGraphNodeBundle::QtGraphNodeBundle(Id tokenId, size_t nodeCount, std::string name)
|
||||
: QtGraphNode()
|
||||
, m_tokenId(tokenId)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << nodeCount << " " << name;
|
||||
this->setName(ss.str());
|
||||
|
||||
this->setAcceptHoverEvents(true);
|
||||
}
|
||||
|
||||
QtGraphNodeBundle::~QtGraphNodeBundle()
|
||||
{
|
||||
}
|
||||
|
||||
bool QtGraphNodeBundle::isBundleNode() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Id QtGraphNodeBundle::getTokenId() const
|
||||
{
|
||||
return m_tokenId;
|
||||
}
|
||||
|
||||
void QtGraphNodeBundle::onClick()
|
||||
{
|
||||
MessageGraphNodeBundleSplit(m_tokenId).dispatch();
|
||||
}
|
||||
|
||||
void QtGraphNodeBundle::updateStyle()
|
||||
{
|
||||
GraphViewStyle::NodeStyle style = GraphViewStyle::getStyleOfBundleNode(m_isHovering);
|
||||
setStyle(style);
|
||||
|
||||
if (!m_undefinedRect)
|
||||
{
|
||||
m_undefinedRect = new QtRoundedRectItem(this);
|
||||
setSize(getSize());
|
||||
m_undefinedRect->moveBy(7, 7);
|
||||
}
|
||||
|
||||
m_undefinedRect->setPen(m_rect->pen());
|
||||
m_undefinedRect->setBrush(m_rect->brush());
|
||||
m_undefinedRect->setRadius(style.cornerRadius);
|
||||
m_undefinedRect->setZValue(-1);
|
||||
m_rect->setZValue(0);
|
||||
}
|
||||
|
||||
|
||||
void QtGraphNodeBundle::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
|
||||
{
|
||||
focusIn();
|
||||
}
|
||||
|
||||
void QtGraphNodeBundle::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
|
||||
{
|
||||
focusOut();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef QT_GRAPH_NODE_BUNDLE_H
|
||||
#define QT_GRAPH_NODE_BUNDLE_H
|
||||
|
||||
#include "qt/view/graphElements/QtGraphNode.h"
|
||||
|
||||
class QtGraphNodeBundle
|
||||
: public QtGraphNode
|
||||
{
|
||||
public:
|
||||
QtGraphNodeBundle(Id tokenId, size_t nodeCount, std::string name);
|
||||
virtual ~QtGraphNodeBundle();
|
||||
|
||||
// QtGraphNode implementation
|
||||
virtual bool isBundleNode() const;
|
||||
virtual Id getTokenId() const;
|
||||
|
||||
virtual void onClick();
|
||||
|
||||
virtual void updateStyle();
|
||||
|
||||
protected:
|
||||
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event);
|
||||
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
|
||||
|
||||
private:
|
||||
Id m_tokenId;
|
||||
};
|
||||
|
||||
#endif // QT_GRAPH_NODE_BUNDLE_H
|
||||
Reference in New Issue
Block a user