diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/data/graph.cpp | 4 | ||||
-rw-r--r-- | src/data/tikzdocument.cpp | 1 | ||||
-rw-r--r-- | src/gui/mainmenu.cpp | 18 | ||||
-rw-r--r-- | src/gui/mainmenu.h | 2 | ||||
-rw-r--r-- | src/gui/mainmenu.ui | 25 | ||||
-rw-r--r-- | src/gui/tikzscene.cpp | 94 | ||||
-rw-r--r-- | src/gui/tikzscene.h | 2 | ||||
-rw-r--r-- | src/gui/undocommands.cpp | 48 | ||||
-rw-r--r-- | src/gui/undocommands.h | 16 |
9 files changed, 206 insertions, 4 deletions
diff --git a/src/data/graph.cpp b/src/data/graph.cpp index 24a17a5..979423e 100644 --- a/src/data/graph.cpp +++ b/src/data/graph.cpp @@ -269,10 +269,10 @@ QString Graph::tikz() code << ")";
foreach (Edge *e1, p->edges()) {
- e1->setTikzLine(line);
- e1->updateData();
code << "\n\t\t\t to ";
line++;
+ e1->setTikzLine(line);
+ e1->updateData();
GraphElementData *pd = e1->data()->pathData();
if (!pd->isEmpty())
diff --git a/src/data/tikzdocument.cpp b/src/data/tikzdocument.cpp index 1099779..633242d 100644 --- a/src/data/tikzdocument.cpp +++ b/src/data/tikzdocument.cpp @@ -90,6 +90,7 @@ void TikzDocument::open(QString fileName) refreshTikz();
setClean();
} else {
+ // TODO: should not quietly fail to open
newGraph->deleteLater();
_parseSuccess = false;
}
diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp index 7b7623b..092d8b4 100644 --- a/src/gui/mainmenu.cpp +++ b/src/gui/mainmenu.cpp @@ -239,6 +239,18 @@ void MainMenu::on_actionMerge_Nodes_triggered() tikzit->activeWindow()->tikzScene()->mergeNodes(); } +void MainMenu::on_actionMake_Path_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->makePath(); +} + +void MainMenu::on_actionSplit_Path_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->splitPath(); +} + // Tikz void MainMenu::on_actionParse_triggered() @@ -333,8 +345,10 @@ void MainMenu::on_actionZoom_Out_triggered() void MainMenu::on_actionShow_Node_Labels_triggered() { - tikzit->activeWindow()->tikzScene()->setDrawNodeLabels(ui.actionShow_Node_Labels->isChecked()); - tikzit->activeWindow()->tikzScene()->invalidate(); + if (tikzit->activeWindow() != 0) { + tikzit->activeWindow()->tikzScene()->setDrawNodeLabels(ui.actionShow_Node_Labels->isChecked()); + tikzit->activeWindow()->tikzScene()->invalidate(); + } } void MainMenu::on_actionAbout_triggered() diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h index 8268802..431e43a 100644 --- a/src/gui/mainmenu.h +++ b/src/gui/mainmenu.h @@ -67,6 +67,8 @@ public slots: void on_actionExtendRight_triggered(); void on_actionReverse_Edge_Direction_triggered(); void on_actionMerge_Nodes_triggered(); + void on_actionMake_Path_triggered(); + void on_actionSplit_Path_triggered(); // Tools void on_actionParse_triggered(); diff --git a/src/gui/mainmenu.ui b/src/gui/mainmenu.ui index 46f4881..2e390f9 100644 --- a/src/gui/mainmenu.ui +++ b/src/gui/mainmenu.ui @@ -58,6 +58,13 @@ <addaction name="actionRotateCW"/> <addaction name="actionRotateCCW"/> </widget> + <widget class="QMenu" name="menuPath"> + <property name="title"> + <string>Path</string> + </property> + <addaction name="actionMake_Path"/> + <addaction name="actionSplit_Path"/> + </widget> <addaction name="actionUndo"/> <addaction name="actionRedo"/> <addaction name="separator"/> @@ -74,6 +81,8 @@ <addaction name="menuTransform"/> <addaction name="actionReverse_Edge_Direction"/> <addaction name="actionMerge_Nodes"/> + <addaction name="separator"/> + <addaction name="menuPath"/> </widget> <widget class="QMenu" name="menuTikz"> <property name="title"> @@ -460,6 +469,22 @@ <string>Ctrl+Shift+Space</string> </property> </action> + <action name="actionMake_Path"> + <property name="text"> + <string>Make Path</string> + </property> + <property name="shortcut"> + <string>Ctrl+P</string> + </property> + </action> + <action name="actionSplit_Path"> + <property name="text"> + <string>Split Path</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+P</string> + </property> + </action> <addaction name="menuFile"/> <addaction name="menuEdit"/> <addaction name="menuView"/> diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index 087f621..cebf5be 100644 --- a/src/gui/tikzscene.cpp +++ b/src/gui/tikzscene.cpp @@ -27,6 +27,7 @@ #include <QDebug> #include <QClipboard> #include <QInputDialog> +#include <QMessageBox> #include <cmath> #include <delimitedstringvalidator.h> #include <QSettings> @@ -295,6 +296,99 @@ void TikzScene::reverseSelectedEdges() _tikzDocument->undoStack()->push(cmd); } +void TikzScene::makePath() +{ + QSet<Node*> selNodes; + QSet<Edge*> edges; + getSelection(selNodes, edges); + + // if no edges are selected, try to infer edges from nodes + if (edges.isEmpty()) { + foreach(Edge *e, graph()->edges()) { + if (selNodes.contains(e->source()) && selNodes.contains(e->target())) + edges << e; + } + } + + if (edges.size() < 2) { + //QMessageBox::warning(nullptr, "Error", "Paths must contain at least 2 edges."); + return; + } + + foreach (Edge *e, edges) { + if (e->path() != nullptr) { + //QMessageBox::warning(nullptr, "Error", "Edges must not already be in another path."); + // TODO: maybe we want to automatically split paths if edges are in a path already? + return; + } + } + + // try to turn selected edges into one contiguous chain or cycle, recording + // which edges need to be flipped. + + // n.b. this is O(n^2) in path length. This could be optimised by saving + // vertex neighbourhoods, but probably doesn't win anything for n < 100. + + QSet<Edge*> flip; + QVector<Edge*> p; + int pLen = -1; + + // keep going as long as 'p' grows + while (pLen < p.length()) { + pLen = p.length(); + Edge *e = nullptr; + foreach (e, edges) { + Node *s = e->source(); + Node *t = e->target(); + if (p.isEmpty()) { + p.append(e); + break; + } + + Node *head = (flip.contains(p.first())) ? p.first()->target() : p.first()->source(); + Node *tail = (flip.contains(p.last())) ? p.last()->source() : p.last()->target(); + + if (s == head || t == head) { + if (s == head) flip << e; + p.prepend(e); + break; + } + + if (s == tail || t == tail) { + if (t == tail) flip << e; + p.append(e); + break; + } + } + + if (e) edges.remove(e); + } + + if (!edges.isEmpty()) { + QMessageBox::warning(nullptr, "Error", "Selected edges do not form a path."); + return; + } + + //qDebug() << p; + //qDebug() << flip; + + QMap<Edge*, GraphElementData*> oldEdgeData; + foreach (Edge *e, p) { + if (e != p.first()) oldEdgeData[e] = e->data()->copy(); + } + qDebug() << oldEdgeData; + + _tikzDocument->undoStack()->beginMacro("Make Path"); + _tikzDocument->undoStack()->push(new ReverseEdgesCommand(this, flip)); + _tikzDocument->undoStack()->push(new MakePathCommand(this, p, oldEdgeData)); + _tikzDocument->undoStack()->endMacro(); +} + +void TikzScene::splitPath() +{ + // TODO: stub +} + void TikzScene::refreshZIndices() { qreal z = 0.0; diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h index 2e7baa5..5996263 100644 --- a/src/gui/tikzscene.h +++ b/src/gui/tikzscene.h @@ -81,6 +81,8 @@ public: void reverseSelectedEdges(); + void makePath(); + void splitPath(); void getSelection(QSet<Node*> &selNodes, QSet<Edge*> &selEdges) const; QSet<Node*> getSelectedNodes() const; diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index 9a1ef34..a07f251 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -594,3 +594,51 @@ void ReverseEdgesCommand::redo() GraphUpdateCommand::redo(); } + +MakePathCommand::MakePathCommand(TikzScene *scene, + const QVector<Edge *> &edgeList, + const QMap<Edge *, GraphElementData *> &oldEdgeData, + QUndoCommand *parent) : + GraphUpdateCommand(scene, parent), + _edgeList(edgeList), _oldEdgeData(oldEdgeData) +{ +} + +void MakePathCommand::undo() +{ + Path *p = _edgeList.first()->path(); + p->removeEdges(); + _scene->graph()->removePath(p); + + foreach (Edge *e, _edgeList) { + if (e != _edgeList.first()) { + // setData transfers ownership, so make a copy + e->setData(_oldEdgeData[e]->copy()); + } + } + + GraphUpdateCommand::undo(); +} + +void MakePathCommand::redo() +{ + GraphElementData *npd = _edgeList.first()->data()->nonPathData(); + GraphElementData *d; + + Path *p = new Path(); + foreach (Edge *e, _edgeList) { + p->addEdge(e); + + if (e != _edgeList.first()) { + d = e->data()->pathData(); + d->mergeData(npd); + e->setData(d); + } + } + + delete npd; + + _scene->graph()->addPath(p); + + GraphUpdateCommand::redo(); +} diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h index 40f0a3b..a1daa07 100644 --- a/src/gui/undocommands.h +++ b/src/gui/undocommands.h @@ -255,4 +255,20 @@ private: QVector<Edge*> _newEdgeOrder; }; +class MakePathCommand : public GraphUpdateCommand +{ +public: + explicit MakePathCommand(TikzScene *scene, + const QVector<Edge*> &edgeList, + const QMap<Edge*,GraphElementData*> &oldEdgeData, + QUndoCommand *parent = nullptr); + void undo() override; + void redo() override; +private: + QVector<Edge*> _edgeList; + + // creating path clobbers data on all but first edge + QMap<Edge*,GraphElementData*> _oldEdgeData; +}; + #endif // UNDOCOMMANDS_H |