diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 7211bbf7..6a49ccad 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -22,6 +22,8 @@ add_files( qt/view/graphElements/QtGraphEdge.h qt/view/graphElements/QtGraphNode.cpp qt/view/graphElements/QtGraphNode.h + qt/view/graphElements/QtGraphNodeMovable.cpp + qt/view/graphElements/QtGraphNodeMovable.h qt/view/QtCodeView.cpp qt/view/QtCodeView.h diff --git a/src/app/qt/view/QtGraphView.cpp b/src/app/qt/view/QtGraphView.cpp index ea07d5a8..647afb68 100644 --- a/src/app/qt/view/QtGraphView.cpp +++ b/src/app/qt/view/QtGraphView.cpp @@ -12,6 +12,7 @@ #include "qt/QtWidgetWrapper.h" #include "qt/view/graphElements/QtGraphEdge.h" #include "qt/view/graphElements/QtGraphNode.h" +#include "qt/view/graphElements/QtGraphNodeMovable.h" QtGraphView::QtGraphView(ViewLayout* viewLayout) : GraphView(viewLayout) @@ -70,15 +71,41 @@ QGraphicsView* QtGraphView::getView() void QtGraphView::doRebuildGraph(const std::vector& nodes, const std::vector& edges) { - doClear(); - QGraphicsView* view = getView(); if (view != NULL) { + std::map> weakNodes; // used when creating the edges + std::list> newNodes; // temporary stores all nodes (existing and newly created) needed in the new graph + // this is a relatively easy and cheap way to save existing nodes that are still needed + + // create nodes (or find existing nodes for re-use) for(unsigned int i = 0; i < nodes.size(); i++) { std::shared_ptr newNode = findOrCreateNode(view, nodes[i]); + newNodes.push_back(newNode); + weakNodes[nodes[i].tokenId] = newNode; + } + + doClear(); + m_nodes = newNodes; + + // create edges + for(unsigned int i = 0; i < edges.size(); i++) + { + if (weakNodes.find(edges[i].ownerId) != weakNodes.end() && weakNodes.find(edges[i].targetId) != weakNodes.end()) + { + if (weakNodes[edges[i].ownerId].expired() == false && weakNodes[edges[i].targetId].expired() == false) + { + std::shared_ptr newEdge = std::make_shared(weakNodes[edges[i].ownerId], weakNodes[edges[i].targetId]); + view->scene()->addItem(newEdge.get()); + if(weakNodes[edges[i].ownerId].lock()->addOutEdge(newEdge)) + { + weakNodes[edges[i].targetId].lock()->addInEdge(newEdge); + m_edges.push_back(newEdge); + } + } + } } } else @@ -126,10 +153,18 @@ std::shared_ptr QtGraphView::createNode(QGraphicsView* view, const Du { if(view != NULL) { - std::shared_ptr newNode = std::make_shared(node.position, node.name, node.tokenId); + std::shared_ptr newNode = std::make_shared(node.position, node.name, node.tokenId); view->scene()->addItem(newNode.get()); m_nodes.push_back(newNode); + // create debug sub node + std::shared_ptr subNode = std::make_shared(Vec2i(10, 30), node.name + " member", 666); + subNode->setParentItem(newNode.get()); + view->scene()->addItem(subNode.get()); + newNode->addSubNode(subNode); + + subNode->setRect(0, 0, 80, 20); + return newNode; } else diff --git a/src/app/qt/view/graphElements/QtGraphEdge.cpp b/src/app/qt/view/graphElements/QtGraphEdge.cpp index ddf88915..31193700 100644 --- a/src/app/qt/view/graphElements/QtGraphEdge.cpp +++ b/src/app/qt/view/graphElements/QtGraphEdge.cpp @@ -17,7 +17,7 @@ QtGraphEdge::QtGraphEdge(const std::weak_ptr& owner, const std::weak_ } else { - LOG_WARNING("Either the owner or the target node could is null."); + LOG_WARNING("Either the owner or the target node is null."); } QPen blackPen(Qt::black); @@ -57,3 +57,27 @@ void QtGraphEdge::targetMoved() LOG_WARNING("Target node is null."); } } + +void QtGraphEdge::removeEdgeFromScene() +{ + std::shared_ptr node = m_owner.lock(); + + if(node != NULL) + { + node->removeOutEdge(this); + } + else + { + LOG_WARNING("Target node is null."); + } +} + +std::weak_ptr QtGraphEdge::getOwner() +{ + return m_owner; +} + +std::weak_ptr QtGraphEdge::getTarget() +{ + return m_target; +} diff --git a/src/app/qt/view/graphElements/QtGraphEdge.h b/src/app/qt/view/graphElements/QtGraphEdge.h index 4437b577..fe130da3 100644 --- a/src/app/qt/view/graphElements/QtGraphEdge.h +++ b/src/app/qt/view/graphElements/QtGraphEdge.h @@ -20,6 +20,11 @@ public: virtual void ownerMoved(); virtual void targetMoved(); + virtual void removeEdgeFromScene(); + + virtual std::weak_ptr getOwner(); + virtual std::weak_ptr getTarget(); + private: std::weak_ptr m_owner; std::weak_ptr m_target; diff --git a/src/app/qt/view/graphElements/QtGraphNode.cpp b/src/app/qt/view/graphElements/QtGraphNode.cpp index 4e070cb5..621ed802 100644 --- a/src/app/qt/view/graphElements/QtGraphNode.cpp +++ b/src/app/qt/view/graphElements/QtGraphNode.cpp @@ -20,6 +20,15 @@ QtGraphNode::QtGraphNode(const Vec2i& position, const std::string& name, const I QtGraphNode::~QtGraphNode() { + std::list >::iterator it = m_inEdges.begin(); + for(it; it != m_inEdges.end(); it++) + { + std::shared_ptr edge = it->lock(); + if(edge != NULL) + { + edge->removeEdgeFromScene(); + } + } } std::string QtGraphNode::getName() @@ -32,61 +41,63 @@ Vec2i QtGraphNode::getPosition() return Vec2i(this->scenePos().x(), this->scenePos().y()); } -void QtGraphNode::addOutEdge(const std::shared_ptr& edge) -{ - m_outEdges.push_back(edge); -} - -void QtGraphNode::addInEdge(const std::weak_ptr& edge) -{ - m_inEdges.push_back(edge); -} - -void QtGraphNode::removeOutEdge(const std::shared_ptr& edge) +bool QtGraphNode::addOutEdge(const std::shared_ptr& edge) { std::list >::iterator it = m_outEdges.begin(); for(it; it != m_outEdges.end(); it++) { - if(*it == edge) + if ((*it)->getOwner().lock() == edge->getOwner().lock() && (*it)->getTarget().lock() == edge->getTarget().lock()) { - m_outEdges.erase(it, it); + return false; + } + } + + m_outEdges.push_back(edge); + return true; +} + +bool QtGraphNode::addInEdge(const std::weak_ptr& edge) +{ + std::list >::iterator it = m_inEdges.begin(); + for(it; it != m_inEdges.end(); it++) + { + std::shared_ptr existingEdge = it->lock(); + if(existingEdge != NULL) + { + if (existingEdge->getOwner().lock() == edge.lock()->getOwner().lock() && + existingEdge->getTarget().lock() == edge.lock()->getTarget().lock()) + { + return false; + } + } + } + + m_inEdges.push_back(edge); + return true; +} + +void QtGraphNode::removeOutEdge(GraphEdge* edge) +{ + std::list >::iterator it = m_outEdges.begin(); + while(it != m_outEdges.end()) + { + if((*it).get() == edge) + { + m_outEdges.erase(it); break; } + + ++it; } } -void QtGraphNode::mousePressEvent(QGraphicsSceneMouseEvent *event) +void QtGraphNode::addSubNode(const std::shared_ptr& node) { - m_mouseOffset.x = event->pos().x(); - m_mouseOffset.y = event->pos().y(); + m_subNodes.push_back(node); +} +void QtGraphNode::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ MessageActivateToken message(m_tokenId); message.dispatch(); } - -void QtGraphNode::mouseMoveEvent(QGraphicsSceneMouseEvent *event) -{ - this->setPos(event->scenePos().x() - m_mouseOffset.x, event->scenePos().y() - m_mouseOffset.y); - - Vec2i p(event->scenePos().x(), event->scenePos().y()); - - std::list >::iterator it = m_outEdges.begin(); - for(it; it != m_outEdges.end(); it++) - { - (*it)->ownerMoved(); - } - - std::list >::iterator it2 = m_inEdges.begin(); - for(it2; it2 != m_inEdges.end(); it2++) - { - std::shared_ptr edge = it2->lock(); - if(edge.get() != NULL) - { - edge->targetMoved(); - } - else - { - m_inEdges.erase(it2, it2); - } - } -} diff --git a/src/app/qt/view/graphElements/QtGraphNode.h b/src/app/qt/view/graphElements/QtGraphNode.h index a7e9a9f7..da8e5e03 100644 --- a/src/app/qt/view/graphElements/QtGraphNode.h +++ b/src/app/qt/view/graphElements/QtGraphNode.h @@ -21,21 +21,23 @@ public: virtual std::string getName(); virtual Vec2i getPosition(); - virtual void addOutEdge(const std::shared_ptr& edge); - virtual void addInEdge(const std::weak_ptr& edge); + virtual bool addOutEdge(const std::shared_ptr& edge); + virtual bool addInEdge(const std::weak_ptr& edge); - virtual void removeOutEdge(const std::shared_ptr& edge); + virtual void removeOutEdge(GraphEdge* edge); - void mousePressEvent(QGraphicsSceneMouseEvent *event); - void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + virtual void addSubNode(const std::shared_ptr& node); + + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event); + +protected: + std::list > m_outEdges; + std::list > m_inEdges; private: QGraphicsTextItem* m_text; - std::list > m_outEdges; - std::list > m_inEdges; - - Vec2i m_mouseOffset; + std::list > m_subNodes; }; #endif // QT_GRAPH_NODE_H diff --git a/src/app/qt/view/graphElements/QtGraphNodeMovable.cpp b/src/app/qt/view/graphElements/QtGraphNodeMovable.cpp new file mode 100644 index 00000000..f01250a7 --- /dev/null +++ b/src/app/qt/view/graphElements/QtGraphNodeMovable.cpp @@ -0,0 +1,48 @@ +#include "qt/view/graphElements/QtGraphNodeMovable.h" + +#include "qgraphicssceneevent.h" + +#include "qt/view/graphElements/QtGraphEdge.h" + +QtGraphNodeMovable::QtGraphNodeMovable(const Vec2i& position, const std::string& name, const Id tokenId) + : QtGraphNode(position, name, tokenId) +{ +} + +QtGraphNodeMovable::~QtGraphNodeMovable() +{ +} + +void QtGraphNodeMovable::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + m_mouseOffset.x = event->pos().x(); + m_mouseOffset.y = event->pos().y(); +} + +void QtGraphNodeMovable::mouseMoveEvent(QGraphicsSceneMouseEvent* event) +{ + this->setPos(event->scenePos().x() - m_mouseOffset.x, event->scenePos().y() - m_mouseOffset.y); + + Vec2i p(event->scenePos().x(), event->scenePos().y()); + + std::list >::iterator it = m_outEdges.begin(); + for(it; it != m_outEdges.end(); it++) + { + (*it)->ownerMoved(); + } + + std::list >::iterator it2 = m_inEdges.begin(); + while(it2 != m_inEdges.end()) + { + std::shared_ptr edge = it2->lock(); + if(edge.get() != NULL) + { + edge->targetMoved(); + ++it2; + } + else + { + m_inEdges.erase(it2++); + } + } +} \ No newline at end of file diff --git a/src/app/qt/view/graphElements/QtGraphNodeMovable.h b/src/app/qt/view/graphElements/QtGraphNodeMovable.h new file mode 100644 index 00000000..97ce2f2c --- /dev/null +++ b/src/app/qt/view/graphElements/QtGraphNodeMovable.h @@ -0,0 +1,19 @@ +#ifndef QT_GRAPH_NODE_MOVABLE_H +#define QT_GRAPH_NODE_MOVABLE_H + +#include "qt/view/graphElements/QtGraphNode.h" + +class QtGraphNodeMovable : public QtGraphNode +{ +public: + QtGraphNodeMovable(const Vec2i& position, const std::string& name, const Id tokenId); + virtual ~QtGraphNodeMovable(); + + void mousePressEvent(QGraphicsSceneMouseEvent* event); + void mouseMoveEvent(QGraphicsSceneMouseEvent* event); + +private: + Vec2i m_mouseOffset; +}; + +#endif // QT_GRAPH_NODE_MOVABLE_H \ No newline at end of file diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index 7a493bb9..ee11bbeb 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -22,6 +22,8 @@ void GraphController::handleMessage(MessageActivateToken* message) if (view != NULL) { std::string name = m_graphAccess->getNameForNodeWithId(message->tokenId); + std::vector > rawEdges = m_graphAccess->getConnectedEdges(message->tokenId); + std::vector neighbourIds = m_graphAccess->getIdsOfNeighbours(message->tokenId); DummyNode node(name, message->tokenId, Vec2i(0,0)); @@ -30,6 +32,48 @@ void GraphController::handleMessage(MessageActivateToken* message) std::vector edges; + int offset = 20; // temporary + + for(unsigned int i = 0; i < rawEdges.size(); i++) + { + edges.push_back(DummyEdge(rawEdges[i].first, rawEdges[i].second)); + + Id newId = 0; + + if (rawEdges[i].first != message->tokenId) + { + name = m_graphAccess->getNameForNodeWithId(rawEdges[i].first); + newId = rawEdges[i].first; + } + else + { + name = m_graphAccess->getNameForNodeWithId(rawEdges[i].second); + newId = rawEdges[i].second; + } + + nodes.push_back(DummyNode(name, newId, Vec2i(0, offset))); + offset += 20; + } + + // find edges between neighbour nodes + for(unsigned int i = 1; i < nodes.size(); i++) + { + std::vector> newEdges = m_graphAccess->getConnectedEdges(nodes[i].tokenId); + for(unsigned int j = 0; j < newEdges.size(); j++) + { + if (std::find(neighbourIds.begin(), neighbourIds.end(), newEdges[j].first) != neighbourIds.end() && + std::find(neighbourIds.begin(), neighbourIds.end(), newEdges[j].second) != neighbourIds.end()) + { + DummyEdge newEdge(newEdges[j].first, newEdges[j].second); + + if (std::find(edges.begin(), edges.end(), newEdge) == edges.end()) + { + edges.push_back(newEdge); + } + } + } + } + view->rebuildGraph(nodes, edges); } } diff --git a/src/lib/component/view/graphElements/GraphEdge.h b/src/lib/component/view/graphElements/GraphEdge.h index 7f901125..246caf2d 100644 --- a/src/lib/component/view/graphElements/GraphEdge.h +++ b/src/lib/component/view/graphElements/GraphEdge.h @@ -3,6 +3,8 @@ #include +#include "utility/types.h" + class GraphNode; class GraphEdge @@ -13,20 +15,39 @@ public: virtual void ownerMoved() = 0; virtual void targetMoved() = 0; + + virtual void removeEdgeFromScene() = 0; + + virtual std::weak_ptr getOwner() = 0; + virtual std::weak_ptr getTarget() = 0; }; // temporary data structure for (visual) graph creation process struct DummyEdge { public: - DummyEdge(const std::weak_ptr o, const std::weak_ptr t) - : owner(o) - , target(t) + DummyEdge(const Id o, const Id t) + : ownerId(o) + , targetId(t) { } - std::weak_ptr owner; - std::weak_ptr target; + Id ownerId; + Id targetId; + + bool operator==(const DummyEdge& other) const + { + if(ownerId == other.ownerId && targetId == other.targetId) + { + return true; + } + return false; + } + + bool operator!=(const DummyEdge& other) const + { + return !(*this == other); + } }; #endif // GRAPH_EDGE_H \ No newline at end of file diff --git a/src/lib/component/view/graphElements/GraphNode.h b/src/lib/component/view/graphElements/GraphNode.h index 50ea36d2..ed70c4a7 100644 --- a/src/lib/component/view/graphElements/GraphNode.h +++ b/src/lib/component/view/graphElements/GraphNode.h @@ -18,10 +18,12 @@ public: Id getTokenId(); virtual Vec2i getPosition() = 0; - virtual void addOutEdge(const std::shared_ptr& edge) = 0; - virtual void addInEdge(const std::weak_ptr& edge) = 0; + virtual bool addOutEdge(const std::shared_ptr& edge) = 0; + virtual bool addInEdge(const std::weak_ptr& edge) = 0; - virtual void removeOutEdge(const std::shared_ptr& edge) = 0; + virtual void removeOutEdge(GraphEdge* edge) = 0; + + virtual void addSubNode(const std::shared_ptr& node) = 0; protected: Id m_tokenId; diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 4ed811d2..f9dcf1d1 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -239,6 +239,63 @@ std::vector Storage::getNamesForNodesWithNamePrefix(const std::stri return names; } +std::vector Storage::getIdsOfNeighbours(const Id id) const +{ + std::vector result; + + Node* node = m_graph.findNode([&](Node* node){ + return node->getId() == id; + }); + + if (node != NULL) + { + std::map addedIds; + addedIds[id] = true; + + node->forEachEdge( + [&result, &addedIds](Edge* e) + { + Id fromId = e->getFrom()->getId(); + if (addedIds.find(fromId) == addedIds.end()) + { + result.push_back(fromId); + addedIds[fromId] = true; + } + + Id toId = e->getTo()->getId(); + if (addedIds.find(toId) == addedIds.end()) + { + result.push_back(toId); + addedIds[toId] = true; + } + } + ); + } + + return result; +} + +std::vector> Storage::getConnectedEdges(const Id id) const +{ + std::vector> result; + + Node* node = m_graph.findNode([&](Node* node){ + return node->getId() == id; + }); + + if (node != NULL) + { + node->forEachEdge( + [&result, &id](Edge* e) + { + result.push_back(std::pair(e->getFrom()->getId(), e->getTo()->getId())); + } + ); + } + + return result; +} + TokenLocationCollection Storage::getTokenLocationsForTokenId(Id id) const { TokenLocationCollection ret; diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index c2c32fcd..ac9de834 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -57,6 +57,8 @@ public: virtual Id getIdForNodeWithName(const std::string& name) const; virtual std::string getNameForNodeWithId(Id id) const; virtual std::vector getNamesForNodesWithNamePrefix(const std::string& prefix) const; + virtual std::vector getIdsOfNeighbours(const Id id) const; + virtual std::vector> getConnectedEdges(const Id id) const; // LocationAccess implementation virtual TokenLocationCollection getTokenLocationsForTokenId(Id locationId) const; diff --git a/src/lib/data/access/GraphAccess.h b/src/lib/data/access/GraphAccess.h index 64cd5424..eafe50e8 100644 --- a/src/lib/data/access/GraphAccess.h +++ b/src/lib/data/access/GraphAccess.h @@ -16,6 +16,8 @@ public: virtual Id getIdForNodeWithName(const std::string& name) const = 0; virtual std::string getNameForNodeWithId(Id id) const = 0; virtual std::vector getNamesForNodesWithNamePrefix(const std::string& prefix) const = 0; + virtual std::vector getIdsOfNeighbours(const Id id) const = 0; + virtual std::vector> getConnectedEdges(const Id id) const = 0; };