From c63dd506beefa844ddeab587a71af94063357372 Mon Sep 17 00:00:00 2001 From: Aleks Kissinger Date: Wed, 27 Dec 2017 23:27:20 +0000 Subject: adding and deleting nodes now works --- tikzit/src/data/edge.cpp | 4 +-- tikzit/src/data/graph.cpp | 43 +++++++++++++--------- tikzit/src/data/graph.h | 10 +++--- tikzit/src/data/node.cpp | 7 ++-- tikzit/src/data/node.h | 4 +-- tikzit/src/data/nodestyle.cpp | 2 ++ tikzit/src/data/nodestyle.h | 2 +- tikzit/src/gui/nodeitem.cpp | 10 +++--- tikzit/src/gui/nodeitem.h | 2 +- tikzit/src/gui/tikzscene.cpp | 65 +++++++++++++++++++++++++++------ tikzit/src/gui/tikzscene.h | 20 ++++++----- tikzit/src/gui/undocommands.cpp | 79 +++++++++++++++++++++++++++++++++++++++-- tikzit/src/gui/undocommands.h | 32 ++++++++++++++++- tikzit/src/tikzit.cpp | 35 ++++-------------- tikzit/src/tikzit.h | 4 +-- 15 files changed, 232 insertions(+), 87 deletions(-) diff --git a/tikzit/src/data/edge.cpp b/tikzit/src/data/edge.cpp index 3ff6c6e..6802b2d 100644 --- a/tikzit/src/data/edge.cpp +++ b/tikzit/src/data/edge.cpp @@ -117,14 +117,14 @@ void Edge::updateControls() { } // TODO: calculate head and tail properly, not just for circles - if (_source->style().isNone()) { + if (_source->style()->isNone()) { _tail = src; } else { _tail = QPointF(src.x() + std::cos(outAngleR) * 0.1, src.y() + std::sin(outAngleR) * 0.1); } - if (_target->style().isNone()) { + if (_target->style()->isNone()) { _head = targ; } else { _head = QPointF(targ.x() + std::cos(inAngleR) * 0.1, diff --git a/tikzit/src/data/graph.cpp b/tikzit/src/data/graph.cpp index 4329928..ba9a4c6 100644 --- a/tikzit/src/data/graph.cpp +++ b/tikzit/src/data/graph.cpp @@ -1,6 +1,10 @@ #include "graph.h" #include +#include +#include +#include +#include Graph::Graph(QObject *parent) : QObject(parent) { @@ -12,30 +16,42 @@ Graph::~Graph() { } +// add a node. The graph claims ownership. +void Graph::addNode(Node *n) { + n->setParent(this); + _nodes << n; +} + +void Graph::addNode(Node *n, int index) +{ + n->setParent(this); + _nodes.insert(index, n); +} + void Graph::removeNode(Node *n) { // the node itself is not deleted, as it may still be referenced in an undo command. It will // be deleted when graph is, via QObject memory management. - _nodes.removeAll(n); - inEdges.remove(n); - outEdges.remove(n); + _nodes.removeOne(n); } -Edge *Graph::addEdge(Edge *e) + +void Graph::addEdge(Edge *e) { e->setParent(this); _edges << e; - outEdges.insert(e->source(), e); - inEdges.insert(e->target(), e); - return e; +} + +void Graph::addEdge(Edge *e, int index) +{ + e->setParent(this); + _edges.insert(index, e); } void Graph::removeEdge(Edge *e) { // the edge itself is not deleted, as it may still be referenced in an undo command. It will // be deleted when graph is, via QObject memory management. - _edges.removeAll(e); - outEdges.remove(e->source(), e); - inEdges.remove(e->target(), e); + _edges.removeOne(e); } GraphElementData *Graph::data() const @@ -153,11 +169,4 @@ void Graph::setBbox(const QRectF &bbox) _bbox = bbox; } -// add a node. The graph claims ownership. -Node *Graph::addNode(Node *n) { - n->setParent(this); - _nodes << n; - return n; -} - diff --git a/tikzit/src/data/graph.h b/tikzit/src/data/graph.h index 963def8..8856e5c 100644 --- a/tikzit/src/data/graph.h +++ b/tikzit/src/data/graph.h @@ -21,9 +21,11 @@ class Graph : public QObject public: explicit Graph(QObject *parent = 0); ~Graph(); - Node *addNode(Node *n); + void addNode(Node *n); + void addNode(Node *n, int index); void removeNode(Node *n); - Edge *addEdge(Edge *e); + void addEdge(Edge *e); + void addEdge(Edge *e, int index); void removeEdge(Edge *e); GraphElementData *data() const; @@ -45,8 +47,8 @@ public slots: private: QVector _nodes; QVector _edges; - QMultiHash inEdges; - QMultiHash outEdges; + //QMultiHash inEdges; + //QMultiHash outEdges; GraphElementData *_data; QRectF _bbox; }; diff --git a/tikzit/src/data/node.cpp b/tikzit/src/data/node.cpp index 1b8ccf8..f94a3df 100644 --- a/tikzit/src/data/node.cpp +++ b/tikzit/src/data/node.cpp @@ -6,7 +6,7 @@ Node::Node(QObject *parent) : QObject(parent) { _data = new GraphElementData(); - _style = NodeStyle(); + _style = noneStyle; _styleName = "none"; } @@ -69,12 +69,11 @@ void Node::setStyleName(const QString &styleName) void Node::attachStyle() { - if (_styleName == "none") _style = NodeStyle(); + if (_styleName == "none") _style = noneStyle; else _style = tikzit->nodeStyle(_styleName); } -NodeStyle Node::style() const +NodeStyle *Node::style() const { return _style; } - diff --git a/tikzit/src/data/node.h b/tikzit/src/data/node.h index 91b1725..ee70835 100644 --- a/tikzit/src/data/node.h +++ b/tikzit/src/data/node.h @@ -31,7 +31,7 @@ public: void setStyleName(const QString &styleName); void attachStyle(); - NodeStyle style() const; + NodeStyle *style() const; signals: @@ -42,7 +42,7 @@ private: QString _name; QString _label; QString _styleName; - NodeStyle _style; + NodeStyle *_style; GraphElementData *_data; }; diff --git a/tikzit/src/data/nodestyle.cpp b/tikzit/src/data/nodestyle.cpp index 109e2af..7eca791 100644 --- a/tikzit/src/data/nodestyle.cpp +++ b/tikzit/src/data/nodestyle.cpp @@ -1,5 +1,7 @@ #include "nodestyle.h" +NodeStyle *noneStyle = new NodeStyle(); + NodeStyle::NodeStyle() { name = "none"; diff --git a/tikzit/src/data/nodestyle.h b/tikzit/src/data/nodestyle.h index baf967c..00d1b20 100644 --- a/tikzit/src/data/nodestyle.h +++ b/tikzit/src/data/nodestyle.h @@ -21,6 +21,6 @@ public: int strokeThickness; }; -extern NodeStyle noneStyle; +extern NodeStyle *noneStyle; #endif // NODESTYLE_H diff --git a/tikzit/src/gui/nodeitem.cpp b/tikzit/src/gui/nodeitem.cpp index 40b4de9..71226f3 100644 --- a/tikzit/src/gui/nodeitem.cpp +++ b/tikzit/src/gui/nodeitem.cpp @@ -41,9 +41,9 @@ QRectF NodeItem::labelRect() const { return rect; } -void NodeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +void NodeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { - if (_node->style().isNone()) { + if (_node->style()->isNone()) { QColor c(180,180,200); painter->setPen(QPen(c)); painter->setBrush(QBrush(c)); @@ -57,10 +57,10 @@ void NodeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, painter->setBrush(Qt::NoBrush); painter->drawPath(shape()); } else { - QPen pen(_node->style().strokeColor); - pen.setWidth(_node->style().strokeThickness); + QPen pen(_node->style()->strokeColor); + pen.setWidth(_node->style()->strokeThickness); painter->setPen(pen); - painter->setBrush(QBrush(_node->style().fillColor)); + painter->setBrush(QBrush(_node->style()->fillColor)); painter->drawPath(shape()); } diff --git a/tikzit/src/gui/nodeitem.h b/tikzit/src/gui/nodeitem.h index d0f1e62..9a3edb0 100644 --- a/tikzit/src/gui/nodeitem.h +++ b/tikzit/src/gui/nodeitem.h @@ -18,7 +18,7 @@ public: NodeItem(Node *node); void readPos(); void writePos(); - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *); QVariant itemChange(GraphicsItemChange change, const QVariant &value); QPainterPath shape() const; QRectF boundingRect() const; diff --git a/tikzit/src/gui/tikzscene.cpp b/tikzit/src/gui/tikzscene.cpp index a900192..2d9b49f 100644 --- a/tikzit/src/gui/tikzscene.cpp +++ b/tikzit/src/gui/tikzscene.cpp @@ -17,7 +17,7 @@ TikzScene::TikzScene(TikzDocument *tikzDocument, QObject *parent) : TikzScene::~TikzScene() { } -Graph *TikzScene::graph() const +Graph *TikzScene::graph() { return _tikzDocument->graph(); } @@ -38,13 +38,13 @@ void TikzScene::graphReplaced() foreach (Edge *e, graph()->edges()) { EdgeItem *ei = new EdgeItem(e); - _edgeItems << ei; + _edgeItems.insert(e, ei); addItem(ei); } foreach (Node *n, graph()->nodes()) { NodeItem *ni = new NodeItem(n); - _nodeItems << ni; + _nodeItems.insert(n, ni); addItem(ni); } } @@ -267,7 +267,14 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) break; case ToolPalette::VERTEX: - // TODO + { + int gridSize = GLOBAL_SCALE / 8; + QPointF gridPos(round(mousePos.x()/gridSize)*gridSize, round(mousePos.y()/gridSize)*gridSize); + Node *n = new Node(); + n->setPoint(fromScreen(gridPos)); + AddNodeCommand *cmd = new AddNodeCommand(this, n); + _tikzDocument->undoStack()->push(cmd); + } break; case ToolPalette::EDGE: break; @@ -276,6 +283,44 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) } } +void TikzScene::keyReleaseEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { + QSet selNodes; + QSet selEdges; + getSelection(selNodes, selEdges); + + QMap deleteNodes; + QMap deleteEdges; + + for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) { + Node *n = _tikzDocument->graph()->nodes()[i]; + if (selNodes.contains(n)) deleteNodes.insert(i, n); + } + + for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) { + Edge *e = _tikzDocument->graph()->edges()[i]; + if (selEdges.contains(e) || + selNodes.contains(e->source()) || + selNodes.contains(e->target())) deleteEdges.insert(i, e); + } + + //qDebug() << "nodes:" << deleteNodes; + //qDebug() << "edges:" << deleteEdges; + DeleteCommand *cmd = new DeleteCommand(this, deleteNodes, deleteEdges, selEdges); + _tikzDocument->undoStack()->push(cmd); + } +} + +void TikzScene::getSelection(QSet &selNodes, QSet &selEdges) +{ + foreach (QGraphicsItem *gi, selectedItems()) { + if (NodeItem *ni = dynamic_cast(gi)) selNodes << ni->node(); + if (EdgeItem *ei = dynamic_cast(gi)) selEdges << ei->edge(); + } +} + + TikzDocument *TikzScene::tikzDocument() const { return _tikzDocument; @@ -287,11 +332,6 @@ void TikzScene::setTikzDocument(TikzDocument *tikzDocument) graphReplaced(); } -QVector TikzScene::edgeItems() const -{ - return _edgeItems; -} - void TikzScene::refreshAdjacentEdges(QList nodes) { if (nodes.empty()) return; @@ -303,7 +343,12 @@ void TikzScene::refreshAdjacentEdges(QList nodes) } } -QVector TikzScene::nodeItems() const +QMap &TikzScene::nodeItems() { return _nodeItems; } + +QMap &TikzScene::edgeItems() +{ + return _edgeItems; +} diff --git a/tikzit/src/gui/tikzscene.h b/tikzit/src/gui/tikzscene.h index db95d88..a9af34b 100644 --- a/tikzit/src/gui/tikzscene.h +++ b/tikzit/src/gui/tikzscene.h @@ -25,9 +25,9 @@ class TikzScene : public QGraphicsScene public: TikzScene(TikzDocument *tikzDocument, QObject *parent); ~TikzScene(); - Graph *graph() const; - QVector nodeItems() const; - QVector edgeItems() const; + Graph *graph(); + QMap &nodeItems(); + QMap &edgeItems(); void refreshAdjacentEdges(QList nodes); TikzDocument *tikzDocument() const; @@ -35,14 +35,16 @@ public: public slots: void graphReplaced(); + protected: - void mousePressEvent(QGraphicsSceneMouseEvent *event); - void mouseMoveEvent(QGraphicsSceneMouseEvent *event); - void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; private: TikzDocument *_tikzDocument; - QVector _nodeItems; - QVector _edgeItems; + QMap _nodeItems; + QMap _edgeItems; EdgeItem *_modifyEdgeItem; bool _firstControlPoint; @@ -51,6 +53,8 @@ private: int _oldBend; int _oldInAngle; int _oldOutAngle; + + void getSelection(QSet &selNodes, QSet &selEdges); }; #endif // TIKZSCENE_H diff --git a/tikzit/src/gui/undocommands.cpp b/tikzit/src/gui/undocommands.cpp index 54741c8..997bf75 100644 --- a/tikzit/src/gui/undocommands.cpp +++ b/tikzit/src/gui/undocommands.cpp @@ -1,4 +1,6 @@ #include "undocommands.h" +#include "nodeitem.h" +#include "edgeitem.h" MoveCommand::MoveCommand(TikzScene *scene, QMap oldNodePositions, @@ -8,8 +10,7 @@ MoveCommand::MoveCommand(TikzScene *scene, _scene(scene), _oldNodePositions(oldNodePositions), _newNodePositions(newNodePositions) -{ -} +{} void MoveCommand::undo() @@ -78,3 +79,77 @@ void EdgeBendCommand::redo() } } } + +DeleteCommand::DeleteCommand(TikzScene *scene, + QMap deleteNodes, + QMap deleteEdges, + QSet selEdges) : + _scene(scene), _deleteNodes(deleteNodes), + _deleteEdges(deleteEdges), _selEdges(selEdges) +{} + +void DeleteCommand::undo() +{ + for (auto it = _deleteNodes.begin(); it != _deleteNodes.end(); ++it) { + Node *n = it.value(); + _scene->graph()->addNode(n, it.key()); + NodeItem *ni = new NodeItem(n); + _scene->nodeItems().insert(n, ni); + _scene->addItem(ni); + ni->setSelected(true); + } + + for (auto it = _deleteEdges.begin(); it != _deleteEdges.end(); ++it) { + Edge *e = it.value(); + _scene->graph()->addEdge(e, it.key()); + EdgeItem *ei = new EdgeItem(e); + _scene->edgeItems().insert(e, ei); + _scene->addItem(ei); + + if (_selEdges.contains(e)) ei->setSelected(true); + } +} + +void DeleteCommand::redo() +{ + foreach (Edge *e, _deleteEdges.values()) { + EdgeItem *ei = _scene->edgeItems()[e]; + _scene->edgeItems().remove(e); + _scene->removeItem(ei); + delete ei; + + _scene->graph()->removeEdge(e); + } + + foreach (Node *n, _deleteNodes.values()) { + NodeItem *ni = _scene->nodeItems()[n]; + _scene->nodeItems().remove(n); + _scene->removeItem(ni); + delete ni; + + _scene->graph()->removeNode(n); + } +} + +AddNodeCommand::AddNodeCommand(TikzScene *scene, Node *node) : + _scene(scene), _node(node) +{} + +void AddNodeCommand::undo() +{ + NodeItem *ni = _scene->nodeItems()[_node]; + _scene->removeItem(ni); + _scene->nodeItems().remove(_node); + delete ni; + + _scene->graph()->removeNode(_node); +} + +void AddNodeCommand::redo() +{ + // TODO: get the current style + _scene->graph()->addNode(_node); + NodeItem *ni = new NodeItem(_node); + _scene->nodeItems().insert(_node, ni); + _scene->addItem(ni); +} diff --git a/tikzit/src/gui/undocommands.h b/tikzit/src/gui/undocommands.h index bb6a8e9..c1b4910 100644 --- a/tikzit/src/gui/undocommands.h +++ b/tikzit/src/gui/undocommands.h @@ -1,5 +1,8 @@ /** - * These classes store the data required to undo/redo a single UI action. + * All changes to a TikzDocument are done via subclasses of QUndoCommand. When a controller + * (e.g. TikzScene) gets input from the user to change the document, it will push one of + * these commands onto the TikzDocument's undo stack, which automatically calls the redo() + * method of the command. */ #ifndef UNDOCOMMANDS_H @@ -45,4 +48,31 @@ private: int _newOutAngle; }; +class DeleteCommand : public QUndoCommand +{ +public: + explicit DeleteCommand(TikzScene *scene, + QMap deleteNodes, + QMap deleteEdges, + QSet selEdges); + void undo() override; + void redo() override; +private: + TikzScene *_scene; + QMap _deleteNodes; + QMap _deleteEdges; + QSet _selEdges; +}; + +class AddNodeCommand : public QUndoCommand +{ +public: + explicit AddNodeCommand(TikzScene *scene, Node *node); + void undo() override; + void redo() override; +private: + TikzScene *_scene; + Node *_node; +}; + #endif // UNDOCOMMANDS_H diff --git a/tikzit/src/tikzit.cpp b/tikzit/src/tikzit.cpp index 94fc644..42d16e8 100644 --- a/tikzit/src/tikzit.cpp +++ b/tikzit/src/tikzit.cpp @@ -43,32 +43,11 @@ PropertyPalette *Tikzit::propertyPalette() const return _propertyPalette; } -//void Tikzit::createMenu() -//{ -// _mainMenu = new QMenuBar(0); -// QMenu *file = _mainMenu->addMenu(tr("&File")); -// QAction *aNew = file->addAction(tr("&New")); -// aNew->setShortcut(QKeySequence::New); -// QAction *aOpen = file->addAction(tr("&Open")); -// aOpen->setShortcut(QKeySequence::Open); - -// QMenu *view = _mainMenu->addMenu(tr("&View")); -// QAction *aZoomIn = view->addAction(tr("Zoom &In")); -// aZoomIn->setShortcut(QKeySequence::ZoomIn); -// QAction *aZoomOut = view->addAction(tr("Zoom &Out")); -// aZoomOut->setShortcut(QKeySequence::ZoomOut); - -// connect(aNew, SIGNAL(triggered()), this, SLOT(newDoc())); -// connect(aOpen, SIGNAL(triggered()), this, SLOT(open())); -// connect(aZoomIn, SIGNAL(triggered()), this, SLOT(zoomIn())); -// connect(aZoomOut, SIGNAL(triggered()), this, SLOT(zoomOut())); -//} - void Tikzit::loadStyles() { - _nodeStyles << NodeStyle("black dot", NodeShape::Circle, Qt::black, Qt::black, 1); - _nodeStyles << NodeStyle("white dot", NodeShape::Circle, Qt::white, Qt::black, 1); - _nodeStyles << NodeStyle("gray dot", NodeShape::Circle, Qt::gray, Qt::black, 1); + _nodeStyles << new NodeStyle("black dot", NodeShape::Circle, Qt::black, Qt::black, 1); + _nodeStyles << new NodeStyle("white dot", NodeShape::Circle, Qt::white, Qt::black, 1); + _nodeStyles << new NodeStyle("gray dot", NodeShape::Circle, Qt::gray, Qt::black, 1); } void Tikzit::newDoc() @@ -97,11 +76,11 @@ void Tikzit::removeWindow(MainWindow *w) } } -NodeStyle Tikzit::nodeStyle(QString name) +NodeStyle *Tikzit::nodeStyle(QString name) { - foreach (NodeStyle s , _nodeStyles) - if (s.name == name) return s; - return NodeStyle(name, NodeShape::Circle, Qt::white); + foreach (NodeStyle *s , _nodeStyles) + if (s->name == name) return s; + return noneStyle; //NodeStyle(name, NodeShape::Circle, Qt::white); } void Tikzit::open() diff --git a/tikzit/src/tikzit.h b/tikzit/src/tikzit.h index bf4f7f1..deb683e 100644 --- a/tikzit/src/tikzit.h +++ b/tikzit/src/tikzit.h @@ -43,7 +43,7 @@ public: MainWindow *activeWindow() const; void setActiveWindow(MainWindow *activeWindow); void removeWindow(MainWindow *w); - NodeStyle nodeStyle(QString name); + NodeStyle *nodeStyle(QString name); static QFont LABEL_FONT; // Ui::MainMenu *_mainMenuUi; @@ -61,7 +61,7 @@ private: PropertyPalette *_propertyPalette; QVector _windows; MainWindow *_activeWindow; - QVector _nodeStyles; + QVector _nodeStyles; }; -- cgit v1.2.3