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:
@@ -121,3 +121,41 @@ public:
|
||||
|
||||
Basket<Apple> apples;
|
||||
Basket<Pear> pears;
|
||||
|
||||
|
||||
class Dog {};
|
||||
|
||||
class Cat
|
||||
{
|
||||
Dog getDog();
|
||||
};
|
||||
|
||||
class Bird
|
||||
{
|
||||
Dog getDog();
|
||||
Cat getCat();
|
||||
};
|
||||
|
||||
class Fish
|
||||
{
|
||||
Dog getDog();
|
||||
Cat getCat();
|
||||
Bird getBird();
|
||||
};
|
||||
|
||||
class Horse
|
||||
: public Dog
|
||||
{
|
||||
Dog getDog();
|
||||
Cat getCat();
|
||||
Bird getBird();
|
||||
Fish getFish();
|
||||
};
|
||||
|
||||
|
||||
class Building {};
|
||||
class House : public Building {};
|
||||
class Tower : public Building {};
|
||||
class SkyScrapper : public Building {};
|
||||
class Mansion : public Building {};
|
||||
class Shard : public House, public Tower, public SkyScrapper, public Mansion {};
|
||||
|
||||
@@ -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
|
||||
@@ -255,6 +255,7 @@ add_files(
|
||||
utility/messaging/type/MessageFinishedParsing.h
|
||||
utility/messaging/type/MessageFocusIn.h
|
||||
utility/messaging/type/MessageFocusOut.h
|
||||
utility/messaging/type/MessageGraphNodeBundleSplit.h
|
||||
utility/messaging/type/MessageGraphNodeExpand.h
|
||||
utility/messaging/type/MessageGraphNodeMove.h
|
||||
utility/messaging/type/MessageInterruptTasks.h
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "utility/logging/logging.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include "component/controller/helper/DummyEdge.h"
|
||||
#include "component/controller/helper/DummyNode.h"
|
||||
@@ -11,7 +12,6 @@
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
#include "data/access/StorageAccess.h"
|
||||
#include "data/graph/Graph.h"
|
||||
#include "data/graph/token_component/TokenComponentAggregation.h"
|
||||
|
||||
GraphController::GraphController(StorageAccess* storageAccess)
|
||||
: m_storageAccess(storageAccess)
|
||||
@@ -33,12 +33,6 @@ void GraphController::handleMessage(MessageActivateTokens* message)
|
||||
return;
|
||||
}
|
||||
|
||||
if (message->isAggregation)
|
||||
{
|
||||
createDummyGraphForTokenIds(message->tokenIds);
|
||||
return;
|
||||
}
|
||||
|
||||
createDummyGraphForTokenIds(message->tokenIds);
|
||||
}
|
||||
|
||||
@@ -57,6 +51,38 @@ void GraphController::handleMessage(MessageFocusOut *message)
|
||||
getView()->defocusToken(message->tokenId);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageGraphNodeBundleSplit* message)
|
||||
{
|
||||
for (size_t i = 0; i < m_dummyNodes.size(); i++)
|
||||
{
|
||||
DummyNode& node = m_dummyNodes[i];
|
||||
if (node.isBundleNode() && node.tokenId == message->bundleId)
|
||||
{
|
||||
m_dummyNodes.insert(m_dummyNodes.end(), node.bundledNodes.begin(), node.bundledNodes.end());
|
||||
m_dummyNodes.erase(m_dummyNodes.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_dummyEdges.size(); i++)
|
||||
{
|
||||
DummyEdge& edge = m_dummyEdges[i];
|
||||
if (!edge.data && edge.targetId == message->bundleId)
|
||||
{
|
||||
m_dummyEdges.erase(m_dummyEdges.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setActiveAndVisibility(m_activeTokenIds);
|
||||
|
||||
layoutNesting();
|
||||
GraphLayouter::layoutSpectralPrototype(m_dummyNodes, m_dummyEdges);
|
||||
GraphPostprocessor::doPostprocessing(m_dummyNodes);
|
||||
|
||||
getView()->rebuildGraph(nullptr, m_dummyNodes, m_dummyEdges);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageGraphNodeExpand* message)
|
||||
{
|
||||
DummyNode* node = findDummyNodeRecursive(m_dummyNodes, message->tokenId);
|
||||
@@ -132,6 +158,8 @@ void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenId
|
||||
autoExpandActiveNode(tokenIds);
|
||||
setActiveAndVisibility(tokenIds);
|
||||
|
||||
bundleNodes();
|
||||
|
||||
layoutNesting();
|
||||
GraphLayouter::layoutSpectralPrototype(m_dummyNodes, m_dummyEdges);
|
||||
GraphPostprocessor::doPostprocessing(m_dummyNodes);
|
||||
@@ -160,7 +188,7 @@ DummyNode GraphController::createDummyNodeTopDown(Node* node)
|
||||
DummyNode* oldNode = findDummyNodeRecursive(m_dummyNodes, node->getId());
|
||||
if (oldNode)
|
||||
{
|
||||
result.expanded = oldNode->expanded;
|
||||
result.expanded = oldNode->isExpanded();
|
||||
}
|
||||
|
||||
node->forEachChildNode(
|
||||
@@ -212,14 +240,10 @@ DummyNode GraphController::createDummyNodeTopDown(Node* node)
|
||||
}
|
||||
);
|
||||
|
||||
node->forEachEdge(
|
||||
node->forEachEdgeOfType(
|
||||
~Edge::EDGE_MEMBER,
|
||||
[&result, node, this](Edge* edge)
|
||||
{
|
||||
if (edge->isType(Edge::EDGE_MEMBER))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const DummyEdge& dummy : m_dummyEdges)
|
||||
{
|
||||
if (dummy.data->getId() == edge->getId())
|
||||
@@ -258,7 +282,7 @@ void GraphController::setActiveAndVisibility(const std::vector<Id>& activeTokenI
|
||||
|
||||
for (DummyEdge& edge : m_dummyEdges)
|
||||
{
|
||||
if (edge.data->isType(Edge::EDGE_AGGREGATION))
|
||||
if (!edge.data || edge.data->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -282,7 +306,7 @@ void GraphController::setActiveAndVisibility(const std::vector<Id>& activeTokenI
|
||||
|
||||
for (DummyEdge& edge : m_dummyEdges)
|
||||
{
|
||||
if (!edge.data->isType(Edge::EDGE_AGGREGATION))
|
||||
if (!edge.data || !edge.data->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -298,7 +322,7 @@ void GraphController::setActiveAndVisibility(const std::vector<Id>& activeTokenI
|
||||
{
|
||||
for (DummyEdge& e : m_dummyEdges)
|
||||
{
|
||||
if (e.visible && e.data->getId() == id)
|
||||
if (e.visible && e.data && e.data->getId() == id)
|
||||
{
|
||||
component->removeAggregationId(id);
|
||||
}
|
||||
@@ -345,6 +369,11 @@ bool GraphController::setNodeVisibilityRecursiveBottomUp(DummyNode& node, bool a
|
||||
node.visible = true;
|
||||
return false;
|
||||
}
|
||||
else if (node.isBundleNode())
|
||||
{
|
||||
node.visible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (DummyNode& subNode : node.subNodes)
|
||||
{
|
||||
@@ -379,6 +408,215 @@ void GraphController::setNodeVisibilityRecursiveTopDown(DummyNode& node, bool pa
|
||||
}
|
||||
}
|
||||
|
||||
void GraphController::bundleNodes()
|
||||
{
|
||||
bundleNodesMatching(
|
||||
[&](const DummyNode& node)
|
||||
{
|
||||
return isTypeNodeWithSingleAggregation(node, TokenComponentAggregation::DIRECTION_BACKWARD);
|
||||
},
|
||||
3,
|
||||
"Used Types"
|
||||
);
|
||||
|
||||
bundleNodesMatching(
|
||||
[&](const DummyNode& node)
|
||||
{
|
||||
return isTypeNodeWithSingleAggregation(node, TokenComponentAggregation::DIRECTION_FORWARD);
|
||||
},
|
||||
3,
|
||||
"Using Types"
|
||||
);
|
||||
|
||||
bundleNodesMatching(
|
||||
[&](const DummyNode& node)
|
||||
{
|
||||
return isTypeNodeWithSingleInheritance(node, true);
|
||||
},
|
||||
3,
|
||||
"Base Types"
|
||||
);
|
||||
|
||||
bundleNodesMatching(
|
||||
[&](const DummyNode& node)
|
||||
{
|
||||
return isTypeNodeWithSingleInheritance(node, false);
|
||||
},
|
||||
3,
|
||||
"Derived Types"
|
||||
);
|
||||
|
||||
bundleNodesMatching(
|
||||
[](const DummyNode& node)
|
||||
{
|
||||
const Node::NodeTypeMask undefinedMask =
|
||||
Node::NODE_UNDEFINED | Node::NODE_UNDEFINED_TYPE |
|
||||
Node::NODE_UNDEFINED_VARIABLE | Node::NODE_UNDEFINED_FUNCTION;
|
||||
|
||||
if (node.visible && node.isGraphNode() && !node.hasActiveSubNode() && node.data->isType(undefinedMask))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
3,
|
||||
"Undefined Nodes"
|
||||
);
|
||||
}
|
||||
|
||||
void GraphController::bundleNodesMatching(std::function<bool(const DummyNode&)> matcher, size_t count, const std::string& name)
|
||||
{
|
||||
size_t matchCount = 0;
|
||||
std::vector<size_t> nodeIndices;
|
||||
|
||||
for (size_t i = 0; i < m_dummyNodes.size(); i++)
|
||||
{
|
||||
const DummyNode& node = m_dummyNodes[i];
|
||||
|
||||
if (matcher(node))
|
||||
{
|
||||
matchCount++;
|
||||
nodeIndices.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (matchCount < count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DummyNode bundleNode;
|
||||
bundleNode.name = name;
|
||||
bundleNode.visible = true;
|
||||
|
||||
for (int i = nodeIndices.size() - 1; i >= 0; i--)
|
||||
{
|
||||
DummyNode& node = m_dummyNodes[nodeIndices[i]];
|
||||
node.visible = false;
|
||||
|
||||
bundleNode.bundledNodes.push_back(node);
|
||||
if (!bundleNode.tokenId)
|
||||
{
|
||||
bundleNode.tokenId = node.data->getId();
|
||||
}
|
||||
|
||||
m_dummyNodes.erase(m_dummyNodes.begin() + nodeIndices[i]);
|
||||
}
|
||||
|
||||
std::vector<DummyEdge> bundleEdges;
|
||||
for (DummyNode& node : bundleNode.bundledNodes)
|
||||
{
|
||||
for (DummyEdge& edge : m_dummyEdges)
|
||||
{
|
||||
bool owner = (edge.ownerId == node.data->getId());
|
||||
bool target = (edge.targetId == node.data->getId());
|
||||
|
||||
if (!owner && !target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DummyEdge* bundleEdgePtr = nullptr;
|
||||
for (DummyEdge& bundleEdge : bundleEdges)
|
||||
{
|
||||
if ((owner && bundleEdge.ownerId == edge.targetId) ||
|
||||
(target && bundleEdge.ownerId == edge.ownerId))
|
||||
{
|
||||
bundleEdgePtr = &bundleEdge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bundleEdgePtr)
|
||||
{
|
||||
DummyEdge bundleEdge;
|
||||
bundleEdge.visible = true;
|
||||
bundleEdge.ownerId = (owner ? edge.targetId : edge.ownerId);
|
||||
bundleEdge.targetId = bundleNode.bundledNodes.front().data->getId();
|
||||
bundleEdges.push_back(bundleEdge);
|
||||
bundleEdgePtr = &bundleEdges.back();
|
||||
}
|
||||
|
||||
bundleEdgePtr->weight += edge.getWeight();
|
||||
bundleEdgePtr->updateDirection(edge.getDirection(), owner);
|
||||
edge.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_dummyEdges.insert(m_dummyEdges.end(), bundleEdges.begin(), bundleEdges.end());
|
||||
m_dummyNodes.push_back(bundleNode);
|
||||
}
|
||||
|
||||
bool GraphController::isTypeNodeWithSingleAggregation(
|
||||
const DummyNode& node, TokenComponentAggregation::Direction direction
|
||||
) const {
|
||||
const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS;
|
||||
|
||||
if (!node.visible || !node.isGraphNode() || node.hasVisibleSubNode() || !node.data->isType(typeMask))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool matches = false;
|
||||
int count = 0;
|
||||
Id tokenId = node.data->getId();
|
||||
|
||||
node.data->forEachEdgeOfType(
|
||||
~Edge::EDGE_MEMBER,
|
||||
[direction, tokenId, &matches, &count](Edge* edge)
|
||||
{
|
||||
count++;
|
||||
|
||||
if (edge->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
TokenComponentAggregation::Direction dir =
|
||||
edge->getComponent<TokenComponentAggregation>()->getDirection();
|
||||
|
||||
if ((edge->getFrom()->getId() == tokenId && dir == direction) ||
|
||||
(edge->getTo()->getId() == tokenId && dir == TokenComponentAggregation::opposite(direction)))
|
||||
{
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (count > 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
bool GraphController::isTypeNodeWithSingleInheritance(const DummyNode& node, bool isBase) const
|
||||
{
|
||||
const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS;
|
||||
|
||||
if (!node.visible || !node.isGraphNode() || node.hasVisibleSubNode() || !node.data->isType(typeMask))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool matches = false;
|
||||
Id tokenId = node.data->getId();
|
||||
|
||||
node.data->forEachEdgeOfType(
|
||||
Edge::EDGE_INHERITANCE,
|
||||
[isBase, tokenId, &matches](Edge* edge)
|
||||
{
|
||||
if ((!isBase && edge->getFrom()->getId() == tokenId) ||
|
||||
(isBase && edge->getTo()->getId() == tokenId))
|
||||
{
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
void GraphController::layoutNesting()
|
||||
{
|
||||
for (DummyNode& node : m_dummyNodes)
|
||||
@@ -394,6 +632,11 @@ void GraphController::layoutNesting()
|
||||
|
||||
void GraphController::layoutNestingRecursive(DummyNode& node) const
|
||||
{
|
||||
if (!node.visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeMargins margins;
|
||||
|
||||
if (node.isGraphNode())
|
||||
@@ -408,6 +651,10 @@ void GraphController::layoutNestingRecursive(DummyNode& node) const
|
||||
{
|
||||
margins = GraphViewStyle::getMarginsOfExpandToggleNode();
|
||||
}
|
||||
else if (node.isBundleNode())
|
||||
{
|
||||
margins = GraphViewStyle::getMarginsOfBundleNode();
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
int x = 0;
|
||||
@@ -423,6 +670,10 @@ void GraphController::layoutNestingRecursive(DummyNode& node) const
|
||||
addExpandToggleNode(node);
|
||||
}
|
||||
}
|
||||
else if (node.isBundleNode())
|
||||
{
|
||||
width = margins.charWidth * (node.name.size() + 1 + utility::digits(node.bundledNodes.size()));
|
||||
}
|
||||
|
||||
// Horizontal layouting is currently not used, but left in place for experimentation.
|
||||
bool layoutHorizontal = false;
|
||||
@@ -541,9 +792,8 @@ void GraphController::addExpandToggleNode(DummyNode& node) const
|
||||
|
||||
void GraphController::layoutToGrid(DummyNode& node) const
|
||||
{
|
||||
if (!node.isGraphNode())
|
||||
if (!node.visible)
|
||||
{
|
||||
LOG_ERROR("Only GraphNodes can be layouted to the grid");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -556,6 +806,11 @@ void GraphController::layoutToGrid(DummyNode& node) const
|
||||
node.size.x = width;
|
||||
node.size.y = height;
|
||||
|
||||
if (!node.isGraphNode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DummyNode* lastAccessNode = nullptr;
|
||||
for (DummyNode& subNode : node.subNodes)
|
||||
{
|
||||
@@ -581,7 +836,7 @@ void GraphController::layoutToGrid(DummyNode& node) const
|
||||
}
|
||||
}
|
||||
|
||||
DummyNode* GraphController::findDummyNodeRecursive(std::vector<DummyNode>& nodes, Id tokenId)
|
||||
DummyNode* GraphController::findDummyNodeRecursive(std::vector<DummyNode>& nodes, Id tokenId) const
|
||||
{
|
||||
for (DummyNode& node : nodes)
|
||||
{
|
||||
@@ -602,7 +857,7 @@ DummyNode* GraphController::findDummyNodeRecursive(std::vector<DummyNode>& nodes
|
||||
|
||||
DummyNode* GraphController::findDummyNodeAccessRecursive(
|
||||
std::vector<DummyNode>& nodes, Id parentId, TokenComponentAccess::AccessType type
|
||||
){
|
||||
) const {
|
||||
DummyNode* node = findDummyNodeRecursive(nodes, parentId);
|
||||
if (node)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "utility/messaging/type/MessageFinishedParsing.h"
|
||||
#include "utility/messaging/type/MessageFocusIn.h"
|
||||
#include "utility/messaging/type/MessageFocusOut.h"
|
||||
#include "utility/messaging/type/MessageGraphNodeBundleSplit.h"
|
||||
#include "utility/messaging/type/MessageGraphNodeExpand.h"
|
||||
#include "utility/messaging/type/MessageGraphNodeMove.h"
|
||||
|
||||
@@ -15,6 +16,7 @@
|
||||
#include "component/controller/GraphLayouter.h"
|
||||
#include "component/view/GraphView.h"
|
||||
#include "data/graph/token_component/TokenComponentAccess.h"
|
||||
#include "data/graph/token_component/TokenComponentAggregation.h"
|
||||
|
||||
struct DummyNode;
|
||||
struct DummyEdge;
|
||||
@@ -28,6 +30,7 @@ class GraphController
|
||||
, public MessageListener<MessageFinishedParsing>
|
||||
, public MessageListener<MessageFocusIn>
|
||||
, public MessageListener<MessageFocusOut>
|
||||
, public MessageListener<MessageGraphNodeBundleSplit>
|
||||
, public MessageListener<MessageGraphNodeExpand>
|
||||
, public MessageListener<MessageGraphNodeMove>
|
||||
{
|
||||
@@ -40,6 +43,7 @@ private:
|
||||
virtual void handleMessage(MessageFinishedParsing* message);
|
||||
virtual void handleMessage(MessageFocusIn* message);
|
||||
virtual void handleMessage(MessageFocusOut* message);
|
||||
virtual void handleMessage(MessageGraphNodeBundleSplit* message);
|
||||
virtual void handleMessage(MessageGraphNodeExpand* message);
|
||||
virtual void handleMessage(MessageGraphNodeMove* message);
|
||||
|
||||
@@ -55,13 +59,18 @@ private:
|
||||
bool setNodeVisibilityRecursiveBottomUp(DummyNode& node, bool aggregated) const;
|
||||
void setNodeVisibilityRecursiveTopDown(DummyNode& node, bool parentExpanded) const;
|
||||
|
||||
void bundleNodes();
|
||||
void bundleNodesMatching(std::function<bool(const DummyNode&)> matcher, size_t count, const std::string& name);
|
||||
bool isTypeNodeWithSingleAggregation(const DummyNode& node, TokenComponentAggregation::Direction direction) const;
|
||||
bool isTypeNodeWithSingleInheritance(const DummyNode& node, bool isBase) const;
|
||||
|
||||
void layoutNesting();
|
||||
void layoutNestingRecursive(DummyNode& node) const;
|
||||
void addExpandToggleNode(DummyNode& node) const;
|
||||
void layoutToGrid(DummyNode& node) const;
|
||||
|
||||
DummyNode* findDummyNodeRecursive(std::vector<DummyNode>& nodes, Id tokenId);
|
||||
DummyNode* findDummyNodeAccessRecursive(std::vector<DummyNode>& nodes, Id parentId, TokenComponentAccess::AccessType type);
|
||||
DummyNode* findDummyNodeRecursive(std::vector<DummyNode>& nodes, Id tokenId) const;
|
||||
DummyNode* findDummyNodeAccessRecursive(std::vector<DummyNode>& nodes, Id parentId, TokenComponentAccess::AccessType type) const;
|
||||
|
||||
StorageAccess* m_storageAccess;
|
||||
|
||||
|
||||
@@ -11,18 +11,35 @@ class Edge;
|
||||
// temporary data structure for (visual) graph creation process
|
||||
struct DummyEdge
|
||||
{
|
||||
DummyEdge()
|
||||
: ownerId(0)
|
||||
, targetId(0)
|
||||
, data(nullptr)
|
||||
, visible(false)
|
||||
, active(false)
|
||||
, weight(0)
|
||||
, direction(TokenComponentAggregation::DIRECTION_INVALID)
|
||||
{
|
||||
}
|
||||
|
||||
DummyEdge(const Id ownerId, const Id targetId, const Edge* data)
|
||||
: ownerId(ownerId)
|
||||
, targetId(targetId)
|
||||
, data(data)
|
||||
, visible(false)
|
||||
, active(false)
|
||||
, weight(0)
|
||||
, direction(TokenComponentAggregation::DIRECTION_INVALID)
|
||||
{
|
||||
}
|
||||
|
||||
int getWeight() const
|
||||
{
|
||||
if (data->isType(Edge::EDGE_AGGREGATION))
|
||||
if (!data)
|
||||
{
|
||||
return weight;
|
||||
}
|
||||
else if (data->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
return data->getComponent<TokenComponentAggregation>()->getAggregationCount();
|
||||
}
|
||||
@@ -30,6 +47,37 @@ struct DummyEdge
|
||||
return 1;
|
||||
}
|
||||
|
||||
void updateDirection(TokenComponentAggregation::Direction dir, bool invert)
|
||||
{
|
||||
if (invert)
|
||||
{
|
||||
dir = TokenComponentAggregation::opposite(dir);
|
||||
}
|
||||
|
||||
if (direction == TokenComponentAggregation::DIRECTION_INVALID)
|
||||
{
|
||||
direction = dir;
|
||||
}
|
||||
else if (direction != dir)
|
||||
{
|
||||
direction = TokenComponentAggregation::DIRECTION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
TokenComponentAggregation::Direction getDirection() const
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
return direction;
|
||||
}
|
||||
else if (data->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
return data->getComponent<TokenComponentAggregation>()->getDirection();
|
||||
}
|
||||
|
||||
return TokenComponentAggregation::DIRECTION_FORWARD;
|
||||
}
|
||||
|
||||
Id ownerId;
|
||||
Id targetId;
|
||||
|
||||
@@ -37,6 +85,10 @@ struct DummyEdge
|
||||
|
||||
bool visible;
|
||||
bool active;
|
||||
|
||||
// BundleEdge
|
||||
int weight;
|
||||
TokenComponentAggregation::Direction direction;
|
||||
};
|
||||
|
||||
#endif // DUMMY_EDGE_H
|
||||
|
||||
@@ -40,7 +40,12 @@ public:
|
||||
|
||||
bool isExpandToggleNode() const
|
||||
{
|
||||
return !data && !isAccessNode();
|
||||
return !isGraphNode() && !isAccessNode() && !isBundleNode();
|
||||
}
|
||||
|
||||
bool isBundleNode() const
|
||||
{
|
||||
return bundledNodes.size() > 0;
|
||||
}
|
||||
|
||||
bool isExpanded() const
|
||||
@@ -48,6 +53,55 @@ public:
|
||||
return expanded || autoExpanded;
|
||||
}
|
||||
|
||||
bool hasVisibleSubNode() const
|
||||
{
|
||||
for (const DummyNode& node : subNodes)
|
||||
{
|
||||
if (node.visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasActiveSubNode() const
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const DummyNode& node : subNodes)
|
||||
{
|
||||
if (node.hasActiveSubNode())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasConnectedSubNode() const
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const DummyNode& node : subNodes)
|
||||
{
|
||||
if (node.hasConnectedSubNode())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2i position;
|
||||
Vec2i size;
|
||||
|
||||
@@ -72,6 +126,10 @@ public:
|
||||
|
||||
// ExpandToggleNode
|
||||
size_t invisibleSubNodeCount;
|
||||
|
||||
// BundleNode
|
||||
std::vector<DummyNode> bundledNodes;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
#endif // DUMMY_NODE_H
|
||||
|
||||
@@ -217,6 +217,11 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfExpandToggleNode()
|
||||
return margins;
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfBundleNode()
|
||||
{
|
||||
return getMarginsForNodeType(Node::NODE_CLASS, false);
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeStyle GraphViewStyle::getStyleForNodeType(
|
||||
Node::NodeType type, bool isActive, bool isFocused, bool hasChildren
|
||||
){
|
||||
@@ -367,12 +372,23 @@ GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfExpandToggleNode()
|
||||
return style;
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfBundleNode(bool isFocused)
|
||||
{
|
||||
NodeStyle style = getStyleForNodeType(Node::NODE_CLASS, false, isFocused, false);
|
||||
|
||||
style.shadowColor = "";
|
||||
style.borderColor = "#3c3c3c";
|
||||
style.borderWidth = isFocused ? 2 : 1;
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused)
|
||||
{
|
||||
EdgeStyle style;
|
||||
|
||||
style.width = isActive ? 3 : 1;
|
||||
style.zValue = isActive ? 5 : 1;
|
||||
style.zValue = isActive ? 5 : 2;
|
||||
|
||||
style.arrowLength = 5;
|
||||
style.arrowWidth = 8;
|
||||
@@ -392,7 +408,7 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType typ
|
||||
style.isStraight = true;
|
||||
style.color = isActive ? "#DDD" : "#EEE";
|
||||
style.width = 1;
|
||||
style.zValue = isActive ? -1 : -5;
|
||||
style.zValue = isActive ? -2 : -5;
|
||||
break;
|
||||
|
||||
case Edge::EDGE_CALL:
|
||||
|
||||
@@ -94,10 +94,12 @@ public:
|
||||
static NodeMargins getMarginsForNodeType(Node::NodeType type, bool hasChildren);
|
||||
static NodeMargins getMarginsOfAccessNode(TokenComponentAccess::AccessType type);
|
||||
static NodeMargins getMarginsOfExpandToggleNode();
|
||||
static NodeMargins getMarginsOfBundleNode();
|
||||
|
||||
static NodeStyle getStyleForNodeType(Node::NodeType type, bool isActive, bool isFocused, bool hasChildren);
|
||||
static NodeStyle getStyleOfAccessNode();
|
||||
static NodeStyle getStyleOfExpandToggleNode();
|
||||
static NodeStyle getStyleOfBundleNode(bool isFocused);
|
||||
|
||||
static EdgeStyle getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused);
|
||||
|
||||
|
||||
@@ -811,10 +811,15 @@ std::shared_ptr<Graph> Storage::getGraphForActiveTokenIds(const std::vector<Id>&
|
||||
Node::NODE_UNDEFINED_FUNCTION | Node::NODE_FUNCTION | Node::NODE_METHOD |
|
||||
Node::NODE_UNDEFINED_VARIABLE | Node::NODE_GLOBAL_VARIABLE | Node::NODE_FIELD;
|
||||
|
||||
if (!node->isType(varFuncMask) || !edge->isType(Edge::EDGE_AGGREGATION))
|
||||
const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS;
|
||||
|
||||
if ((node->isType(varFuncMask) && edge->isType(Edge::EDGE_AGGREGATION)) ||
|
||||
(node->isType(typeMask) && edge->isType(Edge::EDGE_TYPE_USAGE | Edge::EDGE_TYPE_OF)))
|
||||
{
|
||||
graph->addEdgeAndAllChildrenAsPlainCopy(edge);
|
||||
return;
|
||||
}
|
||||
|
||||
graph->addEdgeAndAllChildrenAsPlainCopy(edge);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,17 +128,17 @@ Edge* Node::findEdge(std::function<bool(Edge*)> func) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Edge* Node::findEdgeOfType(Edge::EdgeType type) const
|
||||
Edge* Node::findEdgeOfType(Edge::EdgeTypeMask mask) const
|
||||
{
|
||||
return findEdgeOfType(type, [](Edge* e){ return true; });
|
||||
return findEdgeOfType(mask, [](Edge* e){ return true; });
|
||||
}
|
||||
|
||||
Edge* Node::findEdgeOfType(Edge::EdgeType type, std::function<bool(Edge*)> func) const
|
||||
Edge* Node::findEdgeOfType(Edge::EdgeTypeMask mask, std::function<bool(Edge*)> func) const
|
||||
{
|
||||
std::vector<Edge*>::const_iterator it = find_if(m_edges.begin(), m_edges.end(),
|
||||
[type, func](Edge* e)
|
||||
[mask, func](Edge* e)
|
||||
{
|
||||
if (e->getType() == type)
|
||||
if (e->isType(mask))
|
||||
{
|
||||
return func(e);
|
||||
}
|
||||
@@ -180,12 +180,12 @@ void Node::forEachEdge(std::function<void(Edge*)> func) const
|
||||
for_each(m_edges.begin(), m_edges.end(), func);
|
||||
}
|
||||
|
||||
void Node::forEachEdgeOfType(Edge::EdgeType type, std::function<void(Edge*)> func) const
|
||||
void Node::forEachEdgeOfType(Edge::EdgeTypeMask mask, std::function<void(Edge*)> func) const
|
||||
{
|
||||
for_each(m_edges.begin(), m_edges.end(),
|
||||
[type, func](Edge* e)
|
||||
[mask, func](Edge* e)
|
||||
{
|
||||
if (e->getType() == type)
|
||||
if (e->isType(mask))
|
||||
{
|
||||
func(e);
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@ public:
|
||||
Edge* getMemberEdge() const;
|
||||
|
||||
Edge* findEdge(std::function<bool(Edge*)> func) const;
|
||||
Edge* findEdgeOfType(Edge::EdgeType type) const;
|
||||
Edge* findEdgeOfType(Edge::EdgeType type, std::function<bool(Edge*)> func) const;
|
||||
Edge* findEdgeOfType(Edge::EdgeTypeMask mask) const;
|
||||
Edge* findEdgeOfType(Edge::EdgeTypeMask mask, std::function<bool(Edge*)> func) const;
|
||||
Node* findChildNode(std::function<bool(Node*)> func) const;
|
||||
|
||||
void forEachEdge(std::function<void(Edge*)> func) const;
|
||||
void forEachEdgeOfType(Edge::EdgeType type, std::function<void(Edge*)> func) const;
|
||||
void forEachEdgeOfType(Edge::EdgeTypeMask mask, std::function<void(Edge*)> func) const;
|
||||
void forEachChildNode(std::function<void(Node*)> func) const;
|
||||
|
||||
bool hasReferences() const;
|
||||
|
||||
@@ -197,7 +197,8 @@ void StorageGraph::updateAggregationEdges(Node* from, Node* to, Id addEdgeId, Id
|
||||
|
||||
if (addEdgeId)
|
||||
{
|
||||
edge->getComponent<TokenComponentAggregation>()->addAggregationId(addEdgeId);
|
||||
bool forward = (edge->getFrom() == from);
|
||||
edge->getComponent<TokenComponentAggregation>()->addAggregationId(addEdgeId, forward);
|
||||
}
|
||||
|
||||
if (edge && removeEdgeId)
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
#include "data/graph/token_component/TokenComponentAggregation.h"
|
||||
|
||||
TokenComponentAggregation::Direction TokenComponentAggregation::opposite(Direction direction)
|
||||
{
|
||||
if (direction == DIRECTION_FORWARD)
|
||||
{
|
||||
return DIRECTION_BACKWARD;
|
||||
}
|
||||
else if (direction == DIRECTION_BACKWARD)
|
||||
{
|
||||
return DIRECTION_FORWARD;
|
||||
}
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
TokenComponentAggregation::TokenComponentAggregation()
|
||||
: m_direction(DIRECTION_INVALID)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -18,17 +33,53 @@ int TokenComponentAggregation::getAggregationCount() const
|
||||
return m_ids.size();
|
||||
}
|
||||
|
||||
const std::set<Id>& TokenComponentAggregation::getAggregationIds() const
|
||||
std::set<Id> TokenComponentAggregation::getAggregationIds() const
|
||||
{
|
||||
return m_ids;
|
||||
std::set<Id> ids;
|
||||
|
||||
for (const std::pair<Id, Direction>& p : m_ids)
|
||||
{
|
||||
ids.insert(p.first);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
void TokenComponentAggregation::addAggregationId(Id id)
|
||||
void TokenComponentAggregation::addAggregationId(Id id, bool forward)
|
||||
{
|
||||
m_ids.insert(id);
|
||||
m_ids.emplace(id, forward ? DIRECTION_FORWARD : DIRECTION_BACKWARD);
|
||||
|
||||
m_direction = DIRECTION_INVALID;
|
||||
}
|
||||
|
||||
void TokenComponentAggregation::removeAggregationId(Id id)
|
||||
{
|
||||
m_ids.erase(id);
|
||||
|
||||
m_direction = DIRECTION_INVALID;
|
||||
}
|
||||
|
||||
TokenComponentAggregation::Direction TokenComponentAggregation::getDirection()
|
||||
{
|
||||
if (m_direction != DIRECTION_INVALID)
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
m_direction = DIRECTION_NONE;
|
||||
|
||||
for (const std::pair<Id, Direction>& p : m_ids)
|
||||
{
|
||||
if (m_direction == DIRECTION_NONE)
|
||||
{
|
||||
m_direction = p.second;
|
||||
}
|
||||
else if (m_direction != p.second)
|
||||
{
|
||||
m_direction = DIRECTION_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef TOKEN_COMPONENT_AGGREGATION_H
|
||||
#define TOKEN_COMPONENT_AGGREGATION_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "utility/types.h"
|
||||
@@ -11,19 +12,32 @@ class TokenComponentAggregation
|
||||
: public TokenComponent
|
||||
{
|
||||
public:
|
||||
enum Direction
|
||||
{
|
||||
DIRECTION_NONE,
|
||||
DIRECTION_FORWARD,
|
||||
DIRECTION_BACKWARD,
|
||||
DIRECTION_INVALID
|
||||
};
|
||||
|
||||
static Direction opposite(Direction direction);
|
||||
|
||||
TokenComponentAggregation();
|
||||
virtual ~TokenComponentAggregation();
|
||||
|
||||
virtual std::shared_ptr<TokenComponent> copy() const;
|
||||
|
||||
int getAggregationCount() const;
|
||||
const std::set<Id>& getAggregationIds() const;
|
||||
std::set<Id> getAggregationIds() const;
|
||||
|
||||
void addAggregationId(Id id);
|
||||
void addAggregationId(Id id, bool forward);
|
||||
void removeAggregationId(Id id);
|
||||
|
||||
Direction getDirection();
|
||||
|
||||
private:
|
||||
std::set<Id> m_ids;
|
||||
std::map<Id, Direction> m_ids;
|
||||
Direction m_direction;
|
||||
};
|
||||
|
||||
#endif // TOKEN_COMPONENT_AGGREGATION_H
|
||||
|
||||
@@ -5,11 +5,11 @@ template<class T>
|
||||
class Property
|
||||
{
|
||||
public:
|
||||
Property(T* valuePointer);
|
||||
explicit Property(T* valuePointer);
|
||||
~Property();
|
||||
|
||||
T& operator=(const T& value);
|
||||
T& operator=(const Property& property);
|
||||
Property<T>& operator=(const Property<T>& property);
|
||||
|
||||
operator const T&() const;
|
||||
|
||||
@@ -36,10 +36,9 @@ T& Property<T>::operator=(const T& value)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T& Property<T>::operator=(const Property& property)
|
||||
Property<T>& Property<T>::operator=(const Property<T>& property)
|
||||
{
|
||||
*m_valuePointer = *property.m_valuePointer;
|
||||
return *m_valuePointer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
T operator[](const unsigned int index);
|
||||
|
||||
template<class U>
|
||||
void operator=(const VectorBase<U, N>& other);
|
||||
VectorBase<U, N>& operator=(const VectorBase<U, N>& other);
|
||||
|
||||
template<class U>
|
||||
VectorBase<T, N> operator+(const VectorBase<U, N>& other) const;
|
||||
@@ -339,9 +339,10 @@ T VectorBase<T, N>::operator[](const unsigned int index)
|
||||
|
||||
template<class T, unsigned int N>
|
||||
template<class U>
|
||||
void VectorBase<T, N>::operator=(const VectorBase<U, N>& other)
|
||||
VectorBase<U, N>& VectorBase<T, N>::operator=(const VectorBase<U, N>& other)
|
||||
{
|
||||
assign(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N>
|
||||
|
||||
@@ -10,7 +10,6 @@ public:
|
||||
MessageActivateTokens(const std::vector<Id>& tokenIds)
|
||||
: tokenIds(tokenIds)
|
||||
, isEdge(false)
|
||||
, isAggregation(false)
|
||||
, isFromSystem(false)
|
||||
{
|
||||
}
|
||||
@@ -18,7 +17,6 @@ public:
|
||||
MessageActivateTokens(Id tokenId)
|
||||
: tokenIds(1, tokenId)
|
||||
, isEdge(false)
|
||||
, isAggregation(false)
|
||||
, isFromSystem(false)
|
||||
{
|
||||
}
|
||||
@@ -31,7 +29,7 @@ public:
|
||||
const std::vector<Id> tokenIds;
|
||||
|
||||
bool isEdge;
|
||||
bool isAggregation;
|
||||
bool isBundle;
|
||||
bool isFromSystem;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef MESSAGE_GRAPH_NODE_BUNDLE_SPLIT_H
|
||||
#define MESSAGE_GRAPH_NODE_BUNDLE_SPLIT_H
|
||||
|
||||
#include "utility/messaging/Message.h"
|
||||
#include "utility/types.h"
|
||||
|
||||
class MessageGraphNodeBundleSplit
|
||||
: public Message<MessageGraphNodeBundleSplit>
|
||||
{
|
||||
public:
|
||||
MessageGraphNodeBundleSplit(Id bundleId)
|
||||
: bundleId(bundleId)
|
||||
{
|
||||
}
|
||||
|
||||
static const std::string getStaticType()
|
||||
{
|
||||
return "MessageGraphNodeBundleSplit";
|
||||
}
|
||||
|
||||
Id bundleId;
|
||||
};
|
||||
|
||||
#endif // MESSAGE_GRAPH_NODE_BUNDLE_SPLIT_H
|
||||
@@ -43,3 +43,16 @@ bool utility::intersectionPoint(Vec2f a1, Vec2f b1, Vec2f a2, Vec2f b2, Vec2f* i
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t utility::digits(size_t n)
|
||||
{
|
||||
int digits = 1;
|
||||
|
||||
while (n >= 10)
|
||||
{
|
||||
n /= 10;
|
||||
digits++;
|
||||
}
|
||||
|
||||
return digits;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace utility
|
||||
void append(std::set<T>& a, const std::set<T>& b);
|
||||
|
||||
bool intersectionPoint(Vec2f a1, Vec2f b1, Vec2f a2, Vec2f b2, Vec2f* i);
|
||||
|
||||
size_t digits(size_t n);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
Reference in New Issue
Block a user