logic: Show inheritance edges between parents of active symbol and other visible symbols (issue #167)

* add inheritance edges between parents of active symbol and other visible symbols
* add dashed inheritance edges when symbols are no direct decendants
* inheritance edges are also respected in layouting, putting base/derived classes in the correct top/bottom spot now

bug id = 167
This commit is contained in:
Eberhard Graether
2017-07-16 18:40:59 +02:00
parent 9a36d48d1e
commit 9d0b86ad98
19 changed files with 261 additions and 17 deletions
+1
View File
@@ -124,6 +124,7 @@ add_files(
data/graph/token_component/TokenComponentConst.h
data/graph/token_component/TokenComponentFilePath.cpp
data/graph/token_component/TokenComponentFilePath.h
data/graph/token_component/TokenComponentInheritanceChain.h
data/graph/token_component/TokenComponentSignature.cpp
data/graph/token_component/TokenComponentSignature.h
data/graph/token_component/TokenComponentStatic.cpp
@@ -34,7 +34,6 @@ void ActivationController::handleMessage(MessageActivateEdge* message)
m.tokenIds = message->aggregationIds;
m.setKeepContent(false);
m.isAggregation = true;
m.tokenNames.push_back(NameHierarchy(message->getFullName(), message->sourceNameHierarchy.getDelimiter()));
m.dispatchImmediately();
}
else
@@ -42,7 +41,6 @@ void ActivationController::handleMessage(MessageActivateEdge* message)
MessageActivateTokens m(message);
m.tokenIds.push_back(message->tokenId);
m.isEdge = true;
m.tokenNames.push_back(NameHierarchy(message->getFullName(), message->sourceNameHierarchy.getDelimiter()));
m.dispatchImmediately();
}
}
@@ -103,17 +103,34 @@ void GraphController::handleMessage(MessageActivateTokens* message)
{
bundleNodes();
}
else if (message->isAggregation)
{
bool isInheritanceChain = true;
for (auto edge : m_dummyEdges)
{
if (!edge->data->isType(Edge::EDGE_INHERITANCE))
{
isInheritanceChain = false;
break;
}
}
if (isInheritanceChain)
{
for (auto node : m_dummyNodes)
{
node->bundleInfo.layoutVertical = true;
}
}
m_useBezierEdges = !isInheritanceChain;
}
layoutNesting();
layoutGraph(true);
assignBundleIds();
}
if (message->isAggregation)
{
m_useBezierEdges = true;
}
buildGraph(message, !isNamespace, true, isNamespace);
}
@@ -640,7 +657,8 @@ bool GraphController::setActive(const std::vector<Id>& activeTokenIds, bool show
DummyNode* from = getDummyGraphNodeById(edge->ownerId);
DummyNode* to = getDummyGraphNodeById(edge->targetId);
if (from && to && (showAllEdges || noActive || from->active || to->active || edge->active))
bool isInheritance = edge->data->isType(Edge::EDGE_INHERITANCE);
if (from && to && (showAllEdges || noActive || from->active || to->active || edge->active || isInheritance))
{
edge->visible = true;
from->connected = true;
@@ -104,6 +104,7 @@ GraphViewStyle::EdgeStyle::EdgeStyle()
, arrowClosed(false)
, cornerRadius(0)
, verticalOffset(0)
, dashed(false)
{
}
+2
View File
@@ -89,6 +89,8 @@ public:
Vec2i originOffset;
Vec2i targetOffset;
bool dashed;
};
static std::shared_ptr<GraphViewStyleImpl> getImpl();
+53
View File
@@ -34,6 +34,12 @@ void HierarchyCache::HierarchyNode::setParent(HierarchyNode* parent)
m_parent = parent;
}
void HierarchyCache::HierarchyNode::addBase(HierarchyNode* base, Id edgeId)
{
m_bases.push_back(base);
m_baseEdgeIds.push_back(edgeId);
}
void HierarchyCache::HierarchyNode::addChild(HierarchyNode* child)
{
m_children.push_back(child);
@@ -100,6 +106,30 @@ void HierarchyCache::HierarchyNode::setIsImplicit(bool isImplicit)
m_isImplicit = isImplicit;
}
void HierarchyCache::HierarchyNode::addInheritanceEdgesRecursive(
Id startId, std::vector<Id> inheritanceEdgeIds,
const std::set<Id>& nodeIds, std::vector<std::tuple<Id, Id, std::vector<Id>>>* inheritanceEdges)
{
for (size_t i = 0; i < m_bases.size(); i++)
{
HierarchyNode* base = m_bases[i];
Id baseId = base->getNodeId();
std::vector<Id> inheritanceEdgeIds2 = inheritanceEdgeIds;
inheritanceEdgeIds2.push_back(m_baseEdgeIds[i]);
if (nodeIds.find(baseId) != nodeIds.end())
{
std::vector<Id> inheritanceEdgeIds3 = inheritanceEdgeIds2;
inheritanceEdges->push_back(std::make_tuple<Id, Id, std::vector<Id>>(
std::forward<Id>(startId), std::forward<Id>(baseId), std::forward<std::vector<Id>>(inheritanceEdgeIds3)));
}
base->addInheritanceEdgesRecursive(startId, inheritanceEdgeIds2, nodeIds, inheritanceEdges);
}
}
void HierarchyCache::clear()
{
@@ -120,6 +150,14 @@ void HierarchyCache::createConnection(Id edgeId, Id fromId, Id toId, bool source
to->setIsImplicit(targetImplicit);
}
void HierarchyCache::createInheritance(Id edgeId, Id fromId, Id toId)
{
HierarchyNode* from = createNode(fromId);
HierarchyNode* to = createNode(toId);
from->addBase(to, edgeId);
}
Id HierarchyCache::getLastVisibleParentNodeId(Id nodeId) const
{
HierarchyNode* node = nullptr;
@@ -240,6 +278,21 @@ bool HierarchyCache::nodeHasChildren(Id nodeId) const
return false;
}
std::vector<std::tuple<Id, Id, std::vector<Id>>> HierarchyCache::getInheritanceEdgesForNodeId(
Id nodeId, const std::set<Id>& nodeIds) const
{
std::vector<std::tuple<Id, Id, std::vector<Id>>> inheritanceEdges;
HierarchyNode* node = getNode(nodeId);
if (node)
{
std::vector<Id> inheritanceEdgeIds;
node->addInheritanceEdgesRecursive(node->getNodeId(), inheritanceEdgeIds, nodeIds, & inheritanceEdges);
}
return inheritanceEdges;
}
HierarchyCache::HierarchyNode* HierarchyCache::getNode(Id nodeId) const
{
std::map<Id, std::shared_ptr<HierarchyNode>>::const_iterator it = m_nodes.find(nodeId);
+13
View File
@@ -14,6 +14,7 @@ public:
void clear();
void createConnection(Id edgeId, Id fromId, Id toId, bool sourceVisible, bool targetImplicit);
void createInheritance(Id edgeId, Id fromId, Id toId);
Id getLastVisibleParentNodeId(Id nodeId) const;
size_t getIndexOfLastVisibleParentNode(Id nodeId) const;
@@ -29,6 +30,8 @@ public:
bool nodeHasChildren(Id nodeId) const;
std::vector<std::tuple<Id, Id, std::vector<Id>>> getInheritanceEdgesForNodeId(Id nodeId, const std::set<Id>& nodeIds) const;
private:
class HierarchyNode
{
@@ -43,6 +46,8 @@ private:
HierarchyNode* getParent() const;
void setParent(HierarchyNode* parent);
void addBase(HierarchyNode* base, Id edgeId);
void addChild(HierarchyNode* child);
size_t getChildrenCount() const;
@@ -57,11 +62,19 @@ private:
bool isImplicit() const;
void setIsImplicit(bool isImplicit);
void addInheritanceEdgesRecursive(
Id startId, std::vector<Id> inheritanceEdgeIds,
const std::set<Id>& nodeIds, std::vector<std::tuple<Id, Id, std::vector<Id>>>* inheritanceEdges);
private:
const Id m_nodeId;
Id m_edgeId;
HierarchyNode* m_parent;
std::vector<HierarchyNode*> m_bases;
std::vector<Id> m_baseEdgeIds;
std::vector<HierarchyNode*> m_children;
bool m_isVisible;
+75
View File
@@ -17,6 +17,7 @@
#include "data/graph/token_component/TokenComponentAccess.h"
#include "data/graph/token_component/TokenComponentAggregation.h"
#include "data/graph/token_component/TokenComponentFilePath.h"
#include "data/graph/token_component/TokenComponentInheritanceChain.h"
#include "data/graph/token_component/TokenComponentSignature.h"
#include "data/graph/Graph.h"
#include "data/location/SourceLocationCollection.h"
@@ -1117,6 +1118,8 @@ std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(
addNodesToGraph(expandedChildIds, graph);
addEdgesToGraph(expandedChildEdgeIds, graph);
}
addInheritanceChainsToGraph(nodeIds, graph);
}
addComponentAccessToGraph(graph);
@@ -2078,6 +2081,72 @@ void PersistentStorage::addCompleteFlagsToSourceLocationCollection(SourceLocatio
);
}
void PersistentStorage::addInheritanceChainsToGraph(const std::vector<Id>& activeNodeIds, Graph* graph) const
{
TRACE();
std::set<Id> activeNodeIdsSet;
for (Id activeNodeId : activeNodeIds)
{
std::set<Id> visibleParentIds, edgeIds;
visibleParentIds.insert(activeNodeId);
m_hierarchyCache.addAllVisibleParentIdsForNodeId(activeNodeId, &visibleParentIds, &edgeIds);
for (Id nodeId : visibleParentIds)
{
Node* node = graph->getNodeById(nodeId);
if (node && node->isType(Node::NODE_INHERITABLE_TYPE))
{
activeNodeIdsSet.insert(node->getId());
}
}
}
std::set<Id> nodeIdsSet;
graph->forEachNode(
[&nodeIdsSet, &activeNodeIdsSet](Node* node)
{
if (node->isType(Node::NODE_INHERITABLE_TYPE) && activeNodeIdsSet.find(node->getId()) == activeNodeIdsSet.end())
{
nodeIdsSet.insert(node->getId());
}
}
);
std::vector<std::set<Id>*> nodeIdSets;
nodeIdSets.push_back(&activeNodeIdsSet);
nodeIdSets.push_back(&nodeIdsSet);
size_t inheritanceEdgeCount = 1;
for (size_t i = 0; i < nodeIdSets.size(); i++)
{
for (const Id nodeId : *nodeIdSets[i])
{
for (const std::tuple<Id, Id, std::vector<Id>>& edge :
m_hierarchyCache.getInheritanceEdgesForNodeId(nodeId, *nodeIdSets[(i + 1) % 2]))
{
Id sourceId = std::get<0>(edge);
Id targetId = std::get<1>(edge);
std::vector<Id> edgeIds = std::get<2>(edge);
if (!edgeIds.size() || (edgeIds.size() == 1 && graph->getEdgeById(edgeIds[0])))
{
continue;
}
// Set first 2 bits to 1 to avoid collisions
Id inheritanceEdgeId = ~(~size_t(0) >> 2) + inheritanceEdgeCount++;
Edge* inheritanceEdge = graph->createEdge(
inheritanceEdgeId, Edge::EDGE_INHERITANCE, graph->getNodeById(sourceId), graph->getNodeById(targetId));
inheritanceEdge->addComponentInheritanceChain(std::make_shared<TokenComponentInheritanceChain>(edgeIds));
}
}
}
}
void PersistentStorage::buildFilePathMaps()
{
TRACE();
@@ -2177,4 +2246,10 @@ void PersistentStorage::buildHierarchyCache()
m_hierarchyCache.createConnection(
edge.id, edge.sourceNodeId, edge.targetNodeId, sourceIsVisible, targetIsImplicit);
}
std::vector<StorageEdge> inheritanceEdges = m_sqliteIndexStorage.getEdgesByType(Edge::typeToInt(Edge::EDGE_INHERITANCE));
for (const StorageEdge& edge : inheritanceEdges)
{
m_hierarchyCache.createInheritance(edge.id, edge.sourceNodeId, edge.targetNodeId);
}
}
+1
View File
@@ -165,6 +165,7 @@ private:
void addComponentAccessToGraph(Graph* graph) const;
void addCompleteFlagsToSourceLocationCollection(SourceLocationCollection* collection) const;
void addInheritanceChainsToGraph(const std::vector<Id>& nodeIds, Graph* graph) const;
void buildFilePathMaps();
void buildSearchIndex();
+17
View File
@@ -4,6 +4,7 @@
#include "data/graph/Node.h"
#include "data/graph/token_component/TokenComponentAggregation.h"
#include "data/graph/token_component/TokenComponentInheritanceChain.h"
#include "utility/logging/logging.h"
#include "utility/utilityString.h"
@@ -134,6 +135,22 @@ void Edge::addComponentAggregation(std::shared_ptr<TokenComponentAggregation> co
}
}
void Edge::addComponentInheritanceChain(std::shared_ptr<TokenComponentInheritanceChain> component)
{
if (getComponent<TokenComponentInheritanceChain>())
{
LOG_ERROR("TokenComponentInheritanceChain has been set before!");
}
else if (m_type != EDGE_INHERITANCE)
{
LOG_ERROR("TokenComponentInheritanceChain can't be set on edge of type: " + getReadableTypeString());
}
else
{
addComponent(component);
}
}
std::string Edge::getUnderscoredTypeString(EdgeType type)
{
return utility::replace(utility::replace(getReadableTypeString(type), "-", "_"), " ", "_");
+2 -2
View File
@@ -8,8 +8,7 @@
class Node;
class TokenComponentAggregation;
class TokenComponentAccess;
class TokenComponentDataType;
class TokenComponentInheritanceChain;
class Edge
: public Token
@@ -57,6 +56,7 @@ public:
// Component setters
void addComponentAggregation(std::shared_ptr<TokenComponentAggregation> component);
void addComponentInheritanceChain(std::shared_ptr<TokenComponentInheritanceChain> component);
static std::string getUnderscoredTypeString(EdgeType type);
static std::string getReadableTypeString(EdgeType type);
+3 -1
View File
@@ -14,7 +14,9 @@
const Node::NodeTypeMask Node::NODE_NOT_VISIBLE = Node::NODE_NAMESPACE | Node::NODE_PACKAGE;
const Node::NodeTypeMask Node::NODE_USEABLE_TYPE = Node::NODE_NON_INDEXED | Node::NODE_BUILTIN_TYPE |
Node::NODE_BUILTIN_TYPE | Node::NODE_STRUCT | Node::NODE_CLASS | Node::NODE_INTERFACE | Node::NODE_TYPEDEF;
Node::NODE_STRUCT | Node::NODE_CLASS | Node::NODE_INTERFACE | Node::NODE_TYPEDEF;
const Node::NodeTypeMask Node::NODE_INHERITABLE_TYPE = Node::NODE_NON_INDEXED | Node::NODE_BUILTIN_TYPE |
NODE_TYPE | Node::NODE_STRUCT | Node::NODE_CLASS | Node::NODE_INTERFACE;
std::string Node::getUnderscoredTypeString(NodeType type)
{
+1
View File
@@ -56,6 +56,7 @@ public:
static const NodeTypeMask NODE_NOT_VISIBLE;
static const NodeTypeMask NODE_USEABLE_TYPE;
static const NodeTypeMask NODE_INHERITABLE_TYPE;
Node(Id id, NodeType type, NameHierarchy nameHierarchy, bool defined);
Node(const Node& other);
@@ -0,0 +1,23 @@
#ifndef TOKEN_COMPONENT_INHERITANCE_CHAIN_H
#define TOKEN_COMPONENT_INHERITANCE_CHAIN_H
#include "data/graph/token_component/TokenComponent.h"
class TokenComponentInheritanceChain
: public TokenComponent
{
public:
TokenComponentInheritanceChain(const std::vector<Id>& inheritanceEdgeIds)
: inheritanceEdgeIds(inheritanceEdgeIds)
{
}
virtual std::shared_ptr<TokenComponent> copy() const
{
return std::make_shared<TokenComponentInheritanceChain>(*this);
}
const std::vector<Id> inheritanceEdgeIds;
};
#endif // TOKEN_COMPONENT_INHERITANCE_CHAIN_H
+19 -2
View File
@@ -32,7 +32,8 @@ QPainterPath QtLineItemAngled::shape() const
void QtLineItemAngled::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget)
{
painter->setPen(pen());
QPen p = pen();
painter->setPen(p);
QPainterPath path;
@@ -169,7 +170,17 @@ void QtLineItemAngled::paint(QPainter* painter, const QStyleOptionGraphicsItem*
partRect = getArrowBoundingRect(poly);
if (drawRect.intersects(partRect) && m_showArrow)
{
drawArrow(poly, &path);
if (m_style.dashed)
{
QPainterPath arrowPath;
drawArrow(poly, &path, &arrowPath);
painter->drawPath(arrowPath);
}
else
{
drawArrow(poly, &path);
}
}
else
{
@@ -177,5 +188,11 @@ void QtLineItemAngled::paint(QPainter* painter, const QStyleOptionGraphicsItem*
}
}
if (m_style.dashed)
{
p.setStyle(Qt::DashLine);
painter->setPen(p);
}
painter->drawPath(path);
}
+7 -2
View File
@@ -336,7 +336,7 @@ QRectF QtLineItemBase::getArrowBoundingRect(const QPolygon& poly) const
return rect;
}
void QtLineItemBase::drawArrow(const QPolygon& poly, QPainterPath* path) const
void QtLineItemBase::drawArrow(const QPolygon& poly, QPainterPath* path, QPainterPath* arrowPath) const
{
int dir = getDirection(poly.at(1), poly.at(0));
@@ -364,7 +364,6 @@ void QtLineItemBase::drawArrow(const QPolygon& poly, QPainterPath* path) const
break;
}
if (m_style.arrowClosed)
{
path->lineTo(tip + toBack);
@@ -375,6 +374,12 @@ void QtLineItemBase::drawArrow(const QPolygon& poly, QPainterPath* path) const
path->lineTo(tip);
}
if (arrowPath)
{
path = arrowPath;
path->moveTo(tip);
}
path->lineTo(tip + toBack + toLeft);
if (m_style.arrowClosed)
+1 -1
View File
@@ -45,7 +45,7 @@ protected:
int getDirection(const QPointF& a, const QPointF& b) const;
QRectF getArrowBoundingRect(const QPolygon& poly) const;
void drawArrow(const QPolygon& poly, QPainterPath* path) const;
void drawArrow(const QPolygon& poly, QPainterPath* path, QPainterPath* arrowPath = nullptr) const;
void getPivotPoints(Vec2f* p, const Vec4i& in, const Vec4i& out, int offset, bool target) const;
+1
View File
@@ -398,6 +398,7 @@ MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward)
switch (node->getData()->getType())
{
case Node::NODE_NON_INDEXED:
case Node::NODE_CLASS:
case Node::NODE_STRUCT:
case Node::NODE_INTERFACE:
@@ -6,6 +6,7 @@
#include "component/view/GraphViewStyle.h"
#include "data/graph/Edge.h"
#include "data/graph/token_component/TokenComponentAggregation.h"
#include "data/graph/token_component/TokenComponentInheritanceChain.h"
#include "qt/graphics/QtLineItemAngled.h"
#include "qt/graphics/QtLineItemBezier.h"
#include "qt/graphics/QtLineItemStraight.h"
@@ -207,6 +208,15 @@ void QtGraphEdge::updateLine()
showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE;
}
if (getData())
{
TokenComponentInheritanceChain* componentInheritance = getData()->getComponent<TokenComponentInheritanceChain>();
if (componentInheritance && componentInheritance->inheritanceEdgeIds.size() > 1)
{
style.dashed = true;
}
}
child->updateLine(
owner->getBoundingRect(), target->getBoundingRect(),
owner->getParentBoundingRect(), target->getParentBoundingRect(),
@@ -262,9 +272,11 @@ void QtGraphEdge::onClick()
}
else
{
TokenComponentInheritanceChain* componentInheritance = getData()->getComponent<TokenComponentInheritanceChain>();
MessageActivateEdge msg(
getData()->getId(),
getData()->getType(),
componentInheritance ? Edge::EDGE_AGGREGATION : getData()->getType(),
getData()->getFrom()->getNameHierarchy(),
getData()->getTo()->getNameHierarchy()
);
@@ -274,6 +286,10 @@ void QtGraphEdge::onClick()
msg.aggregationIds =
utility::toVector<Id>(getData()->getComponent<TokenComponentAggregation>()->getAggregationIds());
}
else if (componentInheritance)
{
msg.aggregationIds = componentInheritance->inheritanceEdgeIds;
}
msg.dispatch();
}