diff options
52 files changed, 1816 insertions, 246 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 9f2828f..93060cd 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -5,37 +5,73 @@ branches: - master image: + - macos - Ubuntu1804 - Visual Studio 2015 configuration: - release -install: - - cmd: choco install winflexbison - - cmd: 'C:\Qt\5.11.2\mingw53_32\bin\qtenv2.bat' - - cmd: cd C:\projects\tikzit - - sh: sudo apt-get update - - sh: sudo apt-get -y install flex bison qt5-default libpoppler-dev libpoppler-qt5-dev - -before_build: - - ps: (new-object net.webclient).DownloadFile('http://tikzit.github.io/download/win32-deps.zip', 'c:\projects\tikzit\win32-deps.zip') - - cmd: 7z x win32-deps.zip - -build_script: - - qmake -v - - qmake - - cmd: mingw32-make - - sh: make - -after_build: - - sh: ./deploy-linux.sh - - sh: mv dist/tikzit.tar.gz tikzit-linux.tar.gz - #- sh: appveyor PushArtifact tikzit-linux.tar.gz - - sh: python scripts/gh-push.py tikzit-linux.tar.gz - - cmd: deploy-win.bat - - cmd: move dist\tikzit.zip tikzit-win.zip - - cmd: python scripts\gh-push.py tikzit-win.zip - #- cmd: appveyor PushArtifact tikzit-win.zip +for: +- + matrix: + only: + - image: macos + + install: + - brew update + - brew install qt5 + - brew link --force qt5 + - brew install poppler + + build_script: + - qmake -v + - qmake -r + - make + + after_build: + - '(chmod +x deploy-osx.sh && ./deploy-osx.sh && mv tikzit.dmg tikzit-osx.dmg)' + - python scripts/gh-push.py tikzit-osx.dmg +- + matrix: + only: + - image: Ubuntu1804 + + install: + - sudo apt-get update + - sudo apt-get -y install flex bison qt5-default libpoppler-dev libpoppler-qt5-dev + + build_script: + - qmake -v + - qmake + - make + + after_build: + - ./deploy-linux.sh + - mv dist/tikzit.tar.gz tikzit-linux.tar.gz + - python scripts/gh-push.py tikzit-linux.tar.gz +- + matrix: + only: + - image: Visual Studio 2015 + + install: + - choco install winflexbison + - 'C:\Qt\5.12\mingw73_32\bin\qtenv2.bat' + - cd C:\projects\tikzit + + before_build: + - ps: (new-object net.webclient).DownloadFile('http://tikzit.github.io/download/win32-deps.zip', 'c:\projects\tikzit\win32-deps.zip') + - 7z x win32-deps.zip + + build_script: + - qmake -v + - qmake + - mingw32-make + + after_build: + - deploy-win.bat + - move dist\tikzit.zip tikzit-win.zip + - python scripts\gh-push.py tikzit-win.zip @@ -1,3 +1,4 @@ +*.pro.user*
scripts/*.pyc
tex/sample/Output
tex/sample/sample.pdf
diff --git a/.travis.yml b/.travis.yml index 056b543..787a6cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ install: - brew update - brew install qt5 - brew link --force qt5 -- brew install poppler --with-qt +- brew install poppler script: - qmake -v - qmake -r diff --git a/debian/changelog b/debian/changelog index 21ff17d..a6114eb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,42 @@ +tikzit (2.1.6-3) unstable; urgency=low + + * Fix watch file broken by GitHub change. + * Standards-version 4.6.1.1. No changes needed. + + -- Gard Spreemann <gspr@nonempty.org> Mon, 03 Oct 2022 17:39:38 +0200 + +tikzit (2.1.6-2) unstable; urgency=low + + [ Debian Janitor ] + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + + [ Gard Spreemann ] + * Fix watch file. + * Standards-version 4.5.1, no changes needed. + + -- Gard Spreemann <gspr@nonempty.org> Sun, 15 Aug 2021 11:38:15 +0200 + +tikzit (2.1.6-1) unstable; urgency=medium + + * New upstream version. + * DH compat level 13 (new style). + + -- Gard Spreemann <gspr@nonempty.org> Thu, 20 Aug 2020 09:04:17 +0200 + +tikzit (2.1.5-1) unstable; urgency=medium + + * New upstream version. + * Standards-version 4.5.0. No changes needed. + + -- Gard Spreemann <gspr@nonempty.org> Tue, 14 Apr 2020 09:10:05 +0200 + +tikzit (2.1.4-4) unstable; urgency=medium + + * Depend on tex-common. + + -- Gard Spreemann <gspr@nonempty.org> Sat, 28 Dec 2019 16:50:53 +0100 + tikzit (2.1.4-3) unstable; urgency=medium * Install style file. Thanks to ael for reporting. (Closes: #947517) diff --git a/debian/compat b/debian/compat deleted file mode 100644 index 48082f7..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -12 diff --git a/debian/control b/debian/control index ce54889..fbd4a53 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Gard Spreemann <gspr@nonempty.org> Build-Depends: bison, - debhelper (>= 12), + debhelper-compat (= 13), flex, libpoppler-dev, libpoppler-qt5-dev, @@ -13,7 +13,7 @@ Build-Depends: tex-common, texlive-base, texlive-binaries -Standards-Version: 4.4.1 +Standards-Version: 4.6.1.1 Rules-Requires-Root: no Homepage: https://tikzit.github.io/ Vcs-Browser: https://salsa.debian.org/debian/tikzit @@ -21,7 +21,7 @@ Vcs-Git: https://salsa.debian.org/debian/tikzit.git Package: tikzit Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} +Depends: ${shlibs:Depends}, ${misc:Depends}, tex-common Recommends: preview-latex-style, texlive-latex-base, texlive-pictures diff --git a/debian/copyright b/debian/copyright index de65293..38d53ff 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,7 +3,7 @@ Upstream-Name: tikzit Source: https://tikzit.github.io Files: * -Copyright: 2018-2019 Aleks Kissinger <aleks0@gmail.com> +Copyright: 2018-2022 Aleks Kissinger <aleks0@gmail.com> License: GPL-3+ Files: src/data/tikzparser.y @@ -33,7 +33,7 @@ Copyright: 2008 ELQ License: CC-BY-SA-3.0 Files: debian/* -Copyright: 2017-2019 Gard Spreemann <gspr@nonempty.org> +Copyright: 2017-2022 Gard Spreemann <gspr@nonempty.org> License: GPL-3+ License: GPL-3+ diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000..c5ce822 --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,5 @@ +--- +Bug-Database: https://github.com/tikzit/tikzit/issues +Bug-Submit: https://github.com/tikzit/tikzit/issues/new +Repository: https://github.com/tikzit/tikzit.git +Repository-Browse: https://github.com/tikzit/tikzit diff --git a/debian/watch b/debian/watch index 61a2628..1142229 100644 --- a/debian/watch +++ b/debian/watch @@ -1,3 +1,5 @@ version=4 -opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/tikzit-$1\.tar\.gz/,uversionmangle=s/-?(RC|rc)/~rc/ \ - https://github.com/tikzit/tikzit/releases .*/archive/v(\d\S+)\.tar\.gz
\ No newline at end of file +opts=filenamemangle=s%(?:.*?)?v?(\d[\d.]*@ARCHIVE_EXT@)%@PACKAGE@-$1%,\ + uversionmangle=s/-(RC|rc)/~rc/ \ + https://github.com/tikzit/tikzit/tags \ + (?:.*?/)?v?@ANY_VERSION@@ARCHIVE_EXT@ diff --git a/src/data/edge.cpp b/src/data/edge.cpp index 0bd49e8..652b480 100644 --- a/src/data/edge.cpp +++ b/src/data/edge.cpp @@ -28,6 +28,7 @@ Edge::Edge(Node *s, Node *t, QObject *parent) : {
_data = new GraphElementData(this);
_edgeNode = nullptr;
+ _path = nullptr;
_dirty = true;
if (s != t) {
@@ -436,3 +437,13 @@ QPointF Edge::bezierTangent(qreal start, qreal end) const return QPointF(dx, dy);
}
+
+Path *Edge::path() const
+{
+ return _path;
+}
+
+void Edge::setPath(Path *path)
+{
+ _path = path;
+}
diff --git a/src/data/edge.h b/src/data/edge.h index 954145f..ad21f36 100644 --- a/src/data/edge.h +++ b/src/data/edge.h @@ -27,6 +27,8 @@ #include "node.h"
#include "style.h"
+class Path;
+
#include <QObject>
#include <QPointF>
@@ -92,12 +94,15 @@ public: void setStyleName(const QString & styleName);
Style *style() const;
+ Path *path() const;
+ void setPath(Path *path);
+
signals:
public slots:
private:
- QPointF bezierTangent(qreal start, qreal end) const;
+ QPointF bezierTangent(qreal start, qreal end) const;
QString _sourceAnchor;
QString _targetAnchor;
@@ -108,6 +113,7 @@ private: // referenced
Node *_source;
Node *_target;
+ Path *_path;
Style *_style;
diff --git a/src/data/graph.cpp b/src/data/graph.cpp index 1dd5574..74de24d 100644 --- a/src/data/graph.cpp +++ b/src/data/graph.cpp @@ -73,6 +73,17 @@ void Graph::removeEdge(Edge *e) _edges.removeOne(e);
}
+void Graph::addPath(Path *p)
+{
+ p->setParent(this);
+ _paths << p;
+}
+
+void Graph::removePath(Path *p)
+{
+ _paths.removeOne(p);
+}
+
int Graph::maxIntName()
{
int max = -1;
@@ -167,6 +178,11 @@ const QVector<Edge*> &Graph::edges() return _edges;
}
+const QVector<Path *> &Graph::paths()
+{
+ return _paths;
+}
+
QRectF Graph::bbox() const
{
return _bbox;
@@ -230,37 +246,93 @@ QString Graph::tikz() Edge *e;
+ Path *p;
foreach (e, _edges) {
- e->setTikzLine(line);
- e->updateData();
- code << "\t\t\\draw ";
-
- if (!e->data()->isEmpty())
- code << e->data()->tikz() << " ";
-
- code << "(" << e->source()->name();
- if (e->sourceAnchor() != "")
- code << "." << e->sourceAnchor();
- code << ") to ";
-
- if (e->hasEdgeNode()) {
- code << "node ";
- if (!e->edgeNode()->data()->isEmpty())
- code << e->edgeNode()->data()->tikz() << " ";
- code << "{" << e->edgeNode()->label() << "} ";
- }
+ p = e->path();
+ if (p) { // if edge is part of a path
+ if (p->edges().first() == e) { // only add tikz code once per path
+ e->setTikzLine(line);
+ e->updateData();
+ code << "\t\t\\draw ";
+
+ GraphElementData *npd = e->data()->nonPathData();
+ if (!npd->isEmpty())
+ code << npd->tikz() << " ";
+ delete npd;
+
+ code << "(" << e->source()->name();
+ if (e->sourceAnchor() != "") {
+ code << "." << e->sourceAnchor();
+ } else if (p->isCycle()) {
+ code << ".center";
+ }
+ code << ")";
+
+ foreach (Edge *e1, p->edges()) {
+ code << "\n\t\t\t to ";
+ line++;
+ e1->setTikzLine(line);
+ e1->updateData();
+
+ GraphElementData *pd = e1->data()->pathData();
+ if (!pd->isEmpty())
+ code << pd->tikz() << " ";
+ delete pd;
+
+ if (e1->hasEdgeNode()) {
+ code << "node ";
+ if (!e1->edgeNode()->data()->isEmpty())
+ code << e1->edgeNode()->data()->tikz() << " ";
+ code << "{" << e1->edgeNode()->label() << "} ";
+ }
+
+ if (e->source() == e1->target()) {
+ code << "cycle";
+ } else {
+ code << "(" << e1->target()->name();
+ if (e1->targetAnchor() != "") {
+ code << "." << e1->targetAnchor();
+ } else if (e1 != p->edges().last()) {
+ code << ".center";
+ }
+ code << ")";
+ }
+ }
+ code << ";\n";
+ line++;
+ }
+ } else { // edge is not part of a path
+ e->setTikzLine(line);
+ e->updateData();
+ code << "\t\t\\draw ";
+
+ if (!e->data()->isEmpty())
+ code << e->data()->tikz() << " ";
+
+ code << "(" << e->source()->name();
+ if (e->sourceAnchor() != "")
+ code << "." << e->sourceAnchor();
+ code << ") to ";
+
+ if (e->hasEdgeNode()) {
+ code << "node ";
+ if (!e->edgeNode()->data()->isEmpty())
+ code << e->edgeNode()->data()->tikz() << " ";
+ code << "{" << e->edgeNode()->label() << "} ";
+ }
- if (e->source() == e->target()) {
- code << "()";
- } else {
- code << "(" << e->target()->name();
- if (e->targetAnchor() != "")
- code << "." << e->targetAnchor();
- code << ")";
- }
+ if (e->source() == e->target()) {
+ code << "()";
+ } else {
+ code << "(" << e->target()->name();
+ if (e->targetAnchor() != "")
+ code << "." << e->targetAnchor();
+ code << ")";
+ }
- code << ";\n";
- line++;
+ code << ";\n";
+ line++;
+ }
}
if (!_edges.isEmpty()) {
@@ -288,9 +360,33 @@ Graph *Graph::copyOfSubgraphWithNodes(QSet<Node *> nds) g->addNode(n1);
}
}
+
+ QMap<Edge*,Edge*> edgeTable;
foreach (Edge *e, edges()) {
if (nds.contains(e->source()) && nds.contains(e->target())) {
- g->addEdge(e->copy(&nodeTable));
+ Edge *e1 = e->copy(&nodeTable);
+ g->addEdge(e1);
+ edgeTable.insert(e,e1);
+ }
+ }
+
+ // add a copy of a path to the new graph if all of the edges are there
+ foreach (Path *p, paths()) {
+ bool allEdges = true;
+ Path *p1 = new Path();
+ foreach (Edge *e1, p->edges()) {
+ if (edgeTable.contains(e1)) {
+ p1->addEdge(edgeTable[e1]);
+ } else {
+ allEdges = false;
+ break;
+ }
+ }
+ if (allEdges) {
+ g->addPath(p1);
+ } else {
+ p1->removeEdges();
+ delete p1;
}
}
diff --git a/src/data/graph.h b/src/data/graph.h index 286ccdc..c306bb2 100644 --- a/src/data/graph.h +++ b/src/data/graph.h @@ -25,6 +25,7 @@ #include "node.h"
#include "edge.h"
+#include "path.h"
#include "graphelementdata.h"
#include <QObject>
@@ -46,6 +47,8 @@ public: void addEdge(Edge *e);
void addEdge(Edge *e, int index);
void removeEdge(Edge *e);
+ void addPath(Path *p);
+ void removePath(Path *p);
int maxIntName();
void reorderNodes(const QVector<Node*> &newOrder);
void reorderEdges(const QVector<Edge*> &newOrder);
@@ -64,6 +67,7 @@ public: const QVector<Node *> &nodes();
const QVector<Edge*> &edges();
+ const QVector<Path*> &paths();
QRectF bbox() const;
void setBbox(const QRectF &bbox);
@@ -121,6 +125,7 @@ public slots: private:
QVector<Node*> _nodes;
QVector<Edge*> _edges;
+ QVector<Path*> _paths;
//QMultiHash<Node*,Edge*> inEdges;
//QMultiHash<Node*,Edge*> outEdges;
GraphElementData *_data;
diff --git a/src/data/graphelementdata.cpp b/src/data/graphelementdata.cpp index cd09a6d..e1e89b1 100644 --- a/src/data/graphelementdata.cpp +++ b/src/data/graphelementdata.cpp @@ -110,6 +110,14 @@ int GraphElementData::indexOfKey(QString key) return -1;
}
+void GraphElementData::mergeData(GraphElementData *d)
+{
+ GraphElementProperty p;
+ foreach (p, d->properties()) {
+ if (!hasProperty(p.key())) add(p);
+ }
+}
+
bool GraphElementData::removeRows(int row, int /*count*/, const QModelIndex &parent)
{
if (row >= 0 && row < _properties.length()) {
@@ -257,3 +265,30 @@ QVector<GraphElementProperty> GraphElementData::properties() const {
return _properties;
}
+
+GraphElementData *GraphElementData::pathData() const
+{
+ GraphElementData *d = new GraphElementData();
+ foreach(GraphElementProperty p, _properties) {
+ if (isPathProperty(p.key())) d->add(p);
+ }
+ return d;
+}
+
+GraphElementData *GraphElementData::nonPathData() const
+{
+ GraphElementData *d = new GraphElementData();
+ foreach(GraphElementProperty p, _properties) {
+ if (!isPathProperty(p.key())) d->add(p);
+ }
+ return d;
+}
+
+bool GraphElementData::isPathProperty(QString key)
+{
+ return (key == "bend left" ||
+ key == "bend right" ||
+ key == "in" ||
+ key == "out" ||
+ key == "looseness");
+}
diff --git a/src/data/graphelementdata.h b/src/data/graphelementdata.h index dce0d46..8022a14 100644 --- a/src/data/graphelementdata.h +++ b/src/data/graphelementdata.h @@ -49,6 +49,7 @@ public: bool hasProperty(QString key);
bool atom(QString atom);
int indexOfKey(QString key);
+ void mergeData(GraphElementData *d);
bool removeRows(int row, int count, const QModelIndex &parent) override;
bool moveRows(const QModelIndex &sourceParent,
int sourceRow, int,
@@ -77,12 +78,16 @@ public: bool isEmpty();
QVector<GraphElementProperty> properties() const;
+ GraphElementData *pathData() const;
+ GraphElementData *nonPathData() const;
+
signals:
public slots:
private:
QVector<GraphElementProperty> _properties;
+ static bool isPathProperty(QString key);
};
#endif // GRAPHELEMENTDATA_H
diff --git a/src/data/path.cpp b/src/data/path.cpp new file mode 100644 index 0000000..1438d64 --- /dev/null +++ b/src/data/path.cpp @@ -0,0 +1,35 @@ +#include "path.h" + +Path::Path(QObject *parent) : QObject(parent) +{ + +} + +int Path::length() const +{ + return _edges.length(); +} + +void Path::addEdge(Edge *e) +{ + e->setPath(this); + _edges << e; +} + +void Path::removeEdges() +{ + foreach(Edge *e, _edges) { + e->setPath(nullptr); + } + _edges.clear(); +} + +bool Path::isCycle() const +{ + return !_edges.isEmpty() && _edges.first()->source() == _edges.last()->target(); +} + +QVector<Edge *> Path::edges() const +{ + return _edges; +} diff --git a/src/data/path.h b/src/data/path.h new file mode 100644 index 0000000..3c83170 --- /dev/null +++ b/src/data/path.h @@ -0,0 +1,25 @@ +#ifndef PATH_H +#define PATH_H + +#include "edge.h" + +#include <QObject> + +class Path : public QObject +{ + Q_OBJECT +public: + explicit Path(QObject *parent = nullptr); + int length() const; + void addEdge(Edge *e); + void removeEdges(); + bool isCycle() const; + + QVector<Edge *> edges() const; + +private: + QVector<Edge*> _edges; + +}; + +#endif // PATH_H diff --git a/src/data/style.cpp b/src/data/style.cpp index d0f011d..f4aa892 100644 --- a/src/data/style.cpp +++ b/src/data/style.cpp @@ -62,7 +62,22 @@ QColor Style::fillColor(bool tikzitOverride) const QBrush Style::brush() const
{
- return QBrush(fillColor());
+ if (hasFill()) {
+ return QBrush(fillColor());
+ } else {
+ return Qt::NoBrush;
+ }
+}
+
+bool Style::hasFill() const
+{
+ return (propertyWithDefault("fill", "none") != "none");
+}
+
+bool Style::hasStroke() const
+{
+ if (isEdgeStyle()) return propertyWithDefault("draw", "black") != "none";
+ else return (propertyWithDefault("draw", "none") != "none");
}
QString Style::shape(bool tikzitOverride) const
@@ -152,24 +167,28 @@ Style::DrawStyle Style::drawStyle() const QPen Style::pen() const
{
- QPen p(strokeColor());
- p.setWidthF((float)strokeThickness() * 2.0f);
-
- QVector<qreal> pat;
- switch (drawStyle()) {
- case Dashed:
- pat << 3.0 << 3.0;
- p.setDashPattern(pat);
- break;
- case Dotted:
- pat << 1.0 << 1.0;
- p.setDashPattern(pat);
- break;
- case Solid:
- break;
- }
+ if (hasStroke()) {
+ QPen p(strokeColor());
+ p.setWidthF((float)strokeThickness() * 2.0f);
+
+ QVector<qreal> pat;
+ switch (drawStyle()) {
+ case Dashed:
+ pat << 3.0 << 3.0;
+ p.setDashPattern(pat);
+ break;
+ case Dotted:
+ pat << 1.0 << 1.0;
+ p.setDashPattern(pat);
+ break;
+ case Solid:
+ break;
+ }
- return p;
+ return p;
+ } else {
+ return Qt::NoPen;
+ }
}
QPainterPath Style::path() const
@@ -225,19 +244,27 @@ QIcon Style::icon() const px.fill(Qt::transparent);
QPainter painter(&px);
- if (_data == 0) {
- QPen pen(Qt::black);
- pen.setWidth(3);
- } else {
- painter.setPen(pen());
+// if (_data == 0) {
+// QPen pen(Qt::black);
+// pen.setWidth(3);
+// } else {
+// painter.setPen(pen());
+// }
+
+ QPen pn = pen();
+ painter.setPen(pn);
+
+ if (hasFill()) {
+ painter.fillRect(10,50,80,30,brush());
}
painter.drawLine(10, 50, 90, 50);
- QPen pn = pen();
pn.setStyle(Qt::SolidLine);
painter.setPen(pn);
+
+
switch (arrowHead()) {
case Pointer:
painter.drawLine(90,50,80,40);
diff --git a/src/data/style.h b/src/data/style.h index 78e11dc..0e795e1 100644 --- a/src/data/style.h +++ b/src/data/style.h @@ -57,18 +57,21 @@ public: void setName(const QString &name);
QString propertyWithDefault(QString prop, QString def, bool tikzitOverride=true) const;
QString tikz() const;
- void setArrowAtom(QString atom);
-
- // only relevant for node styles
QColor fillColor(bool tikzitOverride=true) const;
QBrush brush() const;
+ bool hasFill() const;
+ bool hasStroke() const;
+
+ // only relevant for node styles
QString shape(bool tikzitOverride=true) const;
+ QString category() const;
// only relevant for edge styles
Style::ArrowTipStyle arrowHead() const;
Style::ArrowTipStyle arrowTail() const;
Style::DrawStyle drawStyle() const;
- QString category() const;
+ void setArrowAtom(QString atom);
+
protected:
QString _name;
diff --git a/src/data/tikzassembler.cpp b/src/data/tikzassembler.cpp index cd0b517..3cb3c10 100644 --- a/src/data/tikzassembler.cpp +++ b/src/data/tikzassembler.cpp @@ -29,6 +29,8 @@ TikzAssembler::TikzAssembler(Graph *graph, QObject *parent) : {
yylex_init(&scanner);
yyset_extra(this, scanner);
+ _currentEdgeData = nullptr;
+ _currentPath = nullptr;
}
TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) :
@@ -36,6 +38,8 @@ TikzAssembler::TikzAssembler(TikzStyles *tikzStyles, QObject *parent) : {
yylex_init(&scanner);
yyset_extra(this, scanner);
+ _currentEdgeData = nullptr;
+ _currentPath = nullptr;
}
void TikzAssembler::addNodeToMap(Node *n) { _nodeMap.insert(n->name(), n); }
@@ -70,3 +74,70 @@ bool TikzAssembler::isTikzStyles() const return _tikzStyles != 0;
}
+Node *TikzAssembler::currentEdgeSource() const
+{
+ return _currentEdgeSource;
+}
+
+void TikzAssembler::setCurrentEdgeSource(Node *currentEdgeSource)
+{
+ _currentEdgeSource = currentEdgeSource;
+}
+
+Node *TikzAssembler::currentPathSource() const
+{
+ if (_currentPath && _currentPath->length() > 0) {
+ return _currentPath->edges()[0]->source();
+ } else {
+ return nullptr;
+ }
+}
+
+GraphElementData *TikzAssembler::currentEdgeData() const
+{
+ return _currentEdgeData;
+}
+
+void TikzAssembler::setCurrentEdgeData(GraphElementData *currentEdgeData)
+{
+ _currentEdgeData = currentEdgeData;
+}
+
+QString TikzAssembler::currentEdgeSourceAnchor() const
+{
+ return _currentEdgeSourceAnchor;
+}
+
+void TikzAssembler::setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceAnchor)
+{
+ _currentEdgeSourceAnchor = currentEdgeSourceAnchor;
+}
+
+void TikzAssembler::addEdge(Edge *e)
+{
+ if (!_currentPath) _currentPath = new Path();
+ _currentPath->addEdge(e);
+ _graph->addEdge(e);
+}
+
+void TikzAssembler::finishCurrentPath()
+{
+ if (_currentEdgeData) {
+ GraphElementData *d = _currentEdgeData;
+ _currentEdgeData = nullptr;
+ delete d;
+ }
+
+ if (_currentPath) {
+ if (_currentPath->length() < 2) {
+ _currentPath->removeEdges();
+ Path *p = _currentPath;
+ _currentPath = nullptr;
+ delete p;
+ } else {
+ _graph->addPath(_currentPath);
+ _currentPath = nullptr;
+ }
+ }
+}
+
diff --git a/src/data/tikzassembler.h b/src/data/tikzassembler.h index 7b32224..f86abcd 100644 --- a/src/data/tikzassembler.h +++ b/src/data/tikzassembler.h @@ -23,7 +23,6 @@ #ifndef TIKZASSEMBLER_H
#define TIKZASSEMBLER_H
-#include "node.h"
#include "graph.h"
#include "tikzstyles.h"
@@ -46,6 +45,20 @@ public: bool isTikzStyles() const;
+ Node *currentEdgeSource() const;
+ void setCurrentEdgeSource(Node *currentEdgeSource);
+
+ Node *currentPathSource() const;
+
+ GraphElementData *currentEdgeData() const;
+ void setCurrentEdgeData(GraphElementData *currentEdgeData);
+
+ QString currentEdgeSourceAnchor() const;
+ void setCurrentEdgeSourceAnchor(const QString ¤tEdgeSourceAnchor);
+
+ void addEdge(Edge *e);
+ void finishCurrentPath();
+
signals:
public slots:
@@ -54,6 +67,10 @@ private: QHash<QString,Node*> _nodeMap;
Graph *_graph;
TikzStyles *_tikzStyles;
+ Path *_currentPath;
+ Node *_currentEdgeSource;
+ GraphElementData *_currentEdgeData;
+ QString _currentEdgeSourceAnchor;
void *scanner;
};
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/data/tikzlexer.l b/src/data/tikzlexer.l index 0d80467..615cf0d 100644 --- a/src/data/tikzlexer.l +++ b/src/data/tikzlexer.l @@ -82,6 +82,7 @@ FLOAT \-?[0-9]*(\.[0-9]+)? <INITIAL>node { return NODE; }
<INITIAL>at { return AT; }
<INITIAL>to { return TO; }
+<INITIAL>cycle { return CYCLE; }
\([ ]*{FLOAT}[ ]*,[ ]*{FLOAT}[ ]*\) {
yylloc->last_column = yylloc->first_column + 1;
diff --git a/src/data/tikzparser.y b/src/data/tikzparser.y index 4473107..678d8bf 100644 --- a/src/data/tikzparser.y +++ b/src/data/tikzparser.y @@ -96,6 +96,7 @@ void yyerror(YYLTYPE *yylloc, void * /*scanner*/, const char *str) { %token NODE "node"
%token AT "at"
%token TO "to"
+%token CYCLE "cycle"
%token SEMICOLON ";"
%token COMMA ","
@@ -214,10 +215,13 @@ noderef: "(" REFSTRING optanchor ")" $$.node = assembler->nodeWithName(QString($2));
free($2);
$$.anchor = $3;
+ $$.loop = false;
+ $$.cycle = false;
};
optnoderef:
- noderef { $$ = $1; }
- | "(" ")" { $$.node = 0; $$.anchor = 0; }
+ noderef { $$ = $1; }
+ | "(" ")" { $$.node = 0; $$.anchor = 0; $$.loop = true; $$.cycle = false; }
+ | "cycle" { $$.node = 0; $$.anchor = 0; $$.loop = false; $$.cycle = true; }
optedgenode:
{ $$ = 0; }
| "node" optproperties DELIMITEDSTRING
@@ -228,45 +232,68 @@ optedgenode: $$->setLabel(QString($3));
free($3);
}
-edge: "\\draw" optproperties noderef "to" optedgenode optnoderef ";"
- {
- Node *s;
+
+edgesource: optproperties noderef {
+ assembler->setCurrentEdgeSource($2.node);
+ if ($2.anchor) {
+ assembler->setCurrentEdgeSourceAnchor(QString($2.anchor));
+ free($2.anchor);
+ } else {
+ assembler->setCurrentEdgeSourceAnchor(QString());
+ }
+ assembler->setCurrentEdgeData($1);
+ }
+
+optedgetargets: edgetarget optedgetargets |
+
+edgetarget: "to" optproperties optedgenode optnoderef {
+ Node *s = assembler->currentEdgeSource();;
Node *t;
-
- s = $3.node;
- if ($6.node) {
- t = $6.node;
+ if ($4.loop) {
+ t = assembler->currentEdgeSource();
+ } else if ($4.cycle) {
+ t = assembler->currentPathSource();
+ if (!t) t = s;
} else {
- t = s;
+ t = $4.node;
}
- // if the source or the target of the edge doesn't exist, quietly ignore it.
- if (s != 0 && t != 0) {
- Edge *edge = new Edge(s, t);
- if ($2) {
- edge->setData($2);
- edge->setAttributesFromData();
- }
+ if (s != 0 && t != 0) { // if source or target don't exist, quietly ignore edge
+ Edge *e = new Edge(s, t);
+ assembler->setCurrentEdgeSource(t);
- if ($5)
- edge->setEdgeNode($5);
- if ($3.anchor) {
- edge->setSourceAnchor(QString($3.anchor));
- free($3.anchor);
+ if (!assembler->currentEdgeSourceAnchor().isEmpty()) {
+ e->setSourceAnchor(assembler->currentEdgeSourceAnchor());
}
- if ($6.node) {
- if ($6.anchor) {
- edge->setTargetAnchor(QString($6.anchor));
- free($6.anchor);
- }
+ if ($4.anchor) {
+ QString a($4.anchor);
+ free($4.anchor);
+ e->setTargetAnchor(a);
+ assembler->setCurrentEdgeSourceAnchor(a);
} else {
- edge->setTargetAnchor(edge->sourceAnchor());
+ assembler->setCurrentEdgeSourceAnchor(QString());
}
- assembler->graph()->addEdge(edge);
+ if ($3) e->setEdgeNode($3);
+
+ GraphElementData *cd = assembler->currentEdgeData();
+ if ($2) {
+ if (cd) $2->mergeData(cd);
+ e->setData($2);
+ } else {
+ if (cd) e->setData(cd->copy());
+ }
+ e->setAttributesFromData();
+ assembler->addEdge(e);
}
+ }
+
+
+edge: "\\draw" edgesource edgetarget optedgetargets ";"
+ {
+ assembler->finishCurrentPath();
};
ignoreprop: val | val "=" val;
diff --git a/src/data/tikzparserdefs.h b/src/data/tikzparserdefs.h index 02743fe..da60bbf 100644 --- a/src/data/tikzparserdefs.h +++ b/src/data/tikzparserdefs.h @@ -33,6 +33,8 @@ struct noderef {
Node *node;
char *anchor;
+ bool cycle;
+ bool loop;
};
inline int isatty(int) { return 0; }
diff --git a/src/gui/edgeitem.cpp b/src/gui/edgeitem.cpp index 45ae159..675ddd7 100644 --- a/src/gui/edgeitem.cpp +++ b/src/gui/edgeitem.cpp @@ -71,7 +71,8 @@ void EdgeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge QPen pen = _edge->style()->pen(); painter->setPen(pen); painter->setBrush(Qt::NoBrush); - painter->drawPath(path()); + + if (!_edge->path()) painter->drawPath(path()); QPointF ht = _edge->headTangent(); QPointF hLeft(-ht.y(), ht.x()); @@ -83,27 +84,27 @@ void EdgeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge pen.setStyle(Qt::SolidLine); painter->setPen(pen); - - - switch (_edge->style()->arrowHead()) { - case Style::Flat: - { - painter->drawLine( - toScreen(_edge->head() + hLeft), - toScreen(_edge->head() + hRight)); - break; - } - case Style::Pointer: - { - QPainterPath pth; - pth.moveTo(toScreen(_edge->head() + ht + hLeft)); - pth.lineTo(toScreen(_edge->head())); - pth.lineTo(toScreen(_edge->head() + ht + hRight)); - painter->drawPath(pth); - break; - } - case Style::NoTip: - break; + if (!_edge->path() || _edge->path()->edges().last() == _edge) { + switch (_edge->style()->arrowHead()) { + case Style::Flat: + { + painter->drawLine( + toScreen(_edge->head() + hLeft), + toScreen(_edge->head() + hRight)); + break; + } + case Style::Pointer: + { + QPainterPath pth; + pth.moveTo(toScreen(_edge->head() + ht + hLeft)); + pth.lineTo(toScreen(_edge->head())); + pth.lineTo(toScreen(_edge->head() + ht + hRight)); + painter->drawPath(pth); + break; + } + case Style::NoTip: + break; + } } //QPen outline = QPen(Qt::red); @@ -111,26 +112,28 @@ void EdgeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge //painter->drawPath(_expPath); //painter->setPen(pen); - switch (_edge->style()->arrowTail()) { - case Style::Flat: - { - painter->drawLine( - toScreen(_edge->tail() + tLeft), - toScreen(_edge->tail() + tRight)); - break; - } - case Style::Pointer: - { - QPainterPath pth; - pth.moveTo(toScreen(_edge->tail() + tt + tLeft)); - pth.lineTo(toScreen(_edge->tail())); - pth.lineTo(toScreen(_edge->tail() + tt + tRight)); - painter->drawPath(pth); - break; - } - case Style::NoTip: - break; - } + if (!_edge->path() || _edge->path()->edges().first() == _edge) { + switch (_edge->style()->arrowTail()) { + case Style::Flat: + { + painter->drawLine( + toScreen(_edge->tail() + tLeft), + toScreen(_edge->tail() + tRight)); + break; + } + case Style::Pointer: + { + QPainterPath pth; + pth.moveTo(toScreen(_edge->tail() + tt + tLeft)); + pth.lineTo(toScreen(_edge->tail())); + pth.lineTo(toScreen(_edge->tail() + tt + tRight)); + painter->drawPath(pth); + break; + } + case Style::NoTip: + break; + } + } if (isSelected()) { QColor draw; diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp index 35ab736..efd453d 100644 --- a/src/gui/mainmenu.cpp +++ b/src/gui/mainmenu.cpp @@ -239,6 +239,24 @@ void MainMenu::on_actionMerge_Nodes_triggered() tikzit->activeWindow()->tikzScene()->mergeNodes(); } +void MainMenu::on_actionMake_Path_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->makePath(false); +} + +void MainMenu::on_actionMake_Path_as_Background_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->makePath(true); +} + +void MainMenu::on_actionSplit_Path_triggered() +{ + if (tikzit->activeWindow() != 0) + tikzit->activeWindow()->tikzScene()->splitPath(); +} + // Tikz void MainMenu::on_actionParse_triggered() @@ -282,6 +300,36 @@ void MainMenu::on_actionRun_LaTeX_triggered() tikzit->makePreview(); } +void MainMenu::on_actionPrevious_Node_Style_triggered() +{ + tikzit->activeWindow()->stylePalette()->previousNodeStyle(); +} + +void MainMenu::on_actionNext_Node_Style_triggered() +{ + tikzit->activeWindow()->stylePalette()->nextNodeStyle(); +} + +void MainMenu::on_actionClear_Node_Style_triggered() +{ + tikzit->activeWindow()->stylePalette()->toggleClearNodeStyle(); +} + +void MainMenu::on_actionPrevious_Edge_Style_triggered() +{ + tikzit->activeWindow()->stylePalette()->previousEdgeStyle(); +} + +void MainMenu::on_actionNext_Edge_Style_triggered() +{ + tikzit->activeWindow()->stylePalette()->nextEdgeStyle(); +} + +void MainMenu::on_actionClear_Edge_Style_triggered() +{ + tikzit->activeWindow()->stylePalette()->toggleClearEdgeStyle(); +} + void MainMenu::on_actionPreferences_triggered() { PreferenceDialog *d = new PreferenceDialog(this); @@ -301,6 +349,14 @@ void MainMenu::on_actionZoom_Out_triggered() if (tikzit->activeWindow() != 0) tikzit->activeWindow()->tikzView()->zoomOut(); } +void MainMenu::on_actionShow_Node_Labels_triggered() +{ + if (tikzit->activeWindow() != 0) { + tikzit->activeWindow()->tikzScene()->setDrawNodeLabels(ui.actionShow_Node_Labels->isChecked()); + tikzit->activeWindow()->tikzScene()->invalidate(); + } +} + void MainMenu::on_actionAbout_triggered() { QMessageBox::about(this, diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h index 51d7d3c..d0d73e9 100644 --- a/src/gui/mainmenu.h +++ b/src/gui/mainmenu.h @@ -67,17 +67,27 @@ public slots: void on_actionExtendRight_triggered(); void on_actionReverse_Edge_Direction_triggered(); void on_actionMerge_Nodes_triggered(); + void on_actionMake_Path_triggered(); + void on_actionMake_Path_as_Background_triggered(); + void on_actionSplit_Path_triggered(); // Tools void on_actionParse_triggered(); void on_actionRevert_triggered(); void on_actionJump_to_Selection_triggered(); void on_actionRun_LaTeX_triggered(); + void on_actionPrevious_Node_Style_triggered(); + void on_actionNext_Node_Style_triggered(); + void on_actionClear_Node_Style_triggered(); + void on_actionPrevious_Edge_Style_triggered(); + void on_actionNext_Edge_Style_triggered(); + void on_actionClear_Edge_Style_triggered(); void on_actionPreferences_triggered(); // View void on_actionZoom_In_triggered(); void on_actionZoom_Out_triggered(); + void on_actionShow_Node_Labels_triggered(); // Help void on_actionAbout_triggered(); diff --git a/src/gui/mainmenu.ui b/src/gui/mainmenu.ui index 692f82e..11778db 100644 --- a/src/gui/mainmenu.ui +++ b/src/gui/mainmenu.ui @@ -58,6 +58,14 @@ <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"/> + <addaction name="actionMake_Path_as_Background"/> + </widget> <addaction name="actionUndo"/> <addaction name="actionRedo"/> <addaction name="separator"/> @@ -74,16 +82,32 @@ <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"> <string>Tools</string> </property> + <widget class="QMenu" name="menuNode_Style"> + <property name="title"> + <string>Set Style</string> + </property> + <addaction name="actionPrevious_Node_Style"/> + <addaction name="actionNext_Node_Style"/> + <addaction name="actionClear_Node_Style"/> + <addaction name="separator"/> + <addaction name="actionPrevious_Edge_Style"/> + <addaction name="actionNext_Edge_Style"/> + <addaction name="actionClear_Edge_Style"/> + </widget> <addaction name="actionParse"/> <addaction name="actionRevert"/> <addaction name="actionJump_to_Selection"/> <addaction name="actionRun_LaTeX"/> <addaction name="separator"/> + <addaction name="menuNode_Style"/> + <addaction name="separator"/> <addaction name="actionPreferences"/> </widget> <widget class="QMenu" name="menuView"> @@ -92,6 +116,7 @@ </property> <addaction name="actionZoom_In"/> <addaction name="actionZoom_Out"/> + <addaction name="actionShow_Node_Labels"/> </widget> <widget class="QMenu" name="menuHelp"> <property name="title"> @@ -383,6 +408,92 @@ <string>Ctrl+M</string> </property> </action> + <action name="actionShow_Node_Labels"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="text"> + <string>Show Node Labels</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+L</string> + </property> + </action> + <action name="actionPrevious_Node_Style"> + <property name="text"> + <string>Previous Node Style</string> + </property> + <property name="shortcut"> + <string>Ctrl+,</string> + </property> + </action> + <action name="actionNext_Node_Style"> + <property name="text"> + <string>Next Node Style</string> + </property> + <property name="shortcut"> + <string>Ctrl+.</string> + </property> + </action> + <action name="actionClear_Node_Style"> + <property name="text"> + <string>Clear Node Style</string> + </property> + <property name="shortcut"> + <string>Ctrl+Space</string> + </property> + </action> + <action name="actionPrevious_Edge_Style"> + <property name="text"> + <string>Previous Edge Style</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+,</string> + </property> + </action> + <action name="actionNext_Edge_Style"> + <property name="text"> + <string>Next Edge Style</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+.</string> + </property> + </action> + <action name="actionClear_Edge_Style"> + <property name="text"> + <string>Clear Edge Style</string> + </property> + <property name="shortcut"> + <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> + <action name="actionMake_Path_as_Background"> + <property name="text"> + <string>Make Path as Background</string> + </property> + <property name="shortcut"> + <string>Ctrl+B</string> + </property> + </action> <addaction name="menuFile"/> <addaction name="menuEdit"/> <addaction name="menuView"/> diff --git a/src/gui/nodeitem.cpp b/src/gui/nodeitem.cpp index 82692f2..7ae4c4a 100644 --- a/src/gui/nodeitem.cpp +++ b/src/gui/nodeitem.cpp @@ -91,7 +91,13 @@ void NodeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge painter->drawPath(shape()); } - if (_node->label() != "") { + bool drawLabel = _node->label() != ""; + if (scene()) { + TikzScene *sc = static_cast<TikzScene*>(scene()); + drawLabel= drawLabel && sc->drawNodeLabels(); + } + + if (drawLabel) { QRectF rect = labelRect(); QPen pen(QColor(200,0,0,120)); QVector<qreal> d; diff --git a/src/gui/pathitem.cpp b/src/gui/pathitem.cpp new file mode 100644 index 0000000..b45c0a5 --- /dev/null +++ b/src/gui/pathitem.cpp @@ -0,0 +1,68 @@ +#include "pathitem.h" +#include "tikzit.h" + +PathItem::PathItem(Path *path) +{ + _path = path; + readPos(); +} + +void PathItem::readPos() +{ + QPainterPath painterPath; + + foreach (Edge *e, _path->edges()) { + e->updateControls(); + + if (e == _path->edges().first()) + painterPath.moveTo (toScreen(e->tail())); + + if (e->bend() != 0 || !e->basicBendMode()) { + painterPath.cubicTo(toScreen(e->cp1()), + toScreen(e->cp2()), + toScreen(e->head())); + } else { + painterPath.lineTo(toScreen(e->head())); + } + } + + setPainterPath(painterPath); +} + +void PathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + Style *st = _path->edges().first()->style(); + QPen pen = st->pen(); + QBrush brush = st->brush(); + QColor c = brush.color(); + brush.setColor(QColor(c.red(),c.green(),c.blue(),200)); + painter->setPen(pen); + painter->setBrush(brush); + painter->drawPath(painterPath()); +} + +Path *PathItem::path() const +{ + return _path; +} + +QPainterPath PathItem::painterPath() const +{ + return _painterPath; +} + +void PathItem::setPainterPath(const QPainterPath &painterPath) +{ + prepareGeometryChange(); + + _painterPath = painterPath; + float r = GLOBAL_SCALEF * 0.1; + _boundingRect = _painterPath.boundingRect().adjusted(-r,-r,r,r); + + update(); +} + +QRectF PathItem::boundingRect() const +{ + return _boundingRect; +} diff --git a/src/gui/pathitem.h b/src/gui/pathitem.h new file mode 100644 index 0000000..7dc9c80 --- /dev/null +++ b/src/gui/pathitem.h @@ -0,0 +1,28 @@ +#ifndef PATHITEM_H +#define PATHITEM_H + +#include "path.h" + +#include <QGraphicsItem> + +class PathItem : public QGraphicsItem +{ +public: + PathItem(Path *path); + void readPos(); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override; + + Path *path() const; + + QPainterPath painterPath() const; + void setPainterPath(const QPainterPath &painterPath); + + QRectF boundingRect() const override; + +private: + Path *_path; + QPainterPath _painterPath; + QRectF _boundingRect; +}; + +#endif // PATHITEM_H diff --git a/src/gui/styleeditor.cpp b/src/gui/styleeditor.cpp index e2ade45..f8972b1 100644 --- a/src/gui/styleeditor.cpp +++ b/src/gui/styleeditor.cpp @@ -32,8 +32,8 @@ StyleEditor::StyleEditor(QWidget *parent) : { ui->setupUi(this); _formWidgets << ui->name << ui->category << - ui->fillColor << ui->hasTikzitFillColor << ui->tikzitFillColor << - ui->drawColor << ui->hasTikzitDrawColor << ui->tikzitDrawColor << + ui->fillColor << ui->noFill << ui->hasTikzitFillColor << ui->tikzitFillColor << + ui->drawColor << ui->noDraw << ui->hasTikzitDrawColor << ui->tikzitDrawColor << ui->shape << ui->hasTikzitShape << ui->tikzitShape << ui->leftArrow << ui->rightArrow << ui->properties; @@ -283,8 +283,16 @@ void StyleEditor::refreshDisplay() // draw QColor realDraw = s->strokeColor(false); QColor draw = s->strokeColor(); - ui->drawColor->setEnabled(true); - setColor(ui->drawColor, realDraw); + + ui->noDraw->setEnabled(true); + if (s->hasStroke()) { + ui->drawColor->setEnabled(true); + setColor(ui->drawColor, realDraw); + ui->noDraw->setChecked(false); + } else { + ui->noDraw->setChecked(true); + ui->drawColor->setEnabled(false); + } // tikzit draw bool drawOverride = s->data()->hasProperty("tikzit draw"); @@ -294,6 +302,27 @@ void StyleEditor::refreshDisplay() ui->tikzitDrawColor->setEnabled(drawOverride); if (drawOverride) setColor(ui->tikzitDrawColor, draw); + // fill + QColor realFill = s->fillColor(false); + QColor fill = s->fillColor(); + + ui->noFill->setEnabled(true); + if (s->hasFill()) { + ui->fillColor->setEnabled(true); + setColor(ui->fillColor, realFill); + ui->noFill->setChecked(false); + } else { + ui->noFill->setChecked(true); + ui->fillColor->setEnabled(false); + } + + // tikzit fill + bool fillOverride = s->data()->hasProperty("tikzit fill"); + ui->hasTikzitFillColor->setEnabled(true); + ui->hasTikzitFillColor->setChecked(fillOverride); + ui->tikzitFillColor->setEnabled(fillOverride); + if (fillOverride) setColor(ui->tikzitFillColor, fill); + if (!s->isEdgeStyle()) { // qDebug() << "node style update"; // category @@ -301,19 +330,6 @@ void StyleEditor::refreshDisplay() ui->category->setCurrentText( s->propertyWithDefault("tikzit category", "", false)); - // fill - QColor realFill = s->fillColor(false); - QColor fill = s->fillColor(); - ui->fillColor->setEnabled(true); - setColor(ui->fillColor, realFill); - - // tikzit fill - bool fillOverride = s->data()->hasProperty("tikzit fill"); - ui->hasTikzitFillColor->setEnabled(true); - ui->hasTikzitFillColor->setChecked(fillOverride); - ui->tikzitFillColor->setEnabled(fillOverride); - if (fillOverride) setColor(ui->tikzitFillColor, fill); - // shape QString realShape = s->propertyWithDefault("shape", "", false); QString shape = s->propertyWithDefault("tikzit shape", "", false); @@ -327,13 +343,6 @@ void StyleEditor::refreshDisplay() ui->tikzitShape->setEnabled(shapeOverride); if (shapeOverride) ui->tikzitShape->setCurrentText(shape); } else { -// qDebug() << "edge style update"; - - // set fill to gray (disabled) - ui->fillColor->setEnabled(false); - ui->tikzitFillColor->setEnabled(false); - ui->hasTikzitFillColor->setEnabled(false); - ui->shape->setEnabled(false); ui->tikzitShape->setEnabled(false); ui->hasTikzitShape->setEnabled(false); @@ -425,6 +434,30 @@ void StyleEditor::on_hasTikzitDrawColor_stateChanged(int state) } } +void StyleEditor::on_noFill_stateChanged(int state) +{ + Style *s = activeStyle(); + if (s != nullptr) { + if (state == Qt::Checked) s->data()->setProperty("fill", "none"); + else s->data()->setProperty("fill", "white"); + refreshActiveStyle(); + refreshDisplay(); + setDirty(true); + } +} + +void StyleEditor::on_noDraw_stateChanged(int state) +{ + Style *s = activeStyle(); + if (s != nullptr) { + if (state == Qt::Checked) s->data()->setProperty("draw", "none"); + else s->data()->setProperty("draw", "black"); + refreshActiveStyle(); + refreshDisplay(); + setDirty(true); + } +} + void StyleEditor::on_hasTikzitShape_stateChanged(int state) { Style *s = activeStyle(); diff --git a/src/gui/styleeditor.h b/src/gui/styleeditor.h index 2c35d56..7fc62c0 100644 --- a/src/gui/styleeditor.h +++ b/src/gui/styleeditor.h @@ -68,6 +68,8 @@ public slots: void on_tikzitDrawColor_clicked(); void on_hasTikzitFillColor_stateChanged(int state); void on_hasTikzitDrawColor_stateChanged(int state); + void on_noFill_stateChanged(int state); + void on_noDraw_stateChanged(int state); void on_hasTikzitShape_stateChanged(int state); void on_tikzitShape_currentIndexChanged(int); diff --git a/src/gui/styleeditor.ui b/src/gui/styleeditor.ui index 9c06894..22b04a6 100644 --- a/src/gui/styleeditor.ui +++ b/src/gui/styleeditor.ui @@ -822,6 +822,44 @@ <bool>true</bool> </attribute> </widget> + <widget class="QCheckBox" name="noFill"> + <property name="geometry"> + <rect> + <x>340</x> + <y>110</y> + <width>71</width> + <height>17</height> + </rect> + </property> + <property name="font"> + <font> + <pointsize>9</pointsize> + <italic>false</italic> + </font> + </property> + <property name="text"> + <string>none</string> + </property> + </widget> + <widget class="QCheckBox" name="noDraw"> + <property name="geometry"> + <rect> + <x>340</x> + <y>140</y> + <width>71</width> + <height>17</height> + </rect> + </property> + <property name="font"> + <font> + <pointsize>9</pointsize> + <italic>false</italic> + </font> + </property> + <property name="text"> + <string>none</string> + </property> + </widget> </widget> </widget> <resources/> diff --git a/src/gui/stylepalette.cpp b/src/gui/stylepalette.cpp index 15ed4c2..b862902 100644 --- a/src/gui/stylepalette.cpp +++ b/src/gui/stylepalette.cpp @@ -34,6 +34,8 @@ StylePalette::StylePalette(QWidget *parent) : ui(new Ui::StylePalette) { ui->setupUi(this); + _lastStyleIndex = 0; + _lastEdgeStyleIndex = 0; ui->styleListView->setModel(tikzit->styles()->nodeStyles()); ui->styleListView->setViewMode(QListView::IconMode); @@ -73,31 +75,95 @@ void StylePalette::reloadStyles() ui->currentCategory->addItems(tikzit->styles()->categories()); ui->currentCategory->setCurrentText(cat); } + + clearNodeStyle(); + clearEdgeStyle(); } -void StylePalette::changeNodeStyle(int increment) +int StylePalette::styleIndex() { QModelIndexList i = ui->styleListView->selectionModel()->selectedIndexes(); - int row = 0; - if (!i.isEmpty()) { - int row = (i[0].row()+increment)% tikzit->styles()->nodeStyles()->numInCategory(); - if (row < 0) row += tikzit->styles()->nodeStyles()->numInCategory(); + if (!i.isEmpty()) return i[0].row(); + else return 0; +} + +void StylePalette::setStyleIndex(int i) +{ + _lastStyleIndex = styleIndex(); + if (i < 0) { + i = 0; + } else { + int max = tikzit->styles()->nodeStyles()->numInCategory(); + if (i >= max) i = max - 1; } - //QModelIndex i1 = ui->styleListView->rootIndex().child(row, 0); - QModelIndex i1 =tikzit->styles()->nodeStyles()->index(row,0); + QModelIndex i1 =tikzit->styles()->nodeStyles()->index(i,0); ui->styleListView->selectionModel()->select(i1, QItemSelectionModel::ClearAndSelect); ui->styleListView->scrollTo(i1); } +int StylePalette::edgeStyleIndex() +{ + QModelIndexList i = ui->edgeStyleListView->selectionModel()->selectedIndexes(); + if (!i.isEmpty()) return i[0].row(); + else return 0; +} + +void StylePalette::setEdgeStyleIndex(int i) +{ + _lastEdgeStyleIndex = edgeStyleIndex(); + if (i < 0) { + i = 0; + } else { + int max = tikzit->styles()->edgeStyles()->numInCategory(); + if (i >= max) i = max - 1; + } + + QModelIndex i1 =tikzit->styles()->edgeStyles()->index(i,0); + ui->edgeStyleListView->selectionModel()->select(i1, QItemSelectionModel::ClearAndSelect); + ui->edgeStyleListView->scrollTo(i1); +} + void StylePalette::nextNodeStyle() { - changeNodeStyle(1); + setStyleIndex(styleIndex()+1); } void StylePalette::previousNodeStyle() { - changeNodeStyle(-1); + setStyleIndex(styleIndex()-1); +} + +void StylePalette::clearNodeStyle() +{ + setStyleIndex(0); +} + +void StylePalette::toggleClearNodeStyle() +{ + if (styleIndex() == 0) setStyleIndex(_lastStyleIndex); + else setStyleIndex(0); +} + +void StylePalette::nextEdgeStyle() +{ + setEdgeStyleIndex(edgeStyleIndex()+1); +} + +void StylePalette::previousEdgeStyle() +{ + setEdgeStyleIndex(edgeStyleIndex()-1); +} + +void StylePalette::clearEdgeStyle() +{ + setEdgeStyleIndex(0); +} + +void StylePalette::toggleClearEdgeStyle() +{ + if (edgeStyleIndex() == 0) setEdgeStyleIndex(_lastEdgeStyleIndex); + else setEdgeStyleIndex(0); } QString StylePalette::activeNodeStyleName() @@ -164,6 +230,7 @@ void StylePalette::on_currentCategory_currentTextChanged(const QString &cat) { //tikzit->styles()->refreshModels(_nodeModel, _edgeModel, cat); tikzit->styles()->nodeStyles()->setCategory(cat); + clearNodeStyle(); } //void StylePalette::on_buttonApplyNodeStyle_clicked() diff --git a/src/gui/stylepalette.h b/src/gui/stylepalette.h index f27cd4b..6e16812 100644 --- a/src/gui/stylepalette.h +++ b/src/gui/stylepalette.h @@ -36,10 +36,14 @@ public: void reloadStyles(); void nextNodeStyle(); void previousNodeStyle(); + void clearNodeStyle(); + void toggleClearNodeStyle(); + void nextEdgeStyle(); + void previousEdgeStyle(); + void clearEdgeStyle(); + void toggleClearEdgeStyle(); QString activeNodeStyleName(); QString activeEdgeStyleName(); - - public slots: void nodeStyleDoubleClicked(const QModelIndex &); void edgeStyleDoubleClicked(const QModelIndex &); @@ -51,7 +55,12 @@ public slots: //void on_buttonApplyNodeStyle_clicked(); private: - void changeNodeStyle(int increment); + int _lastStyleIndex; + int _lastEdgeStyleIndex; + int styleIndex(); + void setStyleIndex(int i); + int edgeStyleIndex(); + void setEdgeStyleIndex(int i); Ui::StylePalette *ui; diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp index 4577981..33e4710 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> @@ -38,6 +39,7 @@ TikzScene::TikzScene(TikzDocument *tikzDocument, ToolPalette *tools, { _modifyEdgeItem = nullptr; _edgeStartNodeItem = nullptr; + _drawNodeLabels = true; _drawEdgeItem = new QGraphicsLineItem(); _rubberBandItem = new QGraphicsRectItem(); _enabled = true; @@ -95,12 +97,25 @@ void TikzScene::graphReplaced() } _edgeItems.clear(); + foreach (PathItem *pi, _pathItems) { + removeItem(pi); + delete pi; + } + _pathItems.clear(); + foreach (Edge *e, graph()->edges()) { //e->attachStyle(); //e->updateControls(); EdgeItem *ei = new EdgeItem(e); _edgeItems.insert(e, ei); addItem(ei); + + Path *p = e->path(); + if (p && p->edges().first() == e) { + PathItem *pi = new PathItem(p); + _pathItems.insert(p, pi); + addItem(pi); + } } foreach (Node *n, graph()->nodes()) { @@ -235,13 +250,18 @@ void TikzScene::mergeNodes() Node *n = _tikzDocument->graph()->nodes()[i]; if (m1.contains(n)) delNodes.insert(i, n); } + + QSet<Path*> delPaths; for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) { Edge *e = _tikzDocument->graph()->edges()[i]; - if (m1.contains(e->source()) || m1.contains(e->target())) delEdges.insert(i, e); + if (m1.contains(e->source()) || m1.contains(e->target())) { + delEdges.insert(i, e); + if (e->path()) delPaths << e->path(); + } } - DeleteCommand *cmd = new DeleteCommand(this, delNodes, delEdges, - selNodes, selEdges); - _tikzDocument->undoStack()->push(cmd); + _tikzDocument->undoStack()->push(new SplitPathCommand(this, delPaths)); + _tikzDocument->undoStack()->push(new DeleteCommand(this, delNodes, delEdges, + selNodes, selEdges)); _tikzDocument->undoStack()->endMacro(); } @@ -294,11 +314,161 @@ void TikzScene::reverseSelectedEdges() _tikzDocument->undoStack()->push(cmd); } +void TikzScene::makePath(bool duplicateEdges) +{ + QSet<Node*> selNodes; + QSet<Edge*> selEdges; + QSet<Edge*> edges; + getSelection(selNodes, selEdges); + + edges = selEdges; + + // 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 && !duplicateEdges) { + //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; + } + } + + _tikzDocument->undoStack()->beginMacro("Make Path"); + + QVector<Edge *> oldEdgeOrder = graph()->edges(); + QSet<Edge *> oldEdges, newEdges; + oldEdges = edges; + + if (duplicateEdges) { + foreach (Edge *e, edges) { + Edge *e1 = e->copy(); + _tikzDocument->undoStack()->push(new AddEdgeCommand(this, e1, false, selNodes, selEdges)); + newEdges << e1; + oldEdgeOrder << e1; + } + edges = newEdges; + } + + // 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; + } + + _tikzDocument->undoStack()->push(new ReverseEdgesCommand(this, flip)); + + // order all of the edges together, and in the case of + // duplicate edges, just below the first original. + QVector<Edge*> newEdgeOrder; + bool firstEdge = true; + foreach (Edge *e, oldEdgeOrder) { + if (oldEdges.contains(e)) { + if (firstEdge) { + newEdgeOrder += p; + firstEdge = false; + } + + if (duplicateEdges) newEdgeOrder << e; + } else if (!newEdges.contains(e)) { + newEdgeOrder << e; + } + } + + _tikzDocument->undoStack()->push(new ReorderCommand(this, + graph()->nodes(), graph()->nodes(), oldEdgeOrder, newEdgeOrder)); + + QMap<Edge*, GraphElementData*> oldEdgeData; + foreach (Edge *e, p) { + if (e != p.first()) oldEdgeData[e] = e->data()->copy(); + } + + _tikzDocument->undoStack()->push(new MakePathCommand(this, p, oldEdgeData)); + _tikzDocument->undoStack()->endMacro(); +} + +void TikzScene::splitPath() +{ + 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; + } + } + + QSet<Path*> paths; + foreach (Edge *e, edges) { + if (e->path()) paths << e->path(); + } + + _tikzDocument->undoStack()->push(new SplitPathCommand(this, paths)); +} + void TikzScene::refreshZIndices() { qreal z = 0.0; foreach (Edge *e, graph()->edges()) { - edgeItems()[e]->setZValue(z); + if (e->path() && e == e->path()->edges().first()) { + pathItems()[e->path()]->setZValue(z); + edgeItems()[e]->setZValue(z + 0.1); + } else { + edgeItems()[e]->setZValue(z); + } z += 1.0; } @@ -317,6 +487,7 @@ void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event) _mouseDownPos = event->scenePos(); _draggingNodes = false; + _selectingEdge = nullptr; // radius of a control point for bezier edges, in scene coordinates qreal cpR = GLOBAL_SCALEF * (0.1); @@ -397,9 +568,19 @@ void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event) } } - auto its = items(_mouseDownPos); - if (!its.isEmpty() && dynamic_cast<NodeItem*>(its[0])) - _draggingNodes = true; + QList<QGraphicsItem*> its = items(_mouseDownPos); + if (!its.isEmpty()) { + if (dynamic_cast<NodeItem*>(its[0])) { + _draggingNodes = true; + } else { + foreach (QGraphicsItem *gi, its) { + if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) { + _selectingEdge = ei->edge(); + break; + } + } + } + } } break; @@ -511,6 +692,8 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } _modifyEdgeItem->readPos(); + Path *p = _modifyEdgeItem->edge()->path(); + if (p) pathItems()[p]->readPos(); } else if (_draggingNodes) { // nodes being dragged QGraphicsScene::mouseMoveEvent(event); @@ -597,6 +780,23 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) // otherwise, process mouse move normally QGraphicsScene::mouseReleaseEvent(event); + if (_selectingEdge) { + bool sel = edgeItems()[_selectingEdge]->isSelected(); + Path *p = _selectingEdge->path(); + if (p) { + foreach (Edge *e, p->edges()) { + if (e != _selectingEdge) + edgeItems()[e]->setSelected(sel); + nodeItems()[e->source()]->setSelected(sel); + nodeItems()[e->target()]->setSelected(sel); + } + } +// else { +// nodeItems()[_selectingEdge->source()]->setSelected(sel); +// nodeItems()[_selectingEdge->target()]->setSelected(sel); +// } + } + if (_rubberBandItem->isVisible()) { QPainterPath sel; sel.addRect(_rubberBandItem->rect()); @@ -707,25 +907,6 @@ void TikzScene::keyReleaseEvent(QKeyEvent *event) } - if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { - deleteSelectedItems(); - } else if (mod == Qt::NoModifier) { - switch(event->key()) { - case Qt::Key_S: - tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::SELECT); - break; - case Qt::Key_V: - case Qt::Key_N: - tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::VERTEX); - break; - case Qt::Key_E: - tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::EDGE); - break; - case Qt::Key_B: - tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::CROP); - break; - } - } foreach (QGraphicsItem *it, selectedItems()) it->update(); } @@ -739,11 +920,6 @@ void TikzScene::keyPressEvent(QKeyEvent *event) // slower, but seems to be more reliable than event->modifiers() Qt::KeyboardModifiers mod = QApplication::queryKeyboardModifiers(); - if (event->key() == Qt::Key_QuoteLeft) { - capture = true; - _styles->nextNodeStyle(); - } - if (mod & Qt::ControlModifier) { QSet<Node*> selNodes; QSet<Edge*> selEdges; @@ -865,6 +1041,26 @@ void TikzScene::keyPressEvent(QKeyEvent *event) _tikzDocument->undoStack()->endMacro(); } } + } else { // no CTRL key + if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { + deleteSelectedItems(); + } else if (!event->isAutoRepeat()) { + switch(event->key()) { + case Qt::Key_S: + tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::SELECT); + break; + case Qt::Key_V: + case Qt::Key_N: + tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::VERTEX); + break; + case Qt::Key_E: + tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::EDGE); + break; + case Qt::Key_B: + tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::CROP); + break; + } + } } foreach (QGraphicsItem *it, selectedItems()) it->update(); @@ -907,6 +1103,16 @@ void TikzScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) } } +bool TikzScene::drawNodeLabels() const +{ + return _drawNodeLabels; +} + +void TikzScene::setDrawNodeLabels(bool drawNodeLabels) +{ + _drawNodeLabels = drawNodeLabels; +} + bool TikzScene::highlightTails() const { return _highlightTails && getSelectedNodes().isEmpty(); @@ -956,6 +1162,7 @@ void TikzScene::deleteSelectedItems() QMap<int,Node*> deleteNodes; QMap<int,Edge*> deleteEdges; + QSet<Path*> deletePaths; for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) { Node *n = _tikzDocument->graph()->nodes()[i]; @@ -966,14 +1173,20 @@ void TikzScene::deleteSelectedItems() Edge *e = _tikzDocument->graph()->edges()[i]; if (selEdges.contains(e) || selNodes.contains(e->source()) || - selNodes.contains(e->target())) deleteEdges.insert(i, e); + selNodes.contains(e->target())) + { + if (e->path()) deletePaths << e->path(); + deleteEdges.insert(i, e); + } } //qDebug() << "nodes:" << deleteNodes; //qDebug() << "edges:" << deleteEdges; - DeleteCommand *cmd = new DeleteCommand(this, deleteNodes, deleteEdges, - selNodes, selEdges); - _tikzDocument->undoStack()->push(cmd); + _tikzDocument->undoStack()->beginMacro("Delete"); + _tikzDocument->undoStack()->push(new SplitPathCommand(this, deletePaths)); + _tikzDocument->undoStack()->push(new DeleteCommand(this, deleteNodes, deleteEdges, + selNodes, selEdges)); + _tikzDocument->undoStack()->endMacro(); } void TikzScene::copyToClipboard() @@ -1152,6 +1365,8 @@ void TikzScene::refreshSceneBounds() { void TikzScene::refreshAdjacentEdges(QList<Node*> nodes) { if (nodes.empty()) return; + + QSet<Path*> paths; foreach (Edge *e, _edgeItems.keys()) { EdgeItem *ei = _edgeItems[e]; @@ -1162,6 +1377,13 @@ void TikzScene::refreshAdjacentEdges(QList<Node*> nodes) ei->readPos(); } } + + // only update paths once + Path *p = ei->edge()->path(); + if (p && !paths.contains(p)) { + pathItems()[p]->readPos(); + paths << p; + } } } @@ -1188,3 +1410,8 @@ QMap<Edge*,EdgeItem*> &TikzScene::edgeItems() { return _edgeItems; } + +QMap<Path *, PathItem *> &TikzScene::pathItems() +{ + return _pathItems; +} diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h index f8dc7ec..e1d30d2 100644 --- a/src/gui/tikzscene.h +++ b/src/gui/tikzscene.h @@ -27,6 +27,7 @@ #include "graph.h" #include "nodeitem.h" #include "edgeitem.h" +#include "pathitem.h" #include "tikzdocument.h" #include "toolpalette.h" #include "stylepalette.h" @@ -48,6 +49,7 @@ public: Graph *graph(); QMap<Node*,NodeItem*> &nodeItems(); QMap<Edge*,EdgeItem*> &edgeItems(); + QMap<Path*,PathItem*> &pathItems(); void refreshAdjacentEdges(QList<Node*> nodes); // void setBounds(QRectF bounds); @@ -81,6 +83,8 @@ public: void reverseSelectedEdges(); + void makePath(bool duplicateEdges); + void splitPath(); void getSelection(QSet<Node*> &selNodes, QSet<Edge*> &selEdges) const; QSet<Node*> getSelectedNodes() const; @@ -90,6 +94,9 @@ public: bool highlightHeads() const; bool highlightTails() const; + bool drawNodeLabels() const; + void setDrawNodeLabels(bool drawNodeLabels); + public slots: void graphReplaced(); void refreshZIndices(); @@ -107,14 +114,17 @@ private: StylePalette *_styles; QMap<Node*,NodeItem*> _nodeItems; QMap<Edge*,EdgeItem*> _edgeItems; + QMap<Path*,PathItem*> _pathItems; QGraphicsLineItem *_drawEdgeItem; QGraphicsRectItem *_rubberBandItem; EdgeItem *_modifyEdgeItem; + Edge *_selectingEdge; NodeItem *_edgeStartNodeItem; NodeItem *_edgeEndNodeItem; bool _firstControlPoint; QPointF _mouseDownPos; bool _draggingNodes; + bool _drawNodeLabels; QMap<Node*,QPointF> _oldNodePositions; qreal _oldWeight; @@ -126,6 +136,8 @@ private: bool _highlightHeads; bool _highlightTails; bool _smartTool; + + bool _ctrlWasPressed; }; #endif // TIKZSCENE_H diff --git a/src/gui/tikzview.cpp b/src/gui/tikzview.cpp index 3615685..eb87002 100644 --- a/src/gui/tikzview.cpp +++ b/src/gui/tikzview.cpp @@ -149,6 +149,8 @@ void TikzView::wheelEvent(QWheelEvent *event) } else if (event->angleDelta().y() < 0) { zoomOut(); } + } else if (event->modifiers() & Qt::ShiftModifier) { + horizontalScrollBar()->setValue(horizontalScrollBar()->value() - event->angleDelta().y()); } } diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp index c5c26af..35345de 100644 --- a/src/gui/undocommands.cpp +++ b/src/gui/undocommands.cpp @@ -102,6 +102,9 @@ void EdgeBendCommand::undo() foreach(EdgeItem *ei, _scene->edgeItems()) { if (ei->edge() == _edge) { ei->readPos(); + + Path *p = ei->edge()->path(); + if (p) _scene->pathItems()[p]->readPos(); break; } } @@ -118,6 +121,9 @@ void EdgeBendCommand::redo() foreach(EdgeItem *ei, _scene->edgeItems()) { if (ei->edge() == _edge) { ei->readPos(); + + Path *p = ei->edge()->path(); + if (p) _scene->pathItems()[p]->readPos(); break; } } @@ -287,6 +293,9 @@ void ChangeEdgeModeCommand::undo() // FIXME: this act strangely sometimes _edge->setBasicBendMode(!_edge->basicBendMode()); _scene->edgeItems()[_edge]->readPos(); + Path *p = _edge->path(); + if (p) _scene->pathItems()[p]->readPos(); + GraphUpdateCommand::undo(); } @@ -294,6 +303,9 @@ void ChangeEdgeModeCommand::redo() { _edge->setBasicBendMode(!_edge->basicBendMode()); _scene->edgeItems()[_edge]->readPos(); + Path *p = _edge->path(); + if (p) _scene->pathItems()[p]->readPos(); + GraphUpdateCommand::redo(); } @@ -369,6 +381,16 @@ void PasteCommand::undo() { _scene->clearSelection(); + foreach (Path *p, _graph->paths()) { + PathItem *pi = _scene->pathItems()[p]; + _scene->pathItems().remove(p); + _scene->removeItem(pi); + delete pi; + + p->removeEdges(); + _scene->graph()->removePath(p); + } + foreach (Edge *e, _graph->edges()) { EdgeItem *ei = _scene->edgeItems()[e]; _scene->edgeItems().remove(e); @@ -399,6 +421,12 @@ void PasteCommand::redo() _scene->clearSelection(); _scene->graph()->insertGraph(_graph); + foreach (Path *p, _graph->paths()) { + PathItem *pi = new PathItem(p); + _scene->pathItems().insert(p, pi); + _scene->addItem(pi); + } + foreach (Edge *e, _graph->edges()) { e->attachStyle(); // in case styles have changed EdgeItem *ei = new EdgeItem(e); @@ -488,7 +516,7 @@ void ReflectNodesCommand::undo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::undo(); } @@ -501,7 +529,7 @@ void ReflectNodesCommand::redo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::redo(); } @@ -520,7 +548,7 @@ void RotateNodesCommand::undo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::undo(); } @@ -533,7 +561,7 @@ void RotateNodesCommand::redo() } } - _scene->refreshAdjacentEdges(_nodes.toList()); + _scene->refreshAdjacentEdges(_nodes.values()); GraphUpdateCommand::redo(); } @@ -594,3 +622,107 @@ 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(); + + PathItem *pi = _scene->pathItems()[p]; + _scene->removeItem(pi); + _scene->pathItems().remove(p); + delete pi; + + 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()); + } + } + + _scene->refreshZIndices(); + 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); + + PathItem *pi = new PathItem(p); + _scene->addItem(pi); + _scene->pathItems().insert(p, pi); + pi->readPos(); + + _scene->refreshZIndices(); + GraphUpdateCommand::redo(); +} + +SplitPathCommand::SplitPathCommand(TikzScene *scene, + const QSet<Path *> &paths, + QUndoCommand *parent) : + GraphUpdateCommand(scene, parent), _paths(paths) +{ + foreach (Path *p, paths) _edgeLists[p] = p->edges(); +} + +void SplitPathCommand::undo() +{ + foreach (Path *p, _paths) { + foreach (Edge *e, _edgeLists[p]) { + p->addEdge(e); + } + + _scene->graph()->addPath(p); + + PathItem *pi = new PathItem(p); + _scene->addItem(pi); + _scene->pathItems().insert(p, pi); + pi->readPos(); + } + + _scene->refreshZIndices(); + GraphUpdateCommand::undo(); +} + +void SplitPathCommand::redo() +{ + foreach (Path *p, _paths) { + PathItem *pi = _scene->pathItems()[p]; + _scene->removeItem(pi); + _scene->pathItems().remove(p); + delete pi; + + p->removeEdges(); + _scene->graph()->removePath(p); + } + + _scene->refreshZIndices(); + GraphUpdateCommand::redo(); +} diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h index 40f0a3b..c10eecc 100644 --- a/src/gui/undocommands.h +++ b/src/gui/undocommands.h @@ -255,4 +255,35 @@ 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; +}; + +class SplitPathCommand : public GraphUpdateCommand +{ +public: + explicit SplitPathCommand(TikzScene *scene, + const QSet<Path*> &paths, + QUndoCommand *parent = nullptr); + void undo() override; + void redo() override; +private: + QSet<Path*> _paths; + + // keep a copy of the edge lists so they can be added back to each path in undo() + QMap<Path*,QVector<Edge*>> _edgeLists; +}; + #endif // UNDOCOMMANDS_H diff --git a/src/tikzit.h b/src/tikzit.h index 94aba9d..5debdf3 100644 --- a/src/tikzit.h +++ b/src/tikzit.h @@ -49,7 +49,7 @@ #ifndef TIKZIT_H #define TIKZIT_H -#define TIKZIT_VERSION "2.1.4" +#define TIKZIT_VERSION "2.1.6" #include "mainwindow.h" #include "mainmenu.h" diff --git a/tex/2cat.tikz b/tex/2cat.tikz new file mode 100644 index 0000000..7888ad2 --- /dev/null +++ b/tex/2cat.tikz @@ -0,0 +1,40 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (0, 2.75) {}; + \node [style=none] (1) at (2, 2.75) {}; + \node [style=none] (2) at (2, -2.75) {}; + \node [style=none] (3) at (0, -2.75) {}; + \node [style=none] (4) at (0, -1.25) {}; + \node [style=none] (5) at (0, 1.25) {}; + \node [style=none] (6) at (-2, -2.75) {}; + \node [style=none] (7) at (-2, 2.75) {}; + \node [style=white dot] (8) at (0, 1.25) {$\mu$}; + \node [style=white dot] (9) at (0, -1.25) {$\delta$}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=region B] (5.center) + to (0.center) + to (1.center) + to (2.center) + to (3.center) + to (4.center) + to [bend right=60, looseness=1.25] cycle; + \draw [style=region B] (3.center) to (4.center); + \draw [style=region B, bend right=60, looseness=1.25] (4.center) to (5.center); + \draw [style=region B] (5.center) to (0.center); + \draw [style=region A] (0.center) + to (5.center) + to [bend right=60, looseness=1.25] (4.center) + to (3.center) + to (6.center) + to (7.center) + to cycle; + \draw [style=region C] (4.center) + to [bend left=60, looseness=1.25] (5.center) + to [bend left=60, looseness=1.25] cycle; + \draw (3.center) to (4.center); + \draw [bend left=60, looseness=1.25] (4.center) to (5.center); + \draw (5.center) to (0.center); + \draw [bend right=60, looseness=1.25] (4.center) to (5.center); + \end{pgfonlayer} +\end{tikzpicture} diff --git a/tex/2cat2.tikz b/tex/2cat2.tikz new file mode 100644 index 0000000..7a9ce93 --- /dev/null +++ b/tex/2cat2.tikz @@ -0,0 +1,36 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (0, 2) {}; + \node [style=none] (1) at (0, 0) {}; + \node [style=none] (2) at (1, -2) {}; + \node [style=none] (3) at (2, -2) {}; + \node [style=none] (4) at (2, 2) {}; + \node [style=none] (5) at (0, 2) {}; + \node [style=none] (6) at (0, 0) {}; + \node [style=none] (7) at (-1, -2) {}; + \node [style=none] (8) at (-2, -2) {}; + \node [style=none] (9) at (-2, 2) {}; + \node [style=white dot] (10) at (0, 0) {$\mu$}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=region B] (3.center) + to (4.center) + to (0.center) + to (1.center) + to [bend left] (2.center) + to cycle; + \draw [style=region A] (8.center) + to (9.center) + to (5.center) + to (6.center) + to [bend right] (7.center) + to cycle; + \draw [style=region C] (6.center) + to [bend right] (7.center) + to (2.center) + to [bend right] cycle; + \draw [bend left] (7.center) to (6.center); + \draw [bend right] (2.center) to (6.center); + \draw (6.center) to (5.center); + \end{pgfonlayer} +\end{tikzpicture} diff --git a/tex/3cat.tikz b/tex/3cat.tikz new file mode 100644 index 0000000..221bcd4 --- /dev/null +++ b/tex/3cat.tikz @@ -0,0 +1,72 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (0, -3.25) {}; + \node [style=none] (1) at (0, -1.5) {}; + \node [style=none] (2) at (0, 1.5) {}; + \node [style=none] (3) at (0, 3.25) {}; + \node [style=none] (4) at (2.25, -3.25) {}; + \node [style=none] (5) at (2.25, 3.25) {}; + \node [style=none] (6) at (-2.25, -3.25) {}; + \node [style=none] (7) at (-2.25, 3.25) {}; + \node [style=white dot] (8) at (0, -1.5) {}; + \node [style=white dot] (9) at (0, 1.5) {}; + \node [style=none] (10) at (1, -2.5) {}; + \node [style=none] (11) at (1, -0.75) {}; + \node [style=none] (12) at (1, 2.25) {}; + \node [style=none] (13) at (1, 4) {}; + \node [style=none] (14) at (3.25, -2.5) {}; + \node [style=none] (15) at (3.25, 4) {}; + \node [style=none] (16) at (-1.25, -2.5) {}; + \node [style=none] (17) at (-1.25, 4) {}; + \node [style=white dot] (18) at (1, -0.75) {}; + \node [style=white dot] (19) at (1, 2.25) {}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=region B] (3.center) + to (5.center) + to (4.center) + to (0.center) + to (1.center) + to [bend right=45, looseness=1.25] (2.center) + to cycle; + \draw [style=region A] (7.center) + to (6.center) + to (0.center) + to (1.center) + to [bend left=45, looseness=1.25] (2.center) + to (3.center) + to cycle; + \draw (0.center) to (1.center); + \draw [style=region C] (2.center) + to [bend left=45, looseness=1.25] (1.center) + to [bend left=45, looseness=1.25] cycle; + \draw [bend right=45, looseness=1.25] (1.center) to (2.center); + \draw (2.center) to (3.center); + \draw [bend left=45, looseness=1.25] (1.center) to (2.center); + \draw [style=region C, in=135, out=45, loop] (1.center) to (); + \draw [style=region C, in=135, out=45, loop] (1.center) to (); + \draw [style=region B] (13.center) + to (15.center) + to (14.center) + to (10.center) + to (11.center) + to [bend right=45, looseness=1.25] (12.center) + to cycle; + \draw [style=region A] (17.center) + to (16.center) + to (10.center) + to (11.center) + to [bend left=45, looseness=1.25] (12.center) + to (13.center) + to cycle; + \draw (10.center) to (11.center); + \draw [style=region C] (12.center) + to [bend left=45, looseness=1.25] (11.center) + to [bend left=45, looseness=1.25] cycle; + \draw [bend right=45, looseness=1.25] (11.center) to (12.center); + \draw (12.center) to (13.center); + \draw [bend left=45, looseness=1.25] (11.center) to (12.center); + \draw [style=region C, in=135, out=45, loop] (11.center) to (); + \draw [style=region C, in=135, out=45, loop] (11.center) to (); + \end{pgfonlayer} +\end{tikzpicture} diff --git a/tex/logo.tikzstyles b/tex/logo.tikzstyles new file mode 100644 index 0000000..1103b0b --- /dev/null +++ b/tex/logo.tikzstyles @@ -0,0 +1,15 @@ +% TiKZ style file generated by TikZiT. You may edit this file manually, +% but some things (e.g. comments) may be overwritten. To be readable in +% TikZiT, the only non-comment lines must be of the form: +% \tikzstyle{NAME}=[PROPERTY LIST] + +% Node styles +\tikzstyle{white dot}=[fill=white, draw=black, shape=circle] + +% Edge styles +\tikzstyle{bg}=[-, line width=0.5mm, fill={rgb,255: red,217; green,217; blue,217}] +\tikzstyle{fg}=[-, fill=black] +\tikzstyle{dir}=[->] +\tikzstyle{region A}=[-, draw=none, fill={rgb,255: red,255; green,160; blue,162}, opacity=0.8] +\tikzstyle{region B}=[-, draw=none, fill={rgb,255: red,190; green,185; blue,255}, opacity=0.8] +\tikzstyle{region C}=[-, fill={rgb,255: red,179; green,255; blue,192}, draw=none, opacity=0.8] diff --git a/tex/paths.tikz b/tex/paths.tikz new file mode 100644 index 0000000..ebbf676 --- /dev/null +++ b/tex/paths.tikz @@ -0,0 +1,42 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (0) at (-4, 1) {}; + \node [style=none] (1) at (-1, 1) {}; + \node [style=none] (2) at (-2.5, -1.5) {}; + \node [style=none] (7) at (-3.5, 0.75) {}; + \node [style=none] (8) at (-0.5, 0.75) {}; + \node [style=none] (9) at (-2, -1.75) {}; + \node [style=none] (10) at (-3, 0.5) {}; + \node [style=none] (11) at (0, 0.5) {}; + \node [style=none] (12) at (-1.5, -2) {}; + \node [style=none] (13) at (-2.5, 0.25) {}; + \node [style=none] (14) at (0.5, 0.25) {}; + \node [style=none] (15) at (-1, -2.25) {}; + \node [style=none] (3) at (-0.25, 4.25) {}; + \node [style=none] (4) at (2, 3.75) {}; + \node [style=none] (5) at (2, 2) {}; + \node [style=none] (6) at (3.25, 1.25) {}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=bg] (2.center) + to (0.center) + to (1.center) + to cycle; + \draw [style=bg] (9.center) + to (7.center) + to (8.center) + to cycle; + \draw [style=bg] (12.center) + to (10.center) + to (11.center) + to cycle; + \draw [style=bg] (15.center) + to (13.center) + to (14.center) + to cycle; + \draw [style=dir] (3.center) + to (4.center) + to (5.center) + to (6.center); + \end{pgfonlayer} +\end{tikzpicture} diff --git a/tex/sample/figures/fig.tikz b/tex/sample/figures/fig.tikz index f454de2..4a30037 100644 --- a/tex/sample/figures/fig.tikz +++ b/tex/sample/figures/fig.tikz @@ -1,9 +1,9 @@ -\begin{tikzpicture}[tikzfig] +\begin{tikzpicture} \begin{pgfonlayer}{nodelayer} \node [style=red node] (0) at (0, 1) {}; \node [style=blue node 2] (1) at (1, 0) {}; \node [style=blue node] (2) at (-1, 0) {}; - \node [style=yellow square] (3) at (0, -1) {\anglevec{\alpha}}; + \node [style=yellow square] (3) at (0, -1) {\param{\alpha}}; \end{pgfonlayer} \begin{pgfonlayer}{edgelayer} \draw [in=-90, out=0] (3) to (1); diff --git a/tex/sample/sample.tikzstyles b/tex/sample/sample.tikzstyles index f32728c..2a3bb7f 100644 --- a/tex/sample/sample.tikzstyles +++ b/tex/sample/sample.tikzstyles @@ -8,6 +8,7 @@ \tikzstyle{blue node}=[fill=blue, shape=circle, draw=black, tikzit category=nodes] \tikzstyle{green node}=[tikzit fill=green, fill=green, shape=circle, draw=black, tikzit category=nodes] \tikzstyle{yellow square}=[draw=black, fill=yellow, shape=rectangle] +\tikzstyle{blue node 2}=[fill={rgb,255: red,128; green,0; blue,128}, draw=black, shape=circle, tikzit fill=blue] % Edge styles \tikzstyle{dashed edge}=[<->, dashed] diff --git a/tex/tikzit-logo.tikz b/tex/tikzit-logo.tikz new file mode 100755 index 0000000..be1eb66 --- /dev/null +++ b/tex/tikzit-logo.tikz @@ -0,0 +1,76 @@ +\begin{tikzpicture} + \begin{pgfonlayer}{nodelayer} + \node [style=none] (5) at (-0.5, -1.25) {}; + \node [style=none] (6) at (0.5, 1.25) {}; + \node [style=none] (7) at (1.25, 0.5) {}; + \node [style=none] (8) at (1.25, 2) {}; + \node [style=none] (9) at (2, 1.25) {}; + \node [style=none] (10) at (2.75, 2) {}; + \node [style=none] (11) at (2, 2.75) {}; + \node [style=none] (18) at (1.25, -2) {}; + \node [style=none] (20) at (2.75, -2) {}; + \node [style=none] (22) at (2, -2.75) {}; + \node [style=none] (23) at (2, -1.25) {}; + \node [style=none] (26) at (-2.75, -2) {}; + \node [style=none] (28) at (-1.25, -2) {}; + \node [style=none] (30) at (-2, -2.75) {}; + \node [style=none] (31) at (-2, -1.25) {}; + \node [style=none] (32) at (0.5, -1.25) {}; + \node [style=none] (33) at (-1.25, -0.5) {}; + \node [style=none] (35) at (1.25, -0.5) {}; + \node [style=none] (36) at (-0.5, 1.25) {}; + \node [style=none] (37) at (-1.25, 0.5) {}; + \node [style=none] (65) at (0, 3) {}; + \node [style=none] (69) at (0.775, -2.9) {}; + \node [style=none] (70) at (2, -3.5) {}; + \node [style=none] (71) at (3.5, -2) {}; + \node [style=none] (72) at (2.9, -0.775) {}; + \node [style=none] (80) at (-3, 0) {}; + \node [style=none] (81) at (-0.775, -2.9) {}; + \node [style=none] (82) at (-2, -3.5) {}; + \node [style=none] (83) at (-3.5, -2) {}; + \node [style=none] (84) at (-2.9, -0.775) {}; + \node [style=none] (85) at (0.775, 2.9) {}; + \node [style=none] (86) at (2, 3.5) {}; + \node [style=none] (87) at (3.5, 2) {}; + \node [style=none] (88) at (2.9, 0.775) {}; + \end{pgfonlayer} + \begin{pgfonlayer}{edgelayer} + \draw [style=bg] (72.center) + to [in=90, out=-30, looseness=0.75] (71.center) + to [in=0, out=-90] (70.center) + to [in=-60, out=180, looseness=0.75] (69.center) + to [in=-15, out=-165] (81.center) + to [in=0, out=-120, looseness=0.75] (82.center) + to [in=-90, out=180] (83.center) + to [in=-150, out=90, looseness=0.75] (84.center) + to [in=-90, out=105, looseness=0.75] (80.center) + to [in=-180, out=90] (65.center) + to [in=165, out=0, looseness=0.75] (85.center) + to [in=-180, out=60, looseness=0.75] (86.center) + to [in=90, out=0] (87.center) + to [in=30, out=-90, looseness=0.75] (88.center) + to [in=75, out=-75] cycle; + \draw [style=fg] (36.center) + to [in=135, out=45] (6.center) + to [in=-90, out=-45, looseness=1.25] (8.center) + to [in=-180, out=90] (11.center) + to [in=90, out=0] (10.center) + to [in=0, out=-90] (9.center) + to [in=135, out=180, looseness=1.25] (7.center) + to [in=45, out=-45] (35.center) + to [in=180, out=-135, looseness=1.25] (23.center) + to [in=90, out=0] (20.center) + to [in=0, out=-90] (22.center) + to [in=-90, out=180] (18.center) + to [in=45, out=90, looseness=1.25] (32.center) + to [in=-45, out=-135, looseness=1.25] (5.center) + to [in=90, out=135, looseness=1.25] (28.center) + to [in=0, out=-90] (30.center) + to [in=-90, out=180] (26.center) + to [in=-180, out=90] (31.center) + to [in=-45, out=0, looseness=1.25] (33.center) + to [in=-135, out=135] (37.center) + to cycle; + \end{pgfonlayer} +\end{tikzpicture} @@ -2,7 +2,7 @@ QT += core gui widgets network -VERSION = 2.1.4 +VERSION = 2.1.6 test { CONFIG += testcase @@ -53,6 +53,8 @@ include(flex.pri) include(bison.pri) SOURCES += src/gui/mainwindow.cpp \ + src/data/path.cpp \ + src/gui/pathitem.cpp \ src/gui/toolpalette.cpp \ src/gui/tikzscene.cpp \ src/data/graph.cpp \ @@ -85,6 +87,8 @@ SOURCES += src/gui/mainwindow.cpp \ src/gui/preferencedialog.cpp HEADERS += src/gui/mainwindow.h \ + src/data/path.h \ + src/gui/pathitem.h \ src/gui/toolpalette.h \ src/gui/tikzscene.h \ src/data/graph.h \ |