summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/data/delimitedstringvalidator.cpp58
-rw-r--r--src/data/delimitedstringvalidator.h57
-rw-r--r--src/data/edge.cpp90
-rw-r--r--src/data/edge.h21
-rw-r--r--src/data/graph.cpp4
-rw-r--r--src/data/graphelementdata.cpp9
-rw-r--r--src/data/graphelementdata.h8
-rw-r--r--src/data/graphelementproperty.h37
-rw-r--r--src/data/node.cpp9
-rw-r--r--src/data/node.h1
-rw-r--r--src/data/pdfdocument.cpp134
-rw-r--r--src/data/pdfdocument.h28
-rw-r--r--src/data/tikzdocument.cpp62
-rw-r--r--src/data/tikzdocument.h6
-rw-r--r--src/gui/delimitedstringitemdelegate.cpp44
-rw-r--r--src/gui/delimitedstringitemdelegate.h42
-rw-r--r--src/gui/exportdialog.cpp194
-rw-r--r--src/gui/exportdialog.h61
-rw-r--r--src/gui/exportdialog.ui162
-rw-r--r--src/gui/latexprocess.cpp169
-rw-r--r--src/gui/latexprocess.h56
-rw-r--r--src/gui/mainmenu.cpp51
-rw-r--r--src/gui/mainmenu.h7
-rw-r--r--src/gui/mainmenu.ui36
-rw-r--r--src/gui/preferencedialog.cpp111
-rw-r--r--src/gui/preferencedialog.h31
-rw-r--r--src/gui/preferencedialog.ui272
-rw-r--r--src/gui/previewwindow.cpp208
-rw-r--r--src/gui/previewwindow.h72
-rw-r--r--src/gui/previewwindow.ui107
-rw-r--r--src/gui/styleeditor.cpp62
-rw-r--r--src/gui/styleeditor.h26
-rw-r--r--src/gui/tikzscene.cpp101
-rw-r--r--src/gui/tikzscene.h4
-rw-r--r--src/gui/tikzview.cpp8
-rw-r--r--src/gui/undocommands.cpp6
-rw-r--r--src/gui/undocommands.h36
-rw-r--r--src/main.cpp12
-rw-r--r--src/tikzit.cpp277
-rw-r--r--src/tikzit.h27
-rw-r--r--src/util.cpp35
-rw-r--r--src/util.h16
42 files changed, 2435 insertions, 322 deletions
diff --git a/src/data/delimitedstringvalidator.cpp b/src/data/delimitedstringvalidator.cpp
new file mode 100644
index 0000000..9f1057e
--- /dev/null
+++ b/src/data/delimitedstringvalidator.cpp
@@ -0,0 +1,58 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "delimitedstringvalidator.h"
+
+DelimitedStringValidator::DelimitedStringValidator(QObject *parent) : QValidator(parent)
+{
+}
+
+QValidator::State DelimitedStringValidator::validate(QString &input, int &/*pos*/) const
+{
+ int depth = braceDepth(input);
+ if (depth == 0) return Acceptable;
+ else if (depth > 0) return Intermediate;
+ else return Invalid;
+}
+
+void DelimitedStringValidator::fixup(QString &input) const
+{
+ int depth = braceDepth(input);
+ if (depth > 0) input.append(QString("}").repeated(depth));
+}
+
+int DelimitedStringValidator::braceDepth(QString input) const
+{
+ int depth = 0;
+ bool escape = false;
+ for (int i = 0; i < input.length(); ++i) {
+ QCharRef c = input[i];
+ if (escape) {
+ escape = false;
+ } else if (c == '\\') {
+ escape = true;
+ } else if (c == '{') {
+ depth++;
+ } else if (c == '}') {
+ depth--;
+ if (depth < 0) return -1;
+ }
+ }
+
+ return depth;
+}
diff --git a/src/data/delimitedstringvalidator.h b/src/data/delimitedstringvalidator.h
new file mode 100644
index 0000000..60c2513
--- /dev/null
+++ b/src/data/delimitedstringvalidator.h
@@ -0,0 +1,57 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*!
+ * A string validator which keeps curly braces matched. Used in various places
+ * to ensure the user doesn't make non-parseable .tikz or .tikzstyles files.
+ *
+ * Its validation function will return Acceptable if all curly braces are matched
+ * properly, Intermediate if all braces are matched except for possibly some opening
+ * curly braces, and Invalid if there are unmatched closing curly braces.
+ */
+
+#ifndef DELIMITEDSTRINGVALIDATOR_H
+#define DELIMITEDSTRINGVALIDATOR_H
+
+
+#include <QObject>
+#include <QValidator>
+
+
+class DelimitedStringValidator : public QValidator
+{
+ Q_OBJECT
+public:
+ DelimitedStringValidator(QObject *parent);
+ QValidator::State validate(QString &input, int &/*pos*/) const override;
+
+ /*!
+ * \brief fixup adds curly braces until all braces are matched (if possible)
+ * \param input
+ */
+ void fixup(QString &input) const override;
+private:
+ /*!
+ * \brief braceDepth computes the final (non-escaped) curly-brace depth of a given string
+ * \param input a string
+ * \return the final brace depth, or -1 if the depth *ever* drops below 0
+ */
+ int braceDepth(QString input) const;
+};
+
+#endif // DELIMITEDSTRINGVALIDATOR_H
diff --git a/src/data/edge.cpp b/src/data/edge.cpp
index 0ae566b..864d5ed 100644
--- a/src/data/edge.cpp
+++ b/src/data/edge.cpp
@@ -26,8 +26,8 @@
Edge::Edge(Node *s, Node *t, QObject *parent) :
QObject(parent), _source(s), _target(t)
{
- _data = new GraphElementData();
- _edgeNode = 0;
+ _data = new GraphElementData(this);
+ _edgeNode = nullptr;
_dirty = true;
if (s != t) {
@@ -35,24 +35,18 @@ Edge::Edge(Node *s, Node *t, QObject *parent) :
_bend = 0;
_inAngle = 0;
_outAngle = 0;
- _weight = 0.4f;
+ _weight = 0.4;
} else {
_basicBendMode = false;
_bend = 0;
_inAngle = 135;
_outAngle = 45;
- _weight = 1.0f;
+ _weight = 1.0;
}
_style = noneEdgeStyle;
updateControls();
}
-Edge::~Edge()
-{
- delete _data;
- delete _edgeNode;
-}
-
/*!
* @brief Edge::copy makes a deep copy of an edge.
* @param nodeTable is an optional pointer to a table mapping the old source/target
@@ -63,7 +57,7 @@ Edge::~Edge()
Edge *Edge::copy(QMap<Node*,Node*> *nodeTable)
{
Edge *e;
- if (nodeTable == 0) e = new Edge(_source, _target);
+ if (nodeTable == nullptr) e = new Edge(_source, _target);
else e = new Edge(nodeTable->value(_source), nodeTable->value(_target));
e->setData(_data->copy());
e->setBasicBendMode(_basicBendMode);
@@ -103,8 +97,9 @@ GraphElementData *Edge::data() const
void Edge::setData(GraphElementData *data)
{
- delete _data;
+ GraphElementData *oldData = _data;
_data = data;
+ oldData->deleteLater();
setAttributesFromData();
}
@@ -148,13 +143,14 @@ Node *Edge::edgeNode() const
void Edge::setEdgeNode(Node *edgeNode)
{
- if (_edgeNode != 0) delete _edgeNode;
+ Node *oldEdgeNode = _edgeNode;
_edgeNode = edgeNode;
+ if (oldEdgeNode != nullptr) oldEdgeNode->deleteLater();
}
bool Edge::hasEdgeNode()
{
- return _edgeNode != 0;
+ return _edgeNode != nullptr;
}
void Edge::updateControls() {
@@ -162,22 +158,22 @@ void Edge::updateControls() {
QPointF src = _source->point();
QPointF targ = _target->point();
- float dx = (targ.x() - src.x());
- float dy = (targ.y() - src.y());
+ qreal dx = (targ.x() - src.x());
+ qreal dy = (targ.y() - src.y());
- float outAngleR = 0.0f;
- float inAngleR = 0.0f;
+ qreal outAngleR = 0.0;
+ qreal inAngleR = 0.0;
if (_basicBendMode) {
- float angle = std::atan2(dy, dx);
- float bnd = (float)_bend * (M_PI / 180.0f);
+ qreal angle = std::atan2(dy, dx);
+ qreal bnd = static_cast<qreal>(_bend) * (M_PI / 180.0);
outAngleR = angle - bnd;
inAngleR = M_PI + angle + bnd;
- _outAngle = outAngleR * (180.f / M_PI);
- _inAngle = inAngleR * (180.f / M_PI);
+ _outAngle = static_cast<int>(round(outAngleR * (180.0 / M_PI)));
+ _inAngle = static_cast<int>(round(inAngleR * (180.0 / M_PI)));
} else {
- outAngleR = (float)_outAngle * (M_PI / 180.0f);
- inAngleR = (float)_inAngle * (M_PI / 180.0f);
+ outAngleR = static_cast<qreal>(_outAngle) * (M_PI / 180.0);
+ inAngleR = static_cast<qreal>(_inAngle) * (M_PI / 180.0);
}
// TODO: calculate head and tail properly, not just for circles
@@ -196,7 +192,7 @@ void Edge::updateControls() {
}
// give a default distance for self-loops
- _cpDist = (dx==0.0f && dy==0.0f) ? _weight : std::sqrt(dx*dx + dy*dy) * _weight;
+ _cpDist = (almostZero(dx) && almostZero(dy)) ? _weight : std::sqrt(dx*dx + dy*dy) * _weight;
_cp1 = QPointF(src.x() + (_cpDist * std::cos(outAngleR)),
src.y() + (_cpDist * std::sin(outAngleR)));
@@ -204,9 +200,9 @@ void Edge::updateControls() {
_cp2 = QPointF(targ.x() + (_cpDist * std::cos(inAngleR)),
targ.y() + (_cpDist * std::sin(inAngleR)));
- _mid = bezierInterpolateFull (0.5f, _tail, _cp1, _cp2, _head);
- _tailTangent = bezierTangent(0.0f, 0.1f);
- _headTangent = bezierTangent(1.0f, 0.9f);
+ _mid = bezierInterpolateFull (0.5, _tail, _cp1, _cp2, _head);
+ _tailTangent = bezierTangent(0.0, 0.1);
+ _headTangent = bezierTangent(1.0, 0.9);
}
void Edge::setAttributesFromData()
@@ -218,16 +214,16 @@ void Edge::setAttributesFromData()
_bend = -30;
} else if (_data->atom("bend right")) {
_bend = 30;
- } else if (_data->property("bend left") != 0) {
+ } else if (_data->property("bend left") != nullptr) {
_bend = -_data->property("bend left").toInt(&ok);
if (!ok) _bend = -30;
- } else if (_data->property("bend right") != 0) {
+ } else if (_data->property("bend right") != nullptr) {
_bend = _data->property("bend right").toInt(&ok);
if (!ok) _bend = 30;
} else {
_bend = 0;
- if (_data->property("in") != 0 && _data->property("out") != 0) {
+ if (_data->property("in") != nullptr && _data->property("out") != nullptr) {
_basicBendMode = false;
_inAngle = _data->property("in").toInt(&ok);
if (!ok) _inAngle = 0;
@@ -237,10 +233,10 @@ void Edge::setAttributesFromData()
}
if (!_data->property("looseness").isNull()) {
- _weight = _data->property("looseness").toFloat(&ok) / 2.5f;
- if (!ok) _weight = 0.4f;
+ _weight = _data->property("looseness").toDouble(&ok) / 2.5;
+ if (!ok) _weight = 0.4;
} else {
- _weight = (isSelfLoop()) ? 1.0f : 0.4f;
+ _weight = (isSelfLoop()) ? 1.0 : 0.4;
}
//qDebug() << "bend: " << _bend << " in: " << _inAngle << " out: " << _outAngle;
@@ -258,8 +254,6 @@ void Edge::updateData()
_data->unsetProperty("bend right");
_data->unsetProperty("looseness");
- // TODO: style handling?
-
if (_basicBendMode) {
if (_bend != 0) {
QString bendKey;
@@ -284,8 +278,8 @@ void Edge::updateData()
}
if (_source == _target) _data->setAtom("loop");
- if (!isSelfLoop() && !isStraight() && _weight != 0.4f)
- _data->setProperty("looseness", QString::number(_weight*2.5f, 'f', 2));
+ if (!isSelfLoop() && !isStraight() && !almostEqual(_weight, 0.4))
+ _data->setProperty("looseness", QString::number(_weight*2.5, 'f', 2));
if (_source->isBlankNode()) _sourceAnchor = "center";
else _sourceAnchor = "";
if (_target->isBlankNode()) _targetAnchor = "center";
@@ -329,7 +323,7 @@ int Edge::outAngle() const
return _outAngle;
}
-float Edge::weight() const
+qreal Edge::weight() const
{
return _weight;
}
@@ -339,7 +333,7 @@ bool Edge::basicBendMode() const
return _basicBendMode;
}
-float Edge::cpDist() const
+qreal Edge::cpDist() const
{
return _cpDist;
}
@@ -364,7 +358,7 @@ void Edge::setOutAngle(int outAngle)
_outAngle = outAngle;
}
-void Edge::setWeight(float weight)
+void Edge::setWeight(qreal weight)
{
_weight = weight;
}
@@ -406,18 +400,18 @@ Style *Edge::style() const
return _style;
}
-QPointF Edge::bezierTangent(float start, float end) const
+QPointF Edge::bezierTangent(qreal start, qreal end) const
{
- float dx = bezierInterpolate(end, _tail.x(), _cp1.x(), _cp2.x(), _head.x()) -
+ qreal dx = bezierInterpolate(end, _tail.x(), _cp1.x(), _cp2.x(), _head.x()) -
bezierInterpolate(start, _tail.x(), _cp1.x(), _cp2.x(), _head.x());
- float dy = bezierInterpolate(end, _tail.y(), _cp1.y(), _cp2.y(), _head.y()) -
+ qreal dy = bezierInterpolate(end, _tail.y(), _cp1.y(), _cp2.y(), _head.y()) -
bezierInterpolate(start, _tail.y(), _cp1.y(), _cp2.y(), _head.y());
// normalise
- float len = sqrt(dx*dx + dy * dy);
- if (len != 0) {
- dx = (dx / len) * 0.1f;
- dy = (dy / len) * 0.1f;
+ qreal len = sqrt(dx*dx + dy*dy);
+ if (almostZero(len)) {
+ dx = (dx / len) * 0.1;
+ dy = (dy / len) * 0.1;
}
return QPointF(dx, dy);
diff --git a/src/data/edge.h b/src/data/edge.h
index ad71364..909824b 100644
--- a/src/data/edge.h
+++ b/src/data/edge.h
@@ -16,6 +16,10 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+/*!
+ * Class representing an edge in a Graph.
+ */
+
#ifndef EDGE_H
#define EDGE_H
@@ -30,9 +34,8 @@ class Edge : public QObject
{
Q_OBJECT
public:
- explicit Edge(Node *s, Node *t, QObject *parent = 0);
- ~Edge();
- Edge *copy(QMap<Node *, Node *> *nodeTable = 0);
+ explicit Edge(Node *s, Node *t, QObject *parent = nullptr);
+ Edge *copy(QMap<Node *, Node *> *nodeTable = nullptr);
Node *source() const;
Node *target() const;
@@ -68,15 +71,15 @@ public:
int bend() const;
int inAngle() const;
int outAngle() const;
- float weight() const;
+ qreal weight() const;
bool basicBendMode() const;
- float cpDist() const;
+ qreal cpDist() const;
void setBasicBendMode(bool mode);
void setBend(int bend);
void setInAngle(int inAngle);
void setOutAngle(int outAngle);
- void setWeight(float weight);
+ void setWeight(qreal weight);
int tikzLine() const;
void setTikzLine(int tikzLine);
@@ -92,7 +95,7 @@ signals:
public slots:
private:
- QPointF bezierTangent(float start, float end) const;
+ QPointF bezierTangent(qreal start, qreal end) const;
QString _sourceAnchor;
QString _targetAnchor;
@@ -112,8 +115,8 @@ private:
int _bend;
int _inAngle;
int _outAngle;
- float _weight;
- float _cpDist;
+ qreal _weight;
+ qreal _cpDist;
QPointF _head;
QPointF _tail;
diff --git a/src/data/graph.cpp b/src/data/graph.cpp
index bba2061..1dd5574 100644
--- a/src/data/graph.cpp
+++ b/src/data/graph.cpp
@@ -152,8 +152,9 @@ GraphElementData *Graph::data() const
void Graph::setData(GraphElementData *data)
{
- delete _data;
+ GraphElementData *oldData = _data;
_data = data;
+ oldData->deleteLater();
}
const QVector<Node*> &Graph::nodes()
@@ -278,6 +279,7 @@ Graph *Graph::copyOfSubgraphWithNodes(QSet<Node *> nds)
{
Graph *g = new Graph();
g->setData(_data->copy());
+ g->data()->setAtom("tikzfig");
QMap<Node*,Node*> nodeTable;
foreach (Node *n, nodes()) {
if (nds.contains(n)) {
diff --git a/src/data/graphelementdata.cpp b/src/data/graphelementdata.cpp
index 810ebd6..cd09a6d 100644
--- a/src/data/graphelementdata.cpp
+++ b/src/data/graphelementdata.cpp
@@ -23,18 +23,12 @@
GraphElementData::GraphElementData(QVector<GraphElementProperty> init, QObject *parent) : QAbstractItemModel(parent)
{
- root = new GraphElementProperty();
_properties = init;
}
GraphElementData::GraphElementData(QObject *parent) : QAbstractItemModel(parent) {
- root = new GraphElementProperty();
}
-GraphElementData::~GraphElementData()
-{
- delete root;
-}
GraphElementData *GraphElementData::copy()
{
@@ -103,7 +97,8 @@ bool GraphElementData::hasProperty(QString key)
bool GraphElementData::atom(QString atom)
{
- return (indexOfKey(atom) != -1);
+ int idx = indexOfKey(atom);
+ return (idx != -1 && _properties[idx].atom());
}
int GraphElementData::indexOfKey(QString key)
diff --git a/src/data/graphelementdata.h b/src/data/graphelementdata.h
index 23f0466..dce0d46 100644
--- a/src/data/graphelementdata.h
+++ b/src/data/graphelementdata.h
@@ -16,6 +16,12 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+/*!
+ * A list of GraphElementProperty objects, which convenience methods
+ * for lookup, deletion, re-ordering, etc. It inherits QAbstractItemModel
+ * so it can be used as the model for a QTreeView in the StyleEditor.
+ */
+
#ifndef GRAPHELEMENTDATA_H
#define GRAPHELEMENTDATA_H
@@ -34,7 +40,6 @@ public:
explicit GraphElementData(QVector<GraphElementProperty> init,
QObject *parent = 0);
explicit GraphElementData(QObject *parent = 0);
- ~GraphElementData();
GraphElementData *copy();
void setProperty(QString key, QString value);
void unsetProperty(QString key);
@@ -78,7 +83,6 @@ public slots:
private:
QVector<GraphElementProperty> _properties;
- GraphElementProperty *root;
};
#endif // GRAPHELEMENTDATA_H
diff --git a/src/data/graphelementproperty.h b/src/data/graphelementproperty.h
index 4ebe104..5777c18 100644
--- a/src/data/graphelementproperty.h
+++ b/src/data/graphelementproperty.h
@@ -16,6 +16,11 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+/*!
+ * A class which holds either a single key/value pair (i.e. a proper property)
+ * or simply a key with no value (i.e. an atom).
+ */
+
#ifndef GRAPHELEMENTPROPERTY_H
#define GRAPHELEMENTPROPERTY_H
@@ -26,13 +31,19 @@ class GraphElementProperty
public:
GraphElementProperty();
- // full constructor
GraphElementProperty(QString key, QString value, bool atom);
- // construct a proper property
+ /*!
+ * \brief GraphElementProperty constructs a proper property with the given key/value
+ * \param key
+ * \param value
+ */
GraphElementProperty(QString key, QString value);
- // construct an atom
+ /*!
+ * \brief GraphElementProperty constructs an atom with the given key
+ * \param key
+ */
GraphElementProperty(QString key);
QString key() const;
@@ -40,9 +51,29 @@ public:
QString value() const;
void setValue(const QString &value);
bool atom() const;
+
+ /*!
+ * \brief operator == returns true for atoms if the keys match and for properties
+ * if the keys and values match. Note a property is never equal to an atom.
+ * \param p
+ * \return
+ */
bool operator==(const GraphElementProperty &p);
+ /*!
+ * \brief tikzEscape prepares a property key or value for export to tikz code. If
+ * the property only contains numbers, letters, whitespace, or the characters (<,>,-)
+ * this method does nothing. Otherwise, wrap the property in curly braces.
+ * \param str
+ * \return
+ */
static QString tikzEscape(QString str);
+
+ /*!
+ * \brief tikz escapes the key/value of a propery or atom and outputs it as "key=value"
+ * for properties and "key" for atoms.
+ * \return
+ */
QString tikz();
signals:
diff --git a/src/data/node.cpp b/src/data/node.cpp
index 75acd00..8ec5e9b 100644
--- a/src/data/node.cpp
+++ b/src/data/node.cpp
@@ -23,15 +23,11 @@
Node::Node(QObject *parent) : QObject(parent), _tikzLine(-1)
{
- _data = new GraphElementData();
+ _data = new GraphElementData(this);
_style = noneStyle;
_data->setProperty("style", "none");
}
-Node::~Node()
-{
- delete _data;
-}
Node *Node::copy() {
Node *n1 = new Node();
@@ -81,8 +77,9 @@ GraphElementData *Node::data() const
void Node::setData(GraphElementData *data)
{
- delete _data;
+ GraphElementData *oldData = _data;
_data = data;
+ oldData->deleteLater();
}
QString Node::styleName() const
diff --git a/src/data/node.h b/src/data/node.h
index 490393d..c40627b 100644
--- a/src/data/node.h
+++ b/src/data/node.h
@@ -31,7 +31,6 @@ class Node : public QObject
Q_OBJECT
public:
explicit Node(QObject *parent = 0);
- ~Node();
Node *copy();
diff --git a/src/data/pdfdocument.cpp b/src/data/pdfdocument.cpp
new file mode 100644
index 0000000..c9574b8
--- /dev/null
+++ b/src/data/pdfdocument.cpp
@@ -0,0 +1,134 @@
+#include "pdfdocument.h"
+
+#include <QFile>
+#include <QByteArray>
+#include <QDebug>
+#include <QApplication>
+#include <QClipboard>
+
+PdfDocument::PdfDocument(QString file, QObject *parent) : QObject(parent)
+{
+ // use loadFromData to avoid holding a lock on the PDF file in windows
+ QFile f(file);
+ if (f.open(QFile::ReadOnly)) {
+ QByteArray data = f.readAll();
+ f.close();
+ _doc = Poppler::Document::loadFromData(data);
+ } else {
+ _doc = nullptr;
+ }
+
+ if (!_doc) {
+ _doc = nullptr;
+ _page = nullptr;
+ } else {
+ _doc->setRenderHint(Poppler::Document::Antialiasing);
+ _doc->setRenderHint(Poppler::Document::TextAntialiasing);
+ _doc->setRenderHint(Poppler::Document::TextHinting);
+ _page = _doc->page(0);
+ }
+}
+
+void PdfDocument::renderTo(QLabel *label, QRect rect)
+{
+ if (!isValid()) return;
+
+ QSizeF pageSize = _page->pageSizeF();
+
+ qreal ratio = label->devicePixelRatioF();
+ //QRect rect = ui->scrollArea->visibleRegion().boundingRect();
+ int w = static_cast<int>(ratio * (rect.width() - 20));
+ int h = static_cast<int>(ratio * (rect.height() - 20));
+
+ // not all platforms have fmin, compute the min by hand
+ qreal hscale = static_cast<qreal>(w) / pageSize.width();
+ qreal vscale = static_cast<qreal>(h) / pageSize.height();
+ qreal scale = (hscale < vscale) ? hscale : vscale;
+
+ int dpi = static_cast<int>(scale * 72.0);
+ int w1 = static_cast<int>(scale * pageSize.width());
+ int h1 = static_cast<int>(scale * pageSize.height());
+
+ //qDebug() << "hidpi ratio:" << ratio;
+ //qDebug() << "visible width:" << w;
+ //qDebug() << "visible height:" << h;
+ //qDebug() << "doc width:" << pageSize.width();
+ //qDebug() << "doc height:" << pageSize.height();
+ //qDebug() << "scale:" << scale;
+ //qDebug() << "dpi:" << dpi;
+
+ QPixmap pm = QPixmap::fromImage(_page->renderToImage(dpi, dpi, (w1 - w)/2, (h1 - h)/2, w, h));
+ pm.setDevicePixelRatio(ratio);
+ label->setPixmap(pm);
+}
+
+bool PdfDocument::isValid()
+{
+ return (_page != nullptr);
+}
+
+bool PdfDocument::exportImage(QString file, const char *format, QSize outputSize)
+{
+ QImage img = asImage(outputSize);
+ if (!img.isNull()) return img.save(file, format);
+ else return false;
+}
+
+bool PdfDocument::exportPdf(QString file)
+{
+ if (!isValid()) return false;
+ Poppler::PDFConverter *conv = _doc->pdfConverter();
+ conv->setOutputFileName(file);
+ bool success = conv->convert();
+ delete conv;
+ return success;
+}
+
+void PdfDocument::copyImageToClipboard(QSize outputSize)
+{
+ QImage img = asImage(outputSize);
+ if (!img.isNull()) {
+ QApplication::clipboard()->setImage(img, QClipboard::Clipboard);
+ }
+}
+
+QImage PdfDocument::asImage(QSize outputSize)
+{
+ if (!isValid()) return QImage();
+ if (outputSize.isNull()) outputSize = size();
+ QSize pageSize = _page->pageSize();
+ int dpix = (72 * outputSize.width()) / pageSize.width();
+ int dpiy = (72 * outputSize.width()) / pageSize.width();
+ QImage img = _page->renderToImage(dpix, dpiy, 0, 0,
+ outputSize.width(), outputSize.height());
+ return img;
+}
+
+// CRASHES TikZiT when figures contain text, due to limitations of Arthur backend
+//void PdfDocument::exportToSvg(QString file, QSize size) {
+// QSvgGenerator gen;
+// gen.setFileName(file);
+// gen.setSize(size);
+// gen.setViewBox(QRect(0,0,size.width(),size.height()));
+// gen.setDescription("SVG generated from PDF by TikZiT");
+// QPainter painter;
+
+// // set the backend to Qt for renderToPainter() support
+// Poppler::Document::RenderBackend backend = _doc->renderBackend();
+// _doc->setRenderBackend(Poppler::Document::ArthurBackend);
+// painter.begin(&gen);
+// _page->renderToPainter(&painter);
+// painter.end();
+// _doc->setRenderBackend(backend);
+//}
+
+QSize PdfDocument::size()
+{
+ if (isValid()) {
+ return _page->pageSize();
+ } else {
+ return QSize();
+ }
+}
+
+
diff --git a/src/data/pdfdocument.h b/src/data/pdfdocument.h
new file mode 100644
index 0000000..ebd33e9
--- /dev/null
+++ b/src/data/pdfdocument.h
@@ -0,0 +1,28 @@
+#ifndef PDFDOCUMENT_H
+#define PDFDOCUMENT_H
+
+#include <QObject>
+#include <QString>
+#include <QLabel>
+
+#include <poppler/qt5/poppler-qt5.h>
+
+class PdfDocument : public QObject
+{
+ Q_OBJECT
+public:
+ explicit PdfDocument(QString file, QObject *parent = nullptr);
+ void renderTo(QLabel *label, QRect rect);
+ bool isValid();
+// void exportToSvg(QString file, QSize size);
+ bool exportImage(QString file, const char *format, QSize outputSize=QSize());
+ bool exportPdf(QString file);
+ void copyImageToClipboard(QSize outputSize=QSize());
+ QImage asImage(QSize outputSize=QSize());
+ QSize size();
+private:
+ Poppler::Document *_doc;
+ Poppler::Page *_page;
+};
+
+#endif // PDFDOCUMENT_H
diff --git a/src/data/tikzdocument.cpp b/src/data/tikzdocument.cpp
index 24a793b..1099779 100644
--- a/src/data/tikzdocument.cpp
+++ b/src/data/tikzdocument.cpp
@@ -34,16 +34,10 @@ TikzDocument::TikzDocument(QObject *parent) : QObject(parent)
_parseSuccess = true;
_fileName = "";
_shortName = "";
- _undoStack = new QUndoStack();
+ _undoStack = new QUndoStack(this);
_undoStack->setClean();
}
-TikzDocument::~TikzDocument()
-{
- delete _graph;
- delete _undoStack;
-}
-
QUndoStack *TikzDocument::undoStack() const
{
return _undoStack;
@@ -75,15 +69,18 @@ void TikzDocument::open(QString fileName)
return;
}
+ addToRecentFiles();
+
QTextStream in(&file);
_tikz = in.readAll();
file.close();
+ Graph *oldGraph = _graph;
Graph *newGraph = new Graph(this);
TikzAssembler ass(newGraph);
if (ass.parse(_tikz)) {
- delete _graph;
_graph = newGraph;
+ oldGraph->deleteLater();
foreach (Node *n, _graph->nodes()) n->attachStyle();
foreach (Edge *e, _graph->edges()) {
e->attachStyle();
@@ -93,7 +90,7 @@ void TikzDocument::open(QString fileName)
refreshTikz();
setClean();
} else {
- delete newGraph;
+ newGraph->deleteLater();
_parseSuccess = false;
}
}
@@ -103,10 +100,10 @@ bool TikzDocument::save() {
return saveAs();
} else {
MainWindow *win = tikzit->activeWindow();
- if (win != 0 && !win->tikzScene()->enabled()) {
+ if (win != nullptr && !win->tikzScene()->enabled()) {
win->tikzScene()->parseTikz(win->tikzSource());
if (!win->tikzScene()->enabled()) {
- auto resp = QMessageBox::question(0,
+ auto resp = QMessageBox::question(nullptr,
tr("Tikz failed to parse"),
tr("Cannot save file with invalid TiKZ source. Revert changes and save?"));
if (resp == QMessageBox::Yes) win->tikzScene()->setEnabled(true);
@@ -128,7 +125,8 @@ bool TikzDocument::save() {
setClean();
return true;
} else {
- QMessageBox::warning(0, "Save Failed", "Could not open file: '" + _fileName + "' for writing.");
+ QMessageBox::warning(nullptr,
+ "Save Failed", "Could not open file: '" + _fileName + "' for writing.");
}
}
@@ -145,6 +143,34 @@ void TikzDocument::setClean()
_undoStack->setClean();
}
+QString TikzDocument::fileName() const
+{
+ return _fileName;
+}
+
+bool TikzDocument::isEmpty()
+{
+ return _graph->nodes().isEmpty();
+}
+
+void TikzDocument::addToRecentFiles()
+{
+ QSettings settings("tikzit", "tikzit");
+ if (!_fileName.isEmpty()) {
+ QStringList recentFiles = settings.value("recent-files").toStringList();
+
+ // if the file is in the list already, shift it to the top. Otherwise, add it.
+ recentFiles.removeAll(_fileName);
+ recentFiles.prepend(_fileName);
+
+ // keep max 10 files
+ while (recentFiles.size() > 10) recentFiles.removeLast();
+
+ settings.setValue("recent-files", recentFiles);
+ tikzit->updateRecentFiles();
+ }
+}
+
void TikzDocument::setGraph(Graph *graph)
{
_graph = graph;
@@ -153,10 +179,10 @@ void TikzDocument::setGraph(Graph *graph)
bool TikzDocument::saveAs() {
MainWindow *win = tikzit->activeWindow();
- if (win != 0 && !win->tikzScene()->enabled()) {
+ if (win != nullptr && !win->tikzScene()->enabled()) {
win->tikzScene()->parseTikz(win->tikzSource());
if (!win->tikzScene()->enabled()) {
- auto resp = QMessageBox::question(0,
+ auto resp = QMessageBox::question(nullptr,
tr("Tikz failed to parse"),
tr("Cannot save file with invalid TiKZ source. Revert changes and save?"));
if (resp == QMessageBox::Yes) win->tikzScene()->setEnabled(true);
@@ -175,19 +201,13 @@ bool TikzDocument::saveAs() {
dialog.setDirectory(settings.value("previous-file-path").toString());
dialog.setOption(QFileDialog::DontUseNativeDialog);
-// QString fileName = QFileDialog::getSaveFileName(tikzit->activeWindow(),
-// tr("Save File As"),
-// settings.value("previous-file-path").toString(),
-// tr("TiKZ Files (*.tikz)"),
-// nullptr,
-// QFileDialog::DontUseNativeDialog);
-
if (dialog.exec() && !dialog.selectedFiles().isEmpty()) {
QString fileName = dialog.selectedFiles()[0];
_fileName = fileName;
if (save()) {
// clean state might not change, so update title bar manually
tikzit->activeWindow()->updateFileName();
+ addToRecentFiles();
return true;
}
}
diff --git a/src/data/tikzdocument.h b/src/data/tikzdocument.h
index fca5434..ad5499f 100644
--- a/src/data/tikzdocument.h
+++ b/src/data/tikzdocument.h
@@ -34,7 +34,6 @@ class TikzDocument : public QObject
Q_OBJECT
public:
explicit TikzDocument(QObject *parent = 0);
- ~TikzDocument();
Graph *graph() const;
void setGraph(Graph *graph);
@@ -53,6 +52,10 @@ public:
bool isClean() const;
void setClean();
+ QString fileName() const;
+
+ bool isEmpty();
+
private:
Graph *_graph;
QString _tikz;
@@ -60,6 +63,7 @@ private:
QString _shortName;
QUndoStack *_undoStack;
bool _parseSuccess;
+ void addToRecentFiles();
signals:
diff --git a/src/gui/delimitedstringitemdelegate.cpp b/src/gui/delimitedstringitemdelegate.cpp
new file mode 100644
index 0000000..7b6c58e
--- /dev/null
+++ b/src/gui/delimitedstringitemdelegate.cpp
@@ -0,0 +1,44 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "delimitedstringitemdelegate.h"
+
+#include <QLineEdit>
+
+DelimitedStringItemDelegate::DelimitedStringItemDelegate(QObject *parent) : QItemDelegate (parent)
+{
+ _validator = new DelimitedStringValidator(this);
+}
+
+DelimitedStringItemDelegate::~DelimitedStringItemDelegate()
+{
+}
+
+QWidget *DelimitedStringItemDelegate::createEditor(
+ QWidget *parent,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QWidget *editor = QItemDelegate::createEditor(parent, option, index);
+
+ if (QLineEdit *lineEdit = dynamic_cast<QLineEdit*>(editor)) {
+ lineEdit->setValidator(_validator);
+ }
+
+ return editor;
+}
diff --git a/src/gui/delimitedstringitemdelegate.h b/src/gui/delimitedstringitemdelegate.h
new file mode 100644
index 0000000..bd1a856
--- /dev/null
+++ b/src/gui/delimitedstringitemdelegate.h
@@ -0,0 +1,42 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*!
+ * A QItemDelete that attaches a DelimitedStringValidator to any QLineEdit
+ */
+
+#ifndef DELIMITEDSTRINGITEMDELEGATE_H
+#define DELIMITEDSTRINGITEMDELEGATE_H
+
+#include "delimitedstringvalidator.h"
+
+#include <QWidget>
+#include <QItemDelegate>
+
+class DelimitedStringItemDelegate : public QItemDelegate
+{
+ Q_OBJECT
+public:
+ DelimitedStringItemDelegate(QObject *parent=nullptr);
+ virtual ~DelimitedStringItemDelegate() override;
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+private:
+ DelimitedStringValidator *_validator;
+};
+
+#endif // DELIMITEDSTRINGITEMDELEGATE_H
diff --git a/src/gui/exportdialog.cpp b/src/gui/exportdialog.cpp
new file mode 100644
index 0000000..bd1ef53
--- /dev/null
+++ b/src/gui/exportdialog.cpp
@@ -0,0 +1,194 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "exportdialog.h"
+#include "ui_exportdialog.h"
+
+#include "tikzit.h"
+
+#include <QFileDialog>
+#include <QSettings>
+#include <QStandardPaths>
+
+ExportDialog::ExportDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::ExportDialog)
+{
+ QSettings settings("tikzit", "tikzit");
+ ui->setupUi(this);
+
+ QIntValidator *v = new QIntValidator(this);
+ v->setBottom(1);
+ ui->width->setValidator(v);
+ ui->height->setValidator(v);
+ connect(ui->width, SIGNAL(editingFinished()),
+ this, SLOT(setHeightFromWidth()));
+ connect(ui->height, SIGNAL(editingFinished()),
+ this, SLOT(setWidthFromHeight()));
+
+ PdfDocument *doc = tikzit->previewWindow()->doc();
+ if (doc) {
+ QSize size = doc->size() * 4;
+ ui->width->blockSignals(true);
+ ui->height->blockSignals(true);
+ ui->width->setText(QString::number(size.width()));
+ ui->height->setText(QString::number(size.height()));
+ ui->width->blockSignals(false);
+ ui->height->blockSignals(false);
+ }
+
+ if (!settings.value("previous-export-file-format").isNull()) {
+ ui->fileFormat->setCurrentIndex(settings.value("previous-export-file-format").toInt());
+ }
+
+ // set a default export file
+ QString path = (!settings.value("previous-export-file-path").isNull()) ?
+ settings.value("previous-export-file-path").toString() :
+ QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
+
+ QString suffix;
+ switch (ui->fileFormat->currentIndex()) {
+ case PNG: suffix = ".png"; break;
+ case JPG: suffix = ".jpg"; break;
+ case PDF: suffix = ".pdf"; break;
+ }
+
+ QString fileName;
+ int i = 0;
+ bool exists = true;
+ while (exists) {
+ fileName = path + "/tikzit_image" + QString::number(i) + suffix;
+ exists = QFileInfo::exists(fileName);
+ ++i;
+ }
+ ui->filePath->setText(QDir::toNativeSeparators(fileName));
+}
+
+ExportDialog::~ExportDialog()
+{
+ delete ui;
+}
+
+QString ExportDialog::filePath()
+{
+ return ui->filePath->text();
+}
+
+QSize ExportDialog::size()
+{
+ return QSize(ui->width->text().toInt(),
+ ui->height->text().toInt());
+}
+
+ExportDialog::Format ExportDialog::fileFormat()
+{
+ return static_cast<Format>(ui->fileFormat->currentIndex());
+}
+
+void ExportDialog::accept()
+{
+ QSettings settings("tikzit", "tikzit");
+ QFileInfo fi(filePath());
+ settings.setValue("previous-export-file-path", fi.absolutePath());
+ settings.setValue("previous-export-file-format", fileFormat());
+ QDialog::accept();
+}
+
+void ExportDialog::setHeightFromWidth()
+{
+ if (ui->keepAspect->isChecked()) {
+ PdfDocument *doc = tikzit->previewWindow()->doc();
+ if (doc == nullptr || doc->size().width() == 0 || doc->size().height() == 0) return;
+ int w = ui->width->text().toInt();
+ int h = (w * doc->size().height()) / doc->size().width();
+
+ ui->height->blockSignals(true);
+ ui->height->setText(QString::number(h));
+ ui->height->blockSignals(false);
+ }
+}
+
+void ExportDialog::setWidthFromHeight()
+{
+ if (ui->keepAspect->isChecked()) {
+ PdfDocument *doc = tikzit->previewWindow()->doc();
+ if (doc == nullptr || doc->size().width() == 0 || doc->size().height() == 0) return;
+ int h = ui->height->text().toInt();
+ int w = (h * doc->size().width()) / doc->size().height();
+
+ ui->width->blockSignals(true);
+ ui->width->setText(QString::number(w));
+ ui->width->blockSignals(false);
+ }
+}
+
+void ExportDialog::on_keepAspect_stateChanged(int state)
+{
+ if (state == Qt::Checked) setHeightFromWidth();
+}
+
+void ExportDialog::on_browseButton_clicked()
+{
+ QSettings settings("tikzit", "tikzit");
+
+ QString suffix;
+ switch (ui->fileFormat->currentIndex()) {
+ case PNG: suffix = "png"; break;
+ case JPG: suffix = "jpg"; break;
+ case PDF: suffix = "pdf"; break;
+ }
+
+ QFileDialog dialog;
+ dialog.setDefaultSuffix(suffix);
+ dialog.setWindowTitle(tr("Export File Path"));
+ dialog.setAcceptMode(QFileDialog::AcceptSave);
+ dialog.setNameFilter(ui->fileFormat->currentText());
+ dialog.setFileMode(QFileDialog::AnyFile);
+ dialog.setLabelText(QFileDialog::Accept, "Select");
+
+ QFileInfo fi(ui->filePath->text());
+ if (!fi.absolutePath().isEmpty()) {
+ dialog.setDirectory(fi.absolutePath());
+ dialog.selectFile(fi.baseName());
+ }
+
+ dialog.setOption(QFileDialog::DontUseNativeDialog);
+
+ if (dialog.exec()) {
+ ui->filePath->setText(QDir::toNativeSeparators(dialog.selectedFiles()[0]));
+ }
+}
+
+void ExportDialog::on_fileFormat_currentIndexChanged(int f)
+{
+ ui->width->setEnabled(f != PDF);
+ ui->height->setEnabled(f != PDF);
+ ui->keepAspect->setEnabled(f != PDF);
+
+ QString path = ui->filePath->text();
+ if (!path.isEmpty()) {
+ QRegularExpression re("\\.[^.]*$");
+ switch (f) {
+ case PNG: path.replace(re, ".png"); break;
+ case JPG: path.replace(re, ".jpg"); break;
+ case PDF: path.replace(re, ".pdf"); break;
+ }
+
+ ui->filePath->setText(path);
+ }
+}
diff --git a/src/gui/exportdialog.h b/src/gui/exportdialog.h
new file mode 100644
index 0000000..bcb6879
--- /dev/null
+++ b/src/gui/exportdialog.h
@@ -0,0 +1,61 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*!
+ * A dialog for exporting a LaTeX-generated preview to PNG, JPG, or PDF.
+ */
+
+#ifndef EXPORTDIALOG_H
+#define EXPORTDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+class ExportDialog;
+}
+
+class ExportDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ExportDialog(QWidget *parent = nullptr);
+ ~ExportDialog() override;
+ enum Format {
+ PNG = 0,
+ JPG = 1,
+ PDF = 2
+ };
+ QString filePath();
+ QSize size();
+ Format fileFormat();
+public slots:
+ void accept() override;
+
+protected slots:
+ void setHeightFromWidth();
+ void setWidthFromHeight();
+ void on_keepAspect_stateChanged(int state);
+ void on_browseButton_clicked();
+ void on_fileFormat_currentIndexChanged(int f);
+
+private:
+ Ui::ExportDialog *ui;
+};
+
+#endif // EXPORTDIALOG_H
diff --git a/src/gui/exportdialog.ui b/src/gui/exportdialog.ui
new file mode 100644
index 0000000..82cefdc
--- /dev/null
+++ b/src/gui/exportdialog.ui
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ExportDialog</class>
+ <widget class="QDialog" name="ExportDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>394</width>
+ <height>119</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Export Image</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>File</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Format</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="filePath"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="browseButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="fileFormat">
+ <item>
+ <property name="text">
+ <string>Portable Network Graphics (*.png)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Jpeg Image (*.jpg)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Original (*.pdf)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Dimensions</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLineEdit" name="width"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string> X </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="height"/>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="keepAspect">
+ <property name="text">
+ <string>Keep aspect ratio</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ExportDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ExportDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/latexprocess.cpp b/src/gui/latexprocess.cpp
new file mode 100644
index 0000000..59db9ea
--- /dev/null
+++ b/src/gui/latexprocess.cpp
@@ -0,0 +1,169 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "latexprocess.h"
+#include "tikzit.h"
+
+#include <QDebug>
+#include <QStandardPaths>
+#include <QTemporaryDir>
+#include <QStringList>
+#include <QSettings>
+
+LatexProcess::LatexProcess(PreviewWindow *preview, QObject *parent) : QObject(parent)
+{
+ _preview = preview;
+ _output = preview->outputTextEdit();
+
+ _proc = new QProcess(this);
+ _proc->setProcessChannelMode(QProcess::MergedChannels);
+ _proc->setWorkingDirectory(_workingDir.path());
+
+ connect(_proc, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput()));
+ connect(_proc, SIGNAL(finished(int)), this, SLOT(finished(int)));
+
+ // for debug purposes
+ //_workingDir.setAutoRemove(false);
+}
+
+void LatexProcess::makePreview(QString tikz)
+{
+ QSettings settings("tikzit", "tikzit");
+ _preview->setStatus(PreviewWindow::Running);
+ _output->clear();
+
+ if (!_workingDir.isValid()) {
+ _output->appendPlainText("COULD NOT WRITE TO TEMP DIR: " + _workingDir.path() + "\n");
+ return;
+ }
+
+ _output->appendPlainText("USING TEMP DIR: " + _workingDir.path() + "\n");
+
+ QString pdflatex;
+
+ if (settings.value("auto-detect-pdflatex", true).toBool()) {
+ _output->appendPlainText("SEARCHING FOR pdflatex IN:");
+ _output->appendPlainText(qgetenv("PATH"));
+ _output->appendPlainText("\n");
+ pdflatex = QStandardPaths::findExecutable("pdflatex");
+ if (pdflatex.isEmpty()) {
+ // if pdflatex is not in PATH, we are probably on mac or windows, so try common
+ // install directories.
+ _output->appendPlainText("NOT FOUND IN PATH, TRYING:");
+
+ QStringList texDirs;
+ // common macOS tex directories:
+ texDirs << "/Library/TeX/texbin";
+ texDirs << "/usr/texbin";
+ texDirs << "/usr/local/bin";
+ texDirs << "/sw/bin";
+
+ // common windows tex directories
+ texDirs << "C:\\Program Files\\MiKTeX 2.9\\miktex\\bin";
+ texDirs << "C:\\Program Files\\MiKTeX 2.9\\miktex\\bin\\x64";
+ texDirs << "C:\\Program Files\\MiKTeX 2.8\\miktex\\bin";
+ texDirs << "C:\\Program Files\\MiKTeX 2.8\\miktex\\bin\\x64";
+ texDirs << "C:\\Program Files\\MiKTeX 2.7\\miktex\\bin";
+ texDirs << "C:\\Program Files\\MiKTeX 2.7\\miktex\\bin\\x64";
+
+ _output->appendPlainText(texDirs.join(":"));
+ pdflatex = QStandardPaths::findExecutable("pdflatex", texDirs);
+ }
+
+ if (pdflatex.isEmpty()) {
+ _output->appendPlainText("pdflatex NOT FOUND, ABORTING.\n");
+ _preview->setStatus(PreviewWindow::Failed);
+ return;
+ } else {
+ _output->appendPlainText("FOUND: " + pdflatex + "\n");
+ }
+ } else {
+ _output->appendPlainText("USING pdflatex:\n");
+ pdflatex = settings.value("pdflatex-path", "/usr/bin/pdflatex").toString();
+ _output->appendPlainText(pdflatex + "\n");
+ }
+
+ // copy tikzit.sty to preview dir
+ QFile::copy(":/tex/sample/tikzit.sty", _workingDir.path() + "/tikzit.sty");
+
+ // write out the file containing the tikz picture
+ QFile f(_workingDir.path() + "/preview.tex");
+ f.open(QIODevice::WriteOnly);
+ QTextStream tex(&f);
+ tex << "\\documentclass{article}\n";
+ tex << "\\usepackage{tikzit}\n";
+ tex << "\\tikzstyle{every picture}=[tikzfig]\n";
+ tex << "\\usepackage[graphics,active,tightpage]{preview}\n";
+ tex << "\\PreviewEnvironment{tikzpicture}\n";
+
+ // copy active *.tikzstyles file to preview dir
+ if (!tikzit->styleFile().isEmpty() && QFile::exists(tikzit->styleFilePath())) {
+ QFile::copy(tikzit->styleFilePath(), _workingDir.path() + "/" + tikzit->styleFile());
+ tex << "\\input{" + tikzit->styleFile() + "}\n";
+
+ // if there is a *.tikzdefs file with the same basename, copy and include it as well
+ QFileInfo fi(tikzit->styleFilePath());
+ QString defFile = fi.baseName() + ".tikzdefs";
+ QString defFilePath = fi.absolutePath() + "/" + defFile;
+ if (QFile::exists(defFilePath)) {
+ QFile::copy(defFilePath, _workingDir.path() + "/" + defFile);
+ tex << "\\input{" + defFile + "}\n";
+ }
+ }
+
+ tex << "\\begin{document}\n\n";
+ tex << tikz;
+ tex << "\n\n\\end{document}\n";
+
+ f.close();
+ _proc->start(pdflatex,
+ QStringList()
+ << "-interaction=nonstopmode"
+ << "-halt-on-error"
+ << "preview.tex");
+
+}
+
+void LatexProcess::kill()
+{
+ if (_proc->state() == QProcess::Running) _proc->kill();
+}
+
+void LatexProcess::readyReadStandardOutput()
+{
+ QByteArray s = _proc->readAllStandardOutput();
+ _output->appendPlainText(s);
+}
+
+void LatexProcess::finished(int exitCode)
+{
+ QByteArray s = _proc->readAllStandardOutput();
+ _output->appendPlainText(s);
+
+ if (exitCode == 0) {
+ QString pdf = _workingDir.path() + "/preview.pdf";
+ _output->appendPlainText("\n\nSUCCESSFULLY GENERATED: " + pdf + "\n");
+ _preview->setPdf(pdf);
+ _preview->setStatus(PreviewWindow::Success);
+ emit previewFinished();
+ } else {
+ _output->appendPlainText("\n\npdflatex RETURNED AN ERROR\n");
+ _preview->setStatus(PreviewWindow::Failed);
+ emit previewFinished();
+ }
+}
diff --git a/src/gui/latexprocess.h b/src/gui/latexprocess.h
new file mode 100644
index 0000000..9853883
--- /dev/null
+++ b/src/gui/latexprocess.h
@@ -0,0 +1,56 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*!
+ * Run pdflatex and dump its output to the appropriate tab of
+ * the PreviewWindow.
+ */
+
+#ifndef LATEXPROCESS_H
+#define LATEXPROCESS_H
+
+#include "previewwindow.h"
+
+#include <QObject>
+#include <QProcess>
+#include <QTemporaryDir>
+#include <QPlainTextEdit>
+
+class LatexProcess : public QObject
+{
+ Q_OBJECT
+public:
+ explicit LatexProcess(PreviewWindow *preview, QObject *parent = nullptr);
+ void makePreview(QString tikz);
+ void kill();
+
+private:
+ QTemporaryDir _workingDir;
+ PreviewWindow *_preview;
+ QPlainTextEdit *_output;
+ QProcess *_proc;
+
+public slots:
+ void readyReadStandardOutput();
+ void finished(int exitCode);
+
+signals:
+ void previewFinished();
+};
+
+#endif // LATEXPROCESS_H
diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp
index 8166c59..6f4f8db 100644
--- a/src/gui/mainmenu.cpp
+++ b/src/gui/mainmenu.cpp
@@ -17,6 +17,7 @@
*/
#include "mainmenu.h"
+#include "preferencedialog.h"
#include "tikzit.h"
#include <QDebug>
@@ -33,6 +34,8 @@ MainMenu::MainMenu()
ui.actionCheck_for_updates_automatically->setChecked(settings.value("check-for-updates").toBool());
ui.actionCheck_for_updates_automatically->blockSignals(false);
}
+
+ updateRecentFiles();
}
void MainMenu::addDocks(QMenu *m)
@@ -48,6 +51,31 @@ QAction *MainMenu::updatesAction()
return ui.actionCheck_for_updates_automatically;
}
+void MainMenu::updateRecentFiles()
+{
+ QSettings settings("tikzit", "tikzit");
+ ui.menuOpen_Recent->clear();
+
+ QStringList recentFiles = settings.value("recent-files").toStringList();
+ //qDebug() << "update:" << recentFiles;
+
+ QAction *action;
+ foreach (QString f, recentFiles) {
+ QFileInfo fi(f);
+ action = new QAction(fi.fileName(), ui.menuOpen_Recent);
+ action->setData(f);
+ ui.menuOpen_Recent->addAction(action);
+ connect(action, SIGNAL(triggered()),
+ this, SLOT(openRecent()));
+ }
+
+ ui.menuOpen_Recent->addSeparator();
+ action = new QAction("Clear List", ui.menuOpen_Recent);
+ connect(action, SIGNAL(triggered()),
+ tikzit, SLOT(clearRecentFiles()));
+ ui.menuOpen_Recent->addAction(action);
+}
+
// File
void MainMenu::on_actionNew_triggered()
{
@@ -82,6 +110,15 @@ void MainMenu::on_actionExit_triggered()
tikzit->quit();
}
+void MainMenu::openRecent()
+{
+ if (sender() != nullptr) {
+ if (QAction *action = dynamic_cast<QAction*>(sender())) {
+ tikzit->open(action->data().toString());
+ }
+ }
+}
+
// Edit
void MainMenu::on_actionUndo_triggered()
@@ -228,6 +265,18 @@ void MainMenu::on_actionJump_to_Selection_triggered()
}
}
+void MainMenu::on_actionRun_LaTeX_triggered()
+{
+ tikzit->makePreview();
+}
+
+void MainMenu::on_actionPreferences_triggered()
+{
+ PreferenceDialog *d = new PreferenceDialog(this);
+ d->exec();
+ d->deleteLater();
+}
+
// View
void MainMenu::on_actionZoom_In_triggered()
@@ -260,5 +309,5 @@ void MainMenu::on_actionCheck_for_updates_automatically_triggered()
void MainMenu::on_actionCheck_now_triggered()
{
- tikzit->checkForUpdates();
+ tikzit->checkForUpdates(true);
}
diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h
index c14a284..4d672cd 100644
--- a/src/gui/mainmenu.h
+++ b/src/gui/mainmenu.h
@@ -30,6 +30,7 @@ public:
MainMenu();
void addDocks(QMenu *m);
QAction *updatesAction();
+ void updateRecentFiles();
private:
Ui::MainMenu ui;
@@ -43,6 +44,8 @@ public slots:
void on_actionSave_As_triggered();
void on_actionExit_triggered();
+ void openRecent();
+
// Edit
void on_actionUndo_triggered();
void on_actionRedo_triggered();
@@ -63,10 +66,12 @@ public slots:
void on_actionExtendLeft_triggered();
void on_actionExtendRight_triggered();
- // Tikz
+ // Tools
void on_actionParse_triggered();
void on_actionRevert_triggered();
void on_actionJump_to_Selection_triggered();
+ void on_actionRun_LaTeX_triggered();
+ void on_actionPreferences_triggered();
// View
void on_actionZoom_In_triggered();
diff --git a/src/gui/mainmenu.ui b/src/gui/mainmenu.ui
index 0481c1d..08067aa 100644
--- a/src/gui/mainmenu.ui
+++ b/src/gui/mainmenu.ui
@@ -14,8 +14,14 @@
<property name="title">
<string>File</string>
</property>
+ <widget class="QMenu" name="menuOpen_Recent">
+ <property name="title">
+ <string>Open Recent</string>
+ </property>
+ </widget>
<addaction name="actionNew"/>
<addaction name="actionOpen"/>
+ <addaction name="menuOpen_Recent"/>
<addaction name="separator"/>
<addaction name="actionClose"/>
<addaction name="actionSave"/>
@@ -69,11 +75,14 @@
</widget>
<widget class="QMenu" name="menuTikz">
<property name="title">
- <string>Tikz</string>
+ <string>Tools</string>
</property>
<addaction name="actionParse"/>
<addaction name="actionRevert"/>
<addaction name="actionJump_to_Selection"/>
+ <addaction name="actionRun_LaTeX"/>
+ <addaction name="separator"/>
+ <addaction name="actionPreferences"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
@@ -197,7 +206,7 @@
</action>
<action name="actionParse">
<property name="text">
- <string>Parse Tikz</string>
+ <string>Parse TikZ</string>
</property>
<property name="shortcut">
<string>Ctrl+T</string>
@@ -226,7 +235,10 @@
</action>
<action name="actionRevert">
<property name="text">
- <string>Revert Tikz</string>
+ <string>Revert TikZ</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Alt+T</string>
</property>
</action>
<action name="actionJump_to_Selection">
@@ -335,6 +347,24 @@
<string>About</string>
</property>
</action>
+ <action name="actionRun_LaTeX">
+ <property name="text">
+ <string>Make Preview</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+R</string>
+ </property>
+ </action>
+ <action name="actionClear_Menu">
+ <property name="text">
+ <string>Clear Menu</string>
+ </property>
+ </action>
+ <action name="actionPreferences">
+ <property name="text">
+ <string>Preferences...</string>
+ </property>
+ </action>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
diff --git a/src/gui/preferencedialog.cpp b/src/gui/preferencedialog.cpp
new file mode 100644
index 0000000..06159af
--- /dev/null
+++ b/src/gui/preferencedialog.cpp
@@ -0,0 +1,111 @@
+#include "preferencedialog.h"
+#include "ui_preferencedialog.h"
+
+#include <QColorDialog>
+#include <QFileDialog>
+#include <QSettings>
+
+PreferenceDialog::PreferenceDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::PreferenceDialog)
+{
+ ui->setupUi(this);
+ QSettings settings("tikzit", "tikzit");
+ ui->autoPdflatex->setChecked(true);
+
+ if (!settings.value("auto-detect-pdflatex").isNull())
+ ui->autoPdflatex->setChecked(settings.value("auto-detect-pdflatex").toBool());
+ if (!settings.value("pdflatex-path").isNull())
+ ui->pdflatexPath->setText(settings.value("pdflatex-path").toString());
+
+
+ setColor(ui->axesColor, settings.value("grid-color-axes",
+ QColor(220,220,240)).value<QColor>());
+ setColor(ui->majorColor, settings.value("grid-color-major",
+ QColor(240,240,250)).value<QColor>());
+ setColor(ui->minorColor, settings.value("grid-color-minor",
+ QColor(250,250,255)).value<QColor>());
+
+
+ connect(ui->axesColor, SIGNAL(clicked()), this, SLOT(colorClick()));
+ connect(ui->majorColor, SIGNAL(clicked()), this, SLOT(colorClick()));
+ connect(ui->minorColor, SIGNAL(clicked()), this, SLOT(colorClick()));
+}
+
+PreferenceDialog::~PreferenceDialog()
+{
+ delete ui;
+}
+
+void PreferenceDialog::accept()
+{
+ QSettings settings("tikzit", "tikzit");
+ settings.setValue("auto-detect-pdflatex", ui->autoPdflatex->isChecked());
+ settings.setValue("pdflatex-path", ui->pdflatexPath->text());
+ settings.setValue("grid-color-axes", color(ui->axesColor));
+ settings.setValue("grid-color-major", color(ui->majorColor));
+ settings.setValue("grid-color-minor", color(ui->minorColor));
+ QDialog::accept();
+}
+
+void PreferenceDialog::on_resetColors_clicked()
+{
+ setColor(ui->axesColor, QColor(220,220,240));
+ setColor(ui->majorColor, QColor(240,240,250));
+ setColor(ui->minorColor, QColor(250,250,255));
+}
+
+void PreferenceDialog::colorClick()
+{
+ if (QPushButton *btn = dynamic_cast<QPushButton*>(sender())) {
+ QColor col = QColorDialog::getColor(
+ color(btn),
+ this,
+ "Set color",
+ QColorDialog::DontUseNativeDialog);
+ if (col.isValid()) setColor(btn, col);
+ }
+}
+
+void PreferenceDialog::on_autoPdflatex_stateChanged(int state)
+{
+ ui->pdflatexPath->setEnabled(state != Qt::Checked);
+ ui->browsePdflatex->setEnabled(state != Qt::Checked);
+}
+
+void PreferenceDialog::on_browsePdflatex_clicked()
+{
+ QSettings settings("tikzit", "tikzit");
+
+ QFileDialog dialog;
+ dialog.setWindowTitle(tr("pdflatex Path"));
+ dialog.setAcceptMode(QFileDialog::AcceptOpen);
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ dialog.setLabelText(QFileDialog::Accept, "Select");
+
+ QFileInfo fi(ui->pdflatexPath->text());
+ if (!fi.absolutePath().isEmpty()) {
+ dialog.setDirectory(fi.absolutePath());
+ dialog.selectFile(fi.baseName());
+ }
+
+ dialog.setOption(QFileDialog::DontUseNativeDialog);
+
+ if (dialog.exec()) {
+ ui->pdflatexPath->setText(QDir::toNativeSeparators(dialog.selectedFiles()[0]));
+ }
+}
+
+void PreferenceDialog::setColor(QPushButton *btn, QColor col)
+{
+ QPalette pal = btn->palette();
+ pal.setColor(QPalette::Button, col);
+ btn->setPalette(pal);
+ btn->update();
+}
+
+QColor PreferenceDialog::color(QPushButton *btn)
+{
+ QPalette pal = btn->palette();
+ return pal.color(QPalette::Button);
+}
diff --git a/src/gui/preferencedialog.h b/src/gui/preferencedialog.h
new file mode 100644
index 0000000..9da8ae6
--- /dev/null
+++ b/src/gui/preferencedialog.h
@@ -0,0 +1,31 @@
+#ifndef PREFERENCEDIALOG_H
+#define PREFERENCEDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+class PreferenceDialog;
+}
+
+class PreferenceDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PreferenceDialog(QWidget *parent = nullptr);
+ ~PreferenceDialog() override;
+
+protected slots:
+ void accept() override;
+ void colorClick();
+ void on_resetColors_clicked();
+ void on_autoPdflatex_stateChanged(int state);
+ void on_browsePdflatex_clicked();
+
+private:
+ Ui::PreferenceDialog *ui;
+ QColor color(QPushButton *btn);
+ void setColor(QPushButton *btn, QColor col);
+};
+
+#endif // PREFERENCEDIALOG_H
diff --git a/src/gui/preferencedialog.ui b/src/gui/preferencedialog.ui
new file mode 100644
index 0000000..9a32e7d
--- /dev/null
+++ b/src/gui/preferencedialog.ui
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PreferenceDialog</class>
+ <widget class="QDialog" name="PreferenceDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>345</width>
+ <height>176</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>pdflatex Location</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="pdflatexPath"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="browsePdflatex">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="autoPdflatex">
+ <property name="text">
+ <string>Automatically detect pdflatex</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Grid colors</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="axesColor">
+ <property name="autoFillBackground">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Axes</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame_2">
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="majorColor">
+ <property name="autoFillBackground">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Major</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame_3">
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="minorColor">
+ <property name="autoFillBackground">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Minor</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
+ <widget class="QPushButton" name="resetColors">
+ <property name="text">
+ <string>Reset colors</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>PreferenceDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>PreferenceDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/previewwindow.cpp b/src/gui/previewwindow.cpp
new file mode 100644
index 0000000..2f47efd
--- /dev/null
+++ b/src/gui/previewwindow.cpp
@@ -0,0 +1,208 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "previewwindow.h"
+#include "ui_previewwindow.h"
+
+#include "tikzit.h"
+#include "latexprocess.h"
+#include "exportdialog.h"
+
+#include <QLabel>
+#include <QImage>
+#include <QPixmap>
+#include <QDebug>
+#include <QSettings>
+#include <QTemporaryDir>
+#include <QFile>
+#include <QTextStream>
+#include <QStandardPaths>
+#include <QMessageBox>
+#include <cmath>
+#include <QMovie>
+#include <QAction>
+
+PreviewWindow::PreviewWindow(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::PreviewWindow)
+{
+ QSettings settings("tikzit", "tikzit");
+ ui->setupUi(this);
+
+ QVariant geom = settings.value("geometry-preview");
+
+ if (geom.isValid()) {
+ restoreGeometry(geom.toByteArray());
+ }
+
+ _doc = nullptr;
+
+ _loader = new QLabel(ui->tabWidget->tabBar());
+ _loader->setMinimumSize(QSize(16,16));
+ _loader->setMaximumSize(QSize(16,16));
+ _loader->setAutoFillBackground(false);
+ ui->tabWidget->tabBar()->setTabButton(1, QTabBar::RightSide, _loader);
+
+ connect(ui->tabWidget, SIGNAL(currentChanged(int)),
+ this, SLOT(render()));
+
+ render();
+}
+
+void PreviewWindow::contextMenuEvent(QContextMenuEvent *event)
+{
+ QMenu menu(this);
+ QAction *act;
+
+ act = new QAction("Export Image...");
+ connect(act, SIGNAL(triggered()), this, SLOT(exportImage()));
+ menu.addAction(act);
+
+ act = new QAction("Copy to Clipboard");
+ connect(act, SIGNAL(triggered()), this, SLOT(copyImageToClipboard()));
+ menu.addAction(act);
+
+ menu.exec(event->globalPos());
+}
+
+PdfDocument *PreviewWindow::doc() const
+{
+ return _doc;
+}
+
+PreviewWindow::~PreviewWindow()
+{
+ delete ui;
+}
+
+void PreviewWindow::setPdf(QString file)
+{
+ // use loadFromData to avoid holding a lock on the PDF file in windows
+ //QFile f(file);
+ //f.open(QFile::ReadOnly);
+ //QByteArray data = f.readAll();
+ //f.close();
+ PdfDocument *newDoc = new PdfDocument(file, this);
+
+ if (newDoc->isValid()) {
+ PdfDocument *oldDoc = _doc;
+ _doc = newDoc;
+ if (oldDoc != nullptr) delete oldDoc;
+ render();
+ } else {
+ QMessageBox::warning(nullptr,
+ "Could not read PDF",
+ "Could not read: '" + file + "'.");
+ delete newDoc;
+ }
+}
+
+QPlainTextEdit *PreviewWindow::outputTextEdit()
+{
+ return ui->output;
+}
+
+void PreviewWindow::setStatus(PreviewWindow::Status status)
+{
+ QMovie *oldMovie = _loader->movie();
+ if (status == PreviewWindow::Running) {
+ // loader.gif and loader@2x.gif derived from:
+ // https://commons.wikimedia.org/wiki/Throbbers#/media/File:Linux_Ubuntu_Loader.gif
+ // licensed GNU Free Documentation License v1.2
+ QMovie *movie = new QMovie(
+ (devicePixelRatioF() > 1.0) ? ":images/loader@2x.gif" : ":images/loader.gif",
+ QByteArray(), _loader);
+ _loader->setPixmap(QPixmap());
+ _loader->setMovie(movie);
+ movie->start();
+ } else if (status == PreviewWindow::Success) {
+ _loader->setMovie(nullptr);
+ QIcon accept(":images/dialog-accept.svg");
+ //accept.setDevicePixelRatio(devicePixelRatio());
+ _loader->setPixmap(accept.pixmap(QSize(16,16)));
+ } else if (status == PreviewWindow::Failed) {
+ _loader->setMovie(nullptr);
+ QIcon error(":images/dialog-error.svg");
+ //error.setDevicePixelRatio(devicePixelRatio());
+ _loader->setPixmap(error.pixmap(QSize(16,16)));
+ }
+
+ if (oldMovie != nullptr) oldMovie->deleteLater();
+
+
+ _loader->repaint();
+}
+
+void PreviewWindow::closeEvent(QCloseEvent *e) {
+ QSettings settings("tikzit", "tikzit");
+ settings.setValue("geometry-preview", saveGeometry());
+ QDialog::closeEvent(e);
+}
+
+void PreviewWindow::resizeEvent(QResizeEvent *e) {
+ render();
+ QDialog::resizeEvent(e);
+}
+
+void PreviewWindow::showEvent(QShowEvent *e) {
+ render();
+ QDialog::showEvent(e);
+}
+
+void PreviewWindow::render() {
+ if (_doc != nullptr) {
+ _doc->renderTo(ui->pdf,
+ ui->scrollArea->visibleRegion().boundingRect());
+ ui->pdf->repaint();
+ }
+}
+
+void PreviewWindow::exportImage()
+{
+ QSettings settings("tikzit", "tikzit");
+ if (_doc == nullptr) return;
+ ExportDialog *d = new ExportDialog(this);
+ int ret = d->exec();
+ if (ret == QDialog::Accepted) {
+ bool success;
+ if (d->fileFormat() == ExportDialog::PDF) {
+ success = _doc->exportPdf(d->filePath());
+ } else {
+ success = _doc->exportImage(
+ d->filePath(),
+ (d->fileFormat() == ExportDialog::PNG) ? "PNG" : "JPG",
+ d->size());
+ }
+
+ if (!success) {
+ QMessageBox::warning(this,
+ "Error",
+ "Could not write to: '" + d->filePath() +
+ "'. Check file permissions or choose a new location.");
+ }
+ }
+}
+
+void PreviewWindow::copyImageToClipboard()
+{
+ if (_doc != nullptr) {
+ _doc->copyImageToClipboard(_doc->size() * 4);
+ }
+}
+
+
diff --git a/src/gui/previewwindow.h b/src/gui/previewwindow.h
new file mode 100644
index 0000000..a14303b
--- /dev/null
+++ b/src/gui/previewwindow.h
@@ -0,0 +1,72 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*!
+ * Displays a LaTeX-generated PDF preview using Poppler. The right-click
+ * menu has options for exporting to file or clipboard.
+ */
+
+#ifndef PREVIEWWINDOW_H
+#define PREVIEWWINDOW_H
+
+#include "pdfdocument.h"
+
+#include <QDialog>
+#include <QLabel>
+#include <QPlainTextEdit>
+#include <QContextMenuEvent>
+#include <poppler/qt5/poppler-qt5.h>
+
+namespace Ui {
+class PreviewWindow;
+}
+
+class PreviewWindow : public QDialog
+{
+ Q_OBJECT
+
+public:
+ enum Status {
+ Running, Success, Failed
+ };
+ explicit PreviewWindow(QWidget *parent = nullptr);
+ ~PreviewWindow() override;
+ void setPdf(QString file);
+ QString preparePreview(QString tikz);
+ QPlainTextEdit *outputTextEdit();
+ void setStatus(Status status);
+
+ PdfDocument *doc() const;
+
+public slots:
+ void render();
+ void exportImage();
+ void copyImageToClipboard();
+
+protected:
+ void resizeEvent(QResizeEvent *e) override;
+ void showEvent(QShowEvent *e) override;
+ void closeEvent(QCloseEvent *e) override;
+ void contextMenuEvent(QContextMenuEvent *event) override;
+private:
+ Ui::PreviewWindow *ui;
+ PdfDocument *_doc;
+ QLabel *_loader;
+};
+
+#endif // PREVIEWWINDOW_H
diff --git a/src/gui/previewwindow.ui b/src/gui/previewwindow.ui
new file mode 100644
index 0000000..aa49980
--- /dev/null
+++ b/src/gui/previewwindow.ui
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PreviewWindow</class>
+ <widget class="QDialog" name="PreviewWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>603</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Preview</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="documentMode">
+ <bool>true</bool>
+ </property>
+ <property name="tabsClosable">
+ <bool>false</bool>
+ </property>
+ <widget class="QWidget" name="pdfTab">
+ <attribute name="title">
+ <string>PDF</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QScrollArea" name="scrollArea">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>595</width>
+ <height>451</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QLabel" name="pdf">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="outputTab">
+ <attribute name="title">
+ <string>Output</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QPlainTextEdit" name="output"/>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/styleeditor.cpp b/src/gui/styleeditor.cpp
index 29192d6..e2ade45 100644
--- a/src/gui/styleeditor.cpp
+++ b/src/gui/styleeditor.cpp
@@ -1,10 +1,30 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
#include <QColorDialog>
#include <QDebug>
#include <QMessageBox>
#include "tikzit.h"
#include "styleeditor.h"
+#include "delimitedstringvalidator.h"
#include "ui_styleeditor.h"
+#include "delimitedstringitemdelegate.h"
StyleEditor::StyleEditor(QWidget *parent) :
QMainWindow(parent),
@@ -18,6 +38,14 @@ StyleEditor::StyleEditor(QWidget *parent) :
ui->leftArrow << ui->rightArrow <<
ui->properties;
+ DelimitedStringValidator *v = new DelimitedStringValidator(this);
+ ui->name->setValidator(v);
+ ui->category->lineEdit()->setValidator(v);
+ ui->shape->lineEdit()->setValidator(v);
+
+ DelimitedStringItemDelegate *delegate = new DelimitedStringItemDelegate(ui->properties);
+ ui->properties->setItemDelegate(delegate);
+
setWindowIcon(QIcon(":/images/tikzit.png"));
_styles = nullptr;
_activeStyle = nullptr;
@@ -44,40 +72,6 @@ StyleEditor::StyleEditor(QWidget *parent) :
SIGNAL(currentIndexChanged(int)),
this, SLOT(shapeChanged()));
- // setup the color dialog to display only the named colors that tikzit/xcolor knows
- // about as "standard colors".
- for (int i = 0; i < 48; ++i) {
- QColorDialog::setStandardColor(i, QColor(Qt::white));
- }
-
- // grayscale in column 1
- int pos = 0;
- for (int i=0; i < 5; ++i) {
- QColorDialog::setStandardColor(pos, tikzit->colorByIndex(i));
- pos += 1;
- }
-
- // rainbow in column 2
- pos = 6;
- for (int i=5; i < 11; ++i) {
- QColorDialog::setStandardColor(pos, tikzit->colorByIndex(i));
- pos += 1;
- }
-
- // brown/green/teal spectrum in column 3
- pos = 12;
- for (int i=11; i < 16; ++i) {
- QColorDialog::setStandardColor(pos, tikzit->colorByIndex(i));
- pos += 1;
- }
-
- // pinks in column 4
- pos = 18;
- for (int i=16; i < 19; ++i) {
- QColorDialog::setStandardColor(pos, tikzit->colorByIndex(i));
- pos += 1;
- }
-
refreshDisplay();
}
diff --git a/src/gui/styleeditor.h b/src/gui/styleeditor.h
index 4bae7db..2c35d56 100644
--- a/src/gui/styleeditor.h
+++ b/src/gui/styleeditor.h
@@ -1,3 +1,25 @@
+/*
+ TikZiT - a GUI diagram editor for TikZ
+ Copyright (C) 2018 Aleks Kissinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*!
+ * A GUI editor for .tikzstyles files.
+ */
+
#ifndef STYLEEDITOR_H
#define STYLEEDITOR_H
@@ -17,8 +39,8 @@ class StyleEditor : public QMainWindow
Q_OBJECT
public:
- explicit StyleEditor(QWidget *parent = 0);
- ~StyleEditor();
+ explicit StyleEditor(QWidget *parent = nullptr);
+ ~StyleEditor() override;
void open();
void save();
diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp
index c061221..31d5bf6 100644
--- a/src/gui/tikzscene.cpp
+++ b/src/gui/tikzscene.cpp
@@ -28,14 +28,15 @@
#include <QClipboard>
#include <QInputDialog>
#include <cmath>
+#include <delimitedstringvalidator.h>
TikzScene::TikzScene(TikzDocument *tikzDocument, ToolPalette *tools,
StylePalette *styles, QObject *parent) :
QGraphicsScene(parent), _tikzDocument(tikzDocument), _tools(tools), _styles(styles)
{
- _modifyEdgeItem = 0;
- _edgeStartNodeItem = 0;
+ _modifyEdgeItem = nullptr;
+ _edgeStartNodeItem = nullptr;
_drawEdgeItem = new QGraphicsLineItem();
_rubberBandItem = new QGraphicsRectItem();
_enabled = true;
@@ -43,7 +44,7 @@ TikzScene::TikzScene(TikzDocument *tikzDocument, ToolPalette *tools,
setSceneRect(-1000,-1000,2000,2000);
QPen pen;
- pen.setColor(QColor::fromRgbF(0.5f, 0.0f, 0.5f));
+ pen.setColor(QColor::fromRgbF(0.5, 0.0, 0.5));
//pen.setWidth(3.0f);
pen.setCosmetic(true);
_drawEdgeItem->setPen(pen);
@@ -51,7 +52,7 @@ TikzScene::TikzScene(TikzDocument *tikzDocument, ToolPalette *tools,
_drawEdgeItem->setVisible(false);
addItem(_drawEdgeItem);
- pen.setColor(QColor::fromRgbF(0.6f, 0.6f, 0.8f));
+ pen.setColor(QColor::fromRgbF(0.6, 0.6, 0.8));
//pen.setWidth(3.0f);
//QVector<qreal> dash;
//dash << 4.0 << 4.0;
@@ -110,7 +111,7 @@ void TikzScene::graphReplaced()
void TikzScene::extendSelectionUp()
{
bool found = false;
- float m = 0.0f;
+ qreal m = 0.0;
foreach (Node *n, getSelectedNodes()) {
if (!found) {
m = n->point().y();
@@ -128,7 +129,7 @@ void TikzScene::extendSelectionUp()
void TikzScene::extendSelectionDown()
{
bool found = false;
- float m = 0.0f;
+ qreal m = 0.0;
foreach (Node *n, getSelectedNodes()) {
if (!found) {
m = n->point().y();
@@ -146,7 +147,7 @@ void TikzScene::extendSelectionDown()
void TikzScene::extendSelectionLeft()
{
bool found = false;
- float m = 0.0f;
+ qreal m = 0.0;
foreach (Node *n, getSelectedNodes()) {
if (!found) {
m = n->point().x();
@@ -164,7 +165,7 @@ void TikzScene::extendSelectionLeft()
void TikzScene::extendSelectionRight()
{
bool found = false;
- float m = 0.0f;
+ qreal m = 0.0;
foreach (Node *n, getSelectedNodes()) {
if (!found) {
m = n->point().x();
@@ -266,7 +267,7 @@ void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
}
}
- if (_modifyEdgeItem != 0) {
+ if (_modifyEdgeItem != nullptr) {
// store for undo purposes
Edge *e = _modifyEdgeItem->edge();
_oldBend = e->bend();
@@ -337,15 +338,15 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
switch (_tools->currentTool()) {
case ToolPalette::SELECT:
- if (_modifyEdgeItem != 0) {
+ if (_modifyEdgeItem != nullptr) {
Edge *e = _modifyEdgeItem->edge();
// dragging a control point
QPointF src = toScreen(e->source()->point());
QPointF targ = toScreen(e->target()->point());
- float dx1 = targ.x() - src.x();
- float dy1 = targ.y() - src.y();
- float dx2, dy2;
+ qreal dx1 = targ.x() - src.x();
+ qreal dy1 = targ.y() - src.y();
+ qreal dx2, dy2;
if (_firstControlPoint) {
dx2 = mousePos.x() - src.x();
dy2 = mousePos.y() - src.y();
@@ -354,25 +355,26 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
dy2 = mousePos.y() - targ.y();
}
- float baseDist = sqrt(dx1*dx1 + dy1*dy1);
- float handleDist = sqrt(dx2*dx2 + dy2*dy2);
- float wcoarseness = 0.1f;
+ qreal baseDist = sqrt(dx1*dx1 + dy1*dy1);
+ qreal handleDist = sqrt(dx2*dx2 + dy2*dy2);
+ qreal wcoarseness = 0.1;
if (!e->isSelfLoop()) {
- if (baseDist != 0) {
+ if (baseDist != 0.0) {
e->setWeight(roundToNearest(wcoarseness, handleDist/baseDist));
} else {
e->setWeight(roundToNearest(wcoarseness, handleDist/GLOBAL_SCALEF));
}
}
- float control_angle = atan2(-dy2, dx2);
+ qreal control_angle = atan2(-dy2, dx2);
int bcoarseness = 15;
+ qreal bcoarsenessi = 1.0/15.0;
if(e->basicBendMode()) {
- float bnd;
- float base_angle = atan2(-dy1, dx1);
+ qreal bnd;
+ qreal base_angle = atan2(-dy1, dx1);
if (_firstControlPoint) {
bnd = base_angle - control_angle;
} else {
@@ -380,12 +382,10 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (bnd > M_PI) bnd -= 2*M_PI;
}
- e->setBend(round(bnd * (180.0f / M_PI) * (1.0f / (float)bcoarseness)) * bcoarseness);
+ e->setBend(static_cast<int>(round(bnd * (180.0 / M_PI) * bcoarsenessi)) * bcoarseness);
} else {
- int bnd = round(control_angle * (180.0f / M_PI) *
- (1.0f / (float)bcoarseness)) *
- bcoarseness;
+ int bnd = static_cast<int>(round(control_angle * (180.0 / M_PI) * bcoarsenessi)) * bcoarseness;
if (_firstControlPoint) {
// TODO: enable moving both control points
// if ([theEvent modifierFlags] & NSAlternateKeyMask) {
@@ -428,7 +428,7 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
NodeItem *ni = _nodeItems[n];
// in (rare) cases, the graph can change while we are dragging
- if (ni != 0) {
+ if (ni != nullptr) {
ni->setPos(toScreen(_oldNodePositions[n]) + shift);
ni->writePos();
}
@@ -454,7 +454,7 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
break;
case ToolPalette::EDGE:
if (_drawEdgeItem->isVisible()) {
- _edgeEndNodeItem = 0;
+ _edgeEndNodeItem = nullptr;
foreach (QGraphicsItem *gi, items(mousePos)) {
if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)){
_edgeEndNodeItem = ni;
@@ -462,7 +462,7 @@ void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
}
}
QPointF p1 = _drawEdgeItem->line().p1();
- QPointF p2 = (_edgeEndNodeItem != 0) ? toScreen(_edgeEndNodeItem->node()->point()) : mousePos;
+ QPointF p2 = (_edgeEndNodeItem != nullptr) ? toScreen(_edgeEndNodeItem->node()->point()) : mousePos;
QLineF line(p1, p2);
_drawEdgeItem->setLine(line);
@@ -482,11 +482,11 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
switch (_tools->currentTool()) {
case ToolPalette::SELECT:
- if (_modifyEdgeItem != 0) {
+ if (_modifyEdgeItem != nullptr) {
// finished dragging a control point
Edge *e = _modifyEdgeItem->edge();
- if (_oldWeight != e->weight() ||
+ if (!almostEqual(_oldWeight, e->weight()) ||
_oldBend != e->bend() ||
_oldInAngle != e->inAngle() ||
_oldOutAngle != e->outAngle())
@@ -495,7 +495,7 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
_tikzDocument->undoStack()->push(cmd);
}
- _modifyEdgeItem = 0;
+ _modifyEdgeItem = nullptr;
} else {
// otherwise, process mouse move normally
QGraphicsScene::mouseReleaseEvent(event);
@@ -517,7 +517,7 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
QPointF shift = mousePos - _mouseDownPos;
shift = QPointF(round(shift.x()/GRID_SEP)*GRID_SEP, round(shift.y()/GRID_SEP)*GRID_SEP);
- if (shift.x() != 0 || shift.y() != 0) {
+ if (shift.x() != 0.0 || shift.y() != 0.0) {
QMap<Node*,QPointF> newNodePositions;
foreach (QGraphicsItem *gi, selectedItems()) {
@@ -557,14 +557,14 @@ void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
break;
case ToolPalette::EDGE:
// add an edge
- if (_edgeStartNodeItem != 0 && _edgeEndNodeItem != 0) {
+ if (_edgeStartNodeItem != nullptr && _edgeEndNodeItem != nullptr) {
Edge *e = new Edge(_edgeStartNodeItem->node(), _edgeEndNodeItem->node(), _tikzDocument);
e->setStyleName(_styles->activeEdgeStyleName());
AddEdgeCommand *cmd = new AddEdgeCommand(this, e);
_tikzDocument->undoStack()->push(cmd);
}
- _edgeStartNodeItem = 0;
- _edgeEndNodeItem = 0;
+ _edgeStartNodeItem = nullptr;
+ _edgeEndNodeItem = nullptr;
_drawEdgeItem->setVisible(false);
break;
case ToolPalette::CROP:
@@ -613,19 +613,19 @@ void TikzScene::keyPressEvent(QKeyEvent *event)
if (event->modifiers() & Qt::ControlModifier) {
QPointF delta(0,0);
- float shift = (event->modifiers() & Qt::ShiftModifier) ? 1.0f : 10.0f;
+ qreal shift = (event->modifiers() & Qt::ShiftModifier) ? 1.0 : 10.0;
switch(event->key()) {
case Qt::Key_Left:
- delta.setX(-0.025f * shift);
+ delta.setX(-0.025 * shift);
break;
case Qt::Key_Right:
- delta.setX(0.025f * shift);
+ delta.setX(0.025 * shift);
break;
case Qt::Key_Up:
- delta.setY(0.025f * shift);
+ delta.setY(0.025 * shift);
break;
case Qt::Key_Down:
- delta.setY(-0.025f * shift);
+ delta.setY(-0.025 * shift);
break;
}
@@ -665,16 +665,23 @@ void TikzScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
}
break;
} else if (NodeItem *ni = dynamic_cast<NodeItem*>(it)) {
- bool ok;
- QString newLabel = QInputDialog::getText(views()[0], tr("Node label"),
- tr("Label:"), QLineEdit::Normal,
- ni->node()->label(), &ok);
- if (ok) {
+ QInputDialog *d = new QInputDialog(views()[0]);
+ d->setLabelText(tr("Label:"));
+ d->setTextValue(ni->node()->label());
+ d->setWindowTitle(tr("Node label"));
+
+ if (QLineEdit *le = d->findChild<QLineEdit*>()) {
+ le->setValidator(new DelimitedStringValidator(le));
+ }
+
+ if (d->exec()) {
QMap<Node*,QString> oldLabels;
oldLabels.insert(ni->node(), ni->node()->label());
- ChangeLabelCommand *cmd = new ChangeLabelCommand(this, oldLabels, newLabel);
+ ChangeLabelCommand *cmd = new ChangeLabelCommand(this, oldLabels, d->textValue());
_tikzDocument->undoStack()->push(cmd);
}
+
+ d->deleteLater();
break;
}
}
@@ -767,7 +774,7 @@ void TikzScene::pasteFromClipboard()
QRectF srcRect = g->realBbox();
QRectF tgtRect = graph()->realBbox();
- QPointF shift(tgtRect.right() - srcRect.left(), 0.0f);
+ QPointF shift(tgtRect.right() - srcRect.left(), 0.0);
if (shift.x() > 0) {
foreach (Node *n, g->nodes()) {
@@ -890,7 +897,7 @@ void TikzScene::refreshAdjacentEdges(QList<Node*> nodes)
EdgeItem *ei = _edgeItems[e];
// the list "nodes" can be out of date, e.g. if the graph changes while dragging
- if (ei != 0) {
+ if (ei != nullptr) {
if (nodes.contains(ei->edge()->source()) || nodes.contains(ei->edge()->target())) {
ei->edge()->updateControls();
ei->readPos();
diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h
index 2a3e988..e8ea2c6 100644
--- a/src/gui/tikzscene.h
+++ b/src/gui/tikzscene.h
@@ -44,7 +44,7 @@ class TikzScene : public QGraphicsScene
Q_OBJECT
public:
TikzScene(TikzDocument *tikzDocument, ToolPalette *tools, StylePalette *styles, QObject *parent);
- ~TikzScene();
+ ~TikzScene() override;
Graph *graph();
QMap<Node*,NodeItem*> &nodeItems();
QMap<Edge*,EdgeItem*> &edgeItems();
@@ -108,7 +108,7 @@ private:
bool _draggingNodes;
QMap<Node*,QPointF> _oldNodePositions;
- float _oldWeight;
+ qreal _oldWeight;
int _oldBend;
int _oldInAngle;
int _oldOutAngle;
diff --git a/src/gui/tikzview.cpp b/src/gui/tikzview.cpp
index 52a32cf..5b0f09c 100644
--- a/src/gui/tikzview.cpp
+++ b/src/gui/tikzview.cpp
@@ -21,6 +21,7 @@
#include <QDebug>
#include <QScrollBar>
+#include <QSettings>
TikzView::TikzView(QWidget *parent) : QGraphicsView(parent)
{
@@ -53,6 +54,7 @@ void TikzView::setScene(QGraphicsScene *scene)
void TikzView::drawBackground(QPainter *painter, const QRectF &rect)
{
+ QSettings settings("tikzit", "tikzit");
QGraphicsView::drawBackground(painter, rect);
// draw a gray background if disabled
TikzScene *sc = static_cast<TikzScene*>(scene());
@@ -63,13 +65,13 @@ void TikzView::drawBackground(QPainter *painter, const QRectF &rect)
QPen pen1;
//pen1.setWidthF(0.5);
pen1.setCosmetic(true);
- pen1.setColor(QColor(250,250,255));
+ pen1.setColor(settings.value("grid-color-minor", QColor(250,250,255)).value<QColor>());
QPen pen2 = pen1;
- pen2.setColor(QColor(240,240,250));
+ pen2.setColor(settings.value("grid-color-major", QColor(240,240,250)).value<QColor>());
QPen pen3 = pen1;
- pen3.setColor(QColor(220,220,240));
+ pen3.setColor(settings.value("grid-color-axes", QColor(220,220,240)).value<QColor>());
painter->setPen(pen1);
diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp
index f713582..8a00536 100644
--- a/src/gui/undocommands.cpp
+++ b/src/gui/undocommands.cpp
@@ -80,7 +80,7 @@ void MoveCommand::redo()
}
EdgeBendCommand::EdgeBendCommand(TikzScene *scene, Edge *edge,
- float oldWeight, int oldBend,
+ qreal oldWeight, int oldBend,
int oldInAngle, int oldOutAngle, QUndoCommand *parent) :
GraphUpdateCommand(scene, parent),
_edge(edge),
@@ -405,7 +405,7 @@ void ChangeLabelCommand::undo()
foreach (Node *n, _oldLabels.keys()) {
n->setLabel(_oldLabels[n]);
NodeItem *ni = _scene->nodeItems()[n];
- if (ni != 0) ni->updateBounds();
+ if (ni != nullptr) ni->updateBounds();
}
GraphUpdateCommand::undo();
@@ -416,7 +416,7 @@ void ChangeLabelCommand::redo()
foreach (Node *n, _oldLabels.keys()) {
n->setLabel(_newLabel);
NodeItem *ni = _scene->nodeItems()[n];
- if (ni != 0) ni->updateBounds();
+ if (ni != nullptr) ni->updateBounds();
}
GraphUpdateCommand::redo();
diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h
index dc60549..ff51c90 100644
--- a/src/gui/undocommands.h
+++ b/src/gui/undocommands.h
@@ -36,7 +36,7 @@
class GraphUpdateCommand : public QUndoCommand {
public:
explicit GraphUpdateCommand(TikzScene *scene,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
protected:
@@ -49,7 +49,7 @@ public:
explicit MoveCommand(TikzScene *scene,
QMap<Node*,QPointF> oldNodePositions,
QMap<Node*,QPointF> newNodePositions,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -61,18 +61,18 @@ class EdgeBendCommand : public GraphUpdateCommand
{
public:
explicit EdgeBendCommand(TikzScene *scene, Edge *edge,
- float oldWeight, int oldBend,
+ qreal oldWeight, int oldBend,
int oldInAngle, int oldOutAngle,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
Edge *_edge;
- float _oldWeight;
+ qreal _oldWeight;
int _oldBend;
int _oldInAngle;
int _oldOutAngle;
- float _newWeight;
+ qreal _newWeight;
int _newBend;
int _newInAngle;
int _newOutAngle;
@@ -85,7 +85,7 @@ public:
QMap<int,Node*> deleteNodes,
QMap<int,Edge*> deleteEdges,
QSet<Edge*> selEdges,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -98,7 +98,7 @@ class AddNodeCommand : public GraphUpdateCommand
{
public:
explicit AddNodeCommand(TikzScene *scene, Node *node, QRectF newBounds,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -110,7 +110,7 @@ private:
class AddEdgeCommand : public GraphUpdateCommand
{
public:
- explicit AddEdgeCommand(TikzScene *scene, Edge *edge, QUndoCommand *parent = 0);
+ explicit AddEdgeCommand(TikzScene *scene, Edge *edge, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -120,7 +120,7 @@ private:
class ChangeEdgeModeCommand : public GraphUpdateCommand
{
public:
- explicit ChangeEdgeModeCommand(TikzScene *scene, Edge *edge, QUndoCommand *parent = 0);
+ explicit ChangeEdgeModeCommand(TikzScene *scene, Edge *edge, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -130,7 +130,7 @@ private:
class ApplyStyleToNodesCommand : public GraphUpdateCommand
{
public:
- explicit ApplyStyleToNodesCommand(TikzScene *scene, QString style, QUndoCommand *parent = 0);
+ explicit ApplyStyleToNodesCommand(TikzScene *scene, QString style, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -141,7 +141,7 @@ private:
class ApplyStyleToEdgesCommand : public GraphUpdateCommand
{
public:
- explicit ApplyStyleToEdgesCommand(TikzScene *scene, QString style, QUndoCommand *parent = 0);
+ explicit ApplyStyleToEdgesCommand(TikzScene *scene, QString style, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -152,7 +152,7 @@ private:
class PasteCommand : public GraphUpdateCommand
{
public:
- explicit PasteCommand(TikzScene *scene, Graph *graph, QUndoCommand *parent = 0);
+ explicit PasteCommand(TikzScene *scene, Graph *graph, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -167,7 +167,7 @@ public:
explicit ChangeLabelCommand(TikzScene *scene,
QMap<Node*,QString> oldLabels,
QString newLabel,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -181,7 +181,7 @@ public:
explicit ReplaceGraphCommand(TikzScene *scene,
Graph *oldGraph,
Graph *newGraph,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -195,7 +195,7 @@ public:
explicit ReflectNodesCommand(TikzScene *scene,
QSet<Node*> nodes,
bool horizontal,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -209,7 +209,7 @@ public:
explicit RotateNodesCommand(TikzScene *scene,
QSet<Node*> nodes,
bool clockwise,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
@@ -225,7 +225,7 @@ public:
const QVector<Node*> &newNodeOrder,
const QVector<Edge*> &oldEdgeOrder,
const QVector<Edge*> &newEdgeOrder,
- QUndoCommand *parent = 0);
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
diff --git a/src/main.cpp b/src/main.cpp
index 99d23e9..7a282e6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -16,7 +16,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
/*!
* \file main.cpp
*
@@ -31,21 +30,17 @@
#include <QDebug>
#include <QScreen>
-// #ifdef Q_OS_WIN
-// #include <Windows.h>
-// #endif
-
int main(int argc, char *argv[])
{
// #ifdef Q_OS_WIN
// SetProcessDPIAware();
// #endif
-// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ // QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
// dummy application for detecting DPI
QApplication *a0 = new QApplication(argc, argv);
-// qDebug() << "physical DPI" << QApplication::screens()[0]->physicalDotsPerInch();
+ // qDebug() << "physical DPI" << QApplication::screens()[0]->physicalDotsPerInch();
if (QApplication::screens()[0]->physicalDotsPerInch() >= 100) {
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
@@ -58,14 +53,11 @@ int main(int argc, char *argv[])
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
-
-
tikzit = new Tikzit();
tikzit->init();
qDebug() << a.arguments().length();
-
if (a.arguments().length() > 1) {
tikzit->open(a.arguments()[1]);
}
diff --git a/src/tikzit.cpp b/src/tikzit.cpp
index c9286c9..8569817 100644
--- a/src/tikzit.cpp
+++ b/src/tikzit.cpp
@@ -19,6 +19,8 @@
#include "tikzit.h"
#include "tikzassembler.h"
#include "tikzstyles.h"
+#include "previewwindow.h"
+#include "latexprocess.h"
#include <QFile>
#include <QFileDialog>
@@ -28,6 +30,7 @@
#include <QRegularExpression>
#include <QVersionNumber>
#include <QNetworkAccessManager>
+#include <QColorDialog>
// application-level instance of Tikzit
Tikzit *tikzit;
@@ -35,7 +38,7 @@ Tikzit *tikzit;
// font to use for node labels
QFont Tikzit::LABEL_FONT("Courrier", 9);
-Tikzit::Tikzit() : _styleFile("[no styles]"), _activeWindow(0)
+Tikzit::Tikzit() : _styleFile("[no styles]"), _activeWindow(nullptr)
{
}
@@ -43,54 +46,7 @@ void Tikzit::init()
{
QSettings settings("tikzit", "tikzit");
- // 19 standard xcolor colours
- _colNames <<
- "black" <<
- "darkgray" <<
- "gray" <<
- "lightgray" <<
- "white" <<
-
- "red" <<
- "orange" <<
- "yellow" <<
- "green" <<
- "blue" <<
- "purple" <<
-
- "brown" <<
- "olive" <<
- "lime" <<
- "cyan" <<
- "teal" <<
-
- "magenta" <<
- "violet" <<
- "pink";
-
- _cols <<
- QColor::fromRgbF(0,0,0) <<
- QColor::fromRgbF(0.25,0.25,0.25) <<
- QColor::fromRgbF(0.5,0.5,0.5) <<
- QColor::fromRgbF(0.75,0.75,0.75) <<
- QColor::fromRgbF(1,1,1) <<
-
- QColor::fromRgbF(1,0,0) <<
- QColor::fromRgbF(1,0.5,0) <<
- QColor::fromRgbF(1,1,0) <<
- QColor::fromRgbF(0,1,0) <<
- QColor::fromRgbF(0,0,1) <<
- QColor::fromRgbF(0.75,0,0.25) <<
-
- QColor::fromRgbF(0.75,0.5,0.25) <<
- QColor::fromRgbF(0.5,0.5,0) <<
- QColor::fromRgbF(0.75,1,0) <<
- QColor::fromRgbF(0,1,1) <<
- QColor::fromRgbF(0,0.5,0.5) <<
-
- QColor::fromRgbF(1,0,1) <<
- QColor::fromRgbF(0.5,0,0.5) <<
- QColor::fromRgbF(1,0.75,0.75);
+ initColors();
_mainMenu = new MainMenu();
QMainWindow *dummy = new QMainWindow();
@@ -106,12 +62,14 @@ void Tikzit::init()
_windows << new MainWindow();
_windows[0]->show();
+ _styleFile = "";
+ _styleFilePath = "";
QString styleFile = settings.value("previous-tikzstyles-file").toString();
if (!styleFile.isEmpty()) loadStyles(styleFile);
QVariant check = settings.value("check-for-updates");
if (check.isNull()) {
- int resp = QMessageBox::question(0,
+ int resp = QMessageBox::question(nullptr,
tr("Check for updates"),
tr("Would you like TikZiT to check for updates automatically?"
" (You can always change this later in the Help menu.)"),
@@ -124,8 +82,11 @@ void Tikzit::init()
setCheckForUpdates(check.toBool());
if (check.toBool()) {
- checkForUpdates();
+ checkForUpdates(false);
}
+
+ _preview = new PreviewWindow();
+ _latex = nullptr;
}
//QMenuBar *Tikzit::mainMenu() const
@@ -190,7 +151,7 @@ void Tikzit::newTikzStyles()
if (dialog.exec() && !dialog.selectedFiles().isEmpty()) {
QString fileName = dialog.selectedFiles()[0];
- TikzStyles *st = new TikzStyles;
+ TikzStyles *st = new TikzStyles(this);
if (st->saveStyles(fileName)) {
QFileInfo fi(fileName);
@@ -198,14 +159,14 @@ void Tikzit::newTikzStyles()
_styleFilePath = fi.absoluteFilePath();
settings.setValue("previous-tikzstyles-file", fileName);
settings.setValue("previous-tikzstyles-path", fi.absolutePath());
- delete _styles;
+ _styles->deleteLater();
_styles = st;
foreach (MainWindow *w, _windows) {
w->tikzScene()->reloadStyles();
}
} else {
- QMessageBox::warning(0,
+ QMessageBox::warning(nullptr,
"Could not write to style file.",
"Could not write to: '" + fileName + "'. Check file permissions or choose a new location.");
}
@@ -244,7 +205,7 @@ void Tikzit::removeWindow(MainWindow *w)
_windows.removeAll(w);
if (_activeWindow == w) {
if (_windows.isEmpty()) {
- _activeWindow = 0;
+ _activeWindow = nullptr;
// TODO: check if we should quit when last window closed
quit();
} else _activeWindow = _windows[0];
@@ -254,7 +215,7 @@ void Tikzit::removeWindow(MainWindow *w)
void Tikzit::open()
{
QSettings settings("tikzit", "tikzit");
- QString fileName = QFileDialog::getOpenFileName(0,
+ QString fileName = QFileDialog::getOpenFileName(nullptr,
tr("Open File"),
settings.value("previous-file-path").toString(),
tr("TiKZ Files (*.tikz)"),
@@ -269,23 +230,35 @@ void Tikzit::open(QString fileName)
if (!fileName.isEmpty()) {
if (_windows.size() == 1 &&
_windows[0]->tikzDocument()->isClean() &&
- _windows[0]->tikzDocument()->shortName().isEmpty())
- {
+ _windows[0]->tikzDocument()->shortName().isEmpty())
+ {
_windows[0]->open(fileName);
_windows[0]->show();
- }
- else {
- MainWindow *w = new MainWindow();
- w->show();
- w->open(fileName);
- _windows << w;
+ }
+ else
+ {
+ bool found = false;
+ foreach (MainWindow *w, _windows) {
+ if (w->tikzDocument()->fileName() == fileName) {
+ w->raise();
+ w->activateWindow();
+ found = true;
+ }
+ }
+
+ if (!found) {
+ MainWindow *w = new MainWindow();
+ _windows << w;
+ w->show();
+ w->open(fileName);
+ }
}
}
}
void Tikzit::openTikzStyles() {
QSettings settings("tikzit", "tikzit");
- QString fileName = QFileDialog::getOpenFileName(0,
+ QString fileName = QFileDialog::getOpenFileName(nullptr,
tr("Open File"),
settings.value("previous-tikzstyles-path").toString(),
tr("TiKZ Style Files (*.tikzstyles)"),
@@ -310,7 +283,7 @@ bool Tikzit::loadStyles(QString fileName)
if (st->loadStyles(fileName)) {
_styleFile = fi.fileName();
_styleFilePath = fi.absoluteFilePath();
- delete _styles;
+ _styles->deleteLater();
_styles = st;
foreach (MainWindow *w, _windows) {
@@ -318,7 +291,7 @@ bool Tikzit::loadStyles(QString fileName)
}
return true;
} else {
- QMessageBox::warning(0,
+ QMessageBox::warning(nullptr,
"Bad style file.",
"Bad style file: '" + fileName + "'. Check the file is properly formatted and try to load it again.");
return false;
@@ -326,7 +299,8 @@ bool Tikzit::loadStyles(QString fileName)
} else {
//settings.setValue("previous-tikzstyles-file", "");
- QMessageBox::warning(0, "Style file not found.", "Could not open style file: '" + fileName + "'.");
+ QMessageBox::warning(nullptr,
+ "Style file not found.", "Could not open style file: '" + fileName + "'.");
return false;
}
}
@@ -346,6 +320,20 @@ QString Tikzit::styleFilePath() const
return _styleFilePath;
}
+void Tikzit::updateRecentFiles()
+{
+ foreach (MainWindow *w, _windows) {
+ w->menu()->updateRecentFiles();
+ }
+}
+
+void Tikzit::clearRecentFiles()
+{
+ QSettings settings("tikzit", "tikzit");
+ settings.setValue("recent-files", QStringList());
+ updateRecentFiles();
+}
+
void Tikzit::setCheckForUpdates(bool check)
{
QSettings settings("tikzit", "tikzit");
@@ -357,16 +345,32 @@ void Tikzit::setCheckForUpdates(bool check)
}
}
-void Tikzit::checkForUpdates()
+void Tikzit::checkForUpdates(bool manual)
{
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
- connect(manager, SIGNAL(finished(QNetworkReply*)),
- this, SLOT(updateReply(QNetworkReply*)));
+
+ if (manual) {
+ connect(manager, SIGNAL(finished(QNetworkReply*)),
+ this, SLOT(updateManual(QNetworkReply*)));
+ } else {
+ connect(manager, SIGNAL(finished(QNetworkReply*)),
+ this, SLOT(updateAuto(QNetworkReply*)));
+ }
manager->get(QNetworkRequest(QUrl("https://tikzit.github.io/latest-version.txt")));
}
-void Tikzit::updateReply(QNetworkReply *reply)
+void Tikzit::updateAuto(QNetworkReply *reply)
+{
+ updateReply(reply, false);
+}
+
+void Tikzit::updateManual(QNetworkReply *reply)
+{
+ updateReply(reply, true);
+}
+
+void Tikzit::updateReply(QNetworkReply *reply, bool manual)
{
if (!reply->isReadable()) return;
@@ -395,7 +399,7 @@ void Tikzit::updateReply(QNetworkReply *reply)
QString::number(latest.minorVersion()) + "." +
QString::number(latest.microVersion());
if (rcLatest != 1000) strLatest += "-rc" + QString::number(rcLatest);
- QMessageBox::information(0,
+ QMessageBox::information(nullptr,
tr("Update available"),
"<p><b>A new version of TikZiT is available!</b></p>"
"<p><i>current version: " TIKZIT_VERSION "<br />"
@@ -404,13 +408,138 @@ void Tikzit::updateReply(QNetworkReply *reply)
"<a href=\"https://tikzit.github.io\">tikzit.github.io</a>.</p>");
}
} else {
- QMessageBox::warning(0,
+ // don't complain of invalid response for auto update check
+ if (manual) {
+ QMessageBox::warning(nullptr,
tr("Invalid response"),
"<p>Got invalid version response from "
"<a href=\"https://tikzit.github.io\">tikzit.github.io</a>.</p>");
+ }
+ }
+}
+
+void Tikzit::makePreview()
+{
+ if (activeWindow()) {
+ LatexProcess *oldProc = _latex;
+ _latex = new LatexProcess(_preview, this);
+ if (oldProc != nullptr) {
+ oldProc->kill();
+ oldProc->deleteLater();
+ }
+
+ connect(_latex, SIGNAL(previewFinished()), this, SLOT(cleanupLatex()));
+
+ if (activeWindow()->tikzDocument()->isEmpty()) {
+ _latex->makePreview("\\begin{tikzpicture}\n"
+ " \\node [style=none] (0) at (0,0) {};\n"
+ "\\end{tikzpicture}\n");
+ } else {
+ _latex->makePreview(activeWindow()->tikzSource());
+ }
+ _preview->show();
+ _preview->raise();
}
}
+void Tikzit::cleanupLatex()
+{
+ LatexProcess *oldProc = _latex;
+ _latex = nullptr;
+ if (oldProc != nullptr) {
+ oldProc->deleteLater();
+ }
+}
+
+void Tikzit::initColors()
+{
+ // 19 standard xcolor colours
+ _colNames <<
+ "black" <<
+ "darkgray" <<
+ "gray" <<
+ "lightgray" <<
+ "white" <<
+
+ "red" <<
+ "orange" <<
+ "yellow" <<
+ "green" <<
+ "blue" <<
+ "purple" <<
+
+ "brown" <<
+ "olive" <<
+ "lime" <<
+ "cyan" <<
+ "teal" <<
+
+ "magenta" <<
+ "violet" <<
+ "pink";
+
+ _cols <<
+ QColor::fromRgbF(0,0,0) <<
+ QColor::fromRgbF(0.25,0.25,0.25) <<
+ QColor::fromRgbF(0.5,0.5,0.5) <<
+ QColor::fromRgbF(0.75,0.75,0.75) <<
+ QColor::fromRgbF(1,1,1) <<
+
+ QColor::fromRgbF(1,0,0) <<
+ QColor::fromRgbF(1,0.5,0) <<
+ QColor::fromRgbF(1,1,0) <<
+ QColor::fromRgbF(0,1,0) <<
+ QColor::fromRgbF(0,0,1) <<
+ QColor::fromRgbF(0.75,0,0.25) <<
+
+ QColor::fromRgbF(0.75,0.5,0.25) <<
+ QColor::fromRgbF(0.5,0.5,0) <<
+ QColor::fromRgbF(0.75,1,0) <<
+ QColor::fromRgbF(0,1,1) <<
+ QColor::fromRgbF(0,0.5,0.5) <<
+
+ QColor::fromRgbF(1,0,1) <<
+ QColor::fromRgbF(0.5,0,0.5) <<
+ QColor::fromRgbF(1,0.75,0.75);
+
+ for (int i = 0; i < 48; ++i) {
+ QColorDialog::setStandardColor(i, QColor(Qt::white));
+ }
+
+ // grayscale in column 1
+ int pos = 0;
+ for (int i=0; i < 5; ++i) {
+ QColorDialog::setStandardColor(pos, _cols[i]);
+ pos += 1;
+ }
+
+ // rainbow in column 2
+ pos = 6;
+ for (int i=5; i < 11; ++i) {
+ QColorDialog::setStandardColor(pos, _cols[i]);
+ pos += 1;
+ }
+
+ // brown/green/teal spectrum in column 3
+ pos = 12;
+ for (int i=11; i < 16; ++i) {
+ QColorDialog::setStandardColor(pos, _cols[i]);
+ pos += 1;
+ }
+
+ // pinks in column 4
+ pos = 18;
+ for (int i=16; i < 19; ++i) {
+ QColorDialog::setStandardColor(pos, _cols[i]);
+ pos += 1;
+ }
+}
+
+PreviewWindow *Tikzit::previewWindow() const
+{
+ return _preview;
+}
+
//StylePalette *Tikzit::stylePalette() const
//{
// return _stylePalette;
diff --git a/src/tikzit.h b/src/tikzit.h
index d36a940..4797f48 100644
--- a/src/tikzit.h
+++ b/src/tikzit.h
@@ -60,6 +60,8 @@
#include "propertypalette.h"
#include "stylepalette.h"
#include "tikzstyles.h"
+#include "latexprocess.h"
+#include "previewwindow.h"
#include <QObject>
#include <QVector>
@@ -74,12 +76,13 @@
// Number of pixels between (0,0) and (1,0) at 100% zoom level. This should be
// divisible by 8 to avoid rounding errors with e.g. grid-snapping.
#define GLOBAL_SCALE 40
-#define GLOBAL_SCALEF 40.0f
-#define GLOBAL_SCALEF_INV 0.025f
+#define GLOBAL_SCALEF 40.0
+#define GLOBAL_SCALEF_INV 0.025
#define GRID_N 4
#define GRID_SEP 10
#define GRID_SEPF 10.0f
+
inline QPointF toScreen(QPointF src)
{ src.setY(-src.y()); src *= GLOBAL_SCALEF; return src; }
@@ -131,14 +134,26 @@ public:
//StylePalette *stylePalette() const;
QString styleFilePath() const;
+ void updateRecentFiles();
+
+ PreviewWindow *previewWindow() const;
public slots:
+ void clearRecentFiles();
void setCheckForUpdates(bool check);
- void checkForUpdates();
- void updateReply(QNetworkReply *reply);
+ void checkForUpdates(bool manual);
+ void updateAuto(QNetworkReply *reply);
+ void updateManual(QNetworkReply *reply);
+ void updateReply(QNetworkReply *reply, bool manual);
+ void makePreview();
+ void cleanupLatex();
private:
- // void createMenu();
+ /*!
+ * \brief initColors initialises a table of xcolor named colors and their associated
+ * QColor values, and adds them as standard colors to the Qt color dialog.
+ */
+ void initColors();
MainMenu *_mainMenu;
ToolPalette *_toolPalette;
@@ -152,6 +167,8 @@ private:
StyleEditor *_styleEditor;
QStringList _colNames;
QVector<QColor> _cols;
+ LatexProcess *_latex;
+ PreviewWindow *_preview;
};
extern Tikzit *tikzit;
diff --git a/src/util.cpp b/src/util.cpp
index d5e2b96..304f9e7 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -19,31 +19,31 @@
#include "util.h"
-float bezierInterpolate(float dist, float c0, float c1, float c2, float c3) {
- float distp = 1 - dist;
+qreal bezierInterpolate(qreal dist, qreal c0, qreal c1, qreal c2, qreal c3) {
+ qreal distp = 1 - dist;
return (distp*distp*distp) * c0 +
3 * (distp*distp) * dist * c1 +
3 * (dist*dist) * distp * c2 +
(dist*dist*dist) * c3;
}
-QPointF bezierInterpolateFull (float dist, QPointF c0, QPointF c1, QPointF c2, QPointF c3) {
+QPointF bezierInterpolateFull (qreal dist, QPointF c0, QPointF c1, QPointF c2, QPointF c3) {
return QPointF(bezierInterpolate (dist, c0.x(), c1.x(), c2.x(), c3.x()),
bezierInterpolate (dist, c0.y(), c1.y(), c2.y(), c3.y()));
}
-float roundToNearest(float stepSize, float val) {
- if (stepSize==0.0f) return val;
+qreal roundToNearest(qreal stepSize, qreal val) {
+ if (stepSize==0.0) return val;
else return round(val/stepSize)*stepSize;
}
-float radiansToDegrees (float radians) {
- return (radians * 180.0f) / M_PI;
+qreal radiansToDegrees (qreal radians) {
+ return (radians * 180.0) / M_PI;
}
-float degreesToRadians(float degrees) {
- return (degrees * M_PI) / 180.0f;
+qreal degreesToRadians(qreal degrees) {
+ return (degrees * M_PI) / 180.0;
}
int normaliseAngleDeg (int degrees) {
@@ -56,7 +56,7 @@ int normaliseAngleDeg (int degrees) {
return degrees;
}
-float normaliseAngleRad (float rads) {
+qreal normaliseAngleRad (qreal rads) {
while (rads > M_PI) {
rads -= 2 * M_PI;
}
@@ -66,8 +66,17 @@ float normaliseAngleRad (float rads) {
return rads;
}
-// convert float to string, squashing very small floats to zero
-QString floatToString(float f) {
- if (f >= -0.000001 && f <= 0.000001) return "0";
+bool almostZero(qreal f) {
+ return (f >= -0.000001 && f <= 0.000001);
+}
+
+bool almostEqual(qreal f1, qreal f2) {
+ return almostZero(f1 - f2);
+}
+
+// convert qreal to string, squashing very small qreals to zero
+QString floatToString(qreal f) {
+ if (almostZero(f)) return "0";
else return QString::number(f);
}
+
diff --git a/src/util.h b/src/util.h
index 89d0c5b..5d1073a 100644
--- a/src/util.h
+++ b/src/util.h
@@ -33,17 +33,19 @@
#endif
// interpolate on a cubic bezier curve
-float bezierInterpolate(float dist, float c0, float c1, float c2, float c3);
-QPointF bezierInterpolateFull (float dist, QPointF c0, QPointF c1, QPointF c2, QPointF c3);
+qreal bezierInterpolate(qreal dist, qreal c0, qreal c1, qreal c2, qreal c3);
+QPointF bezierInterpolateFull (qreal dist, QPointF c0, QPointF c1, QPointF c2, QPointF c3);
// rounding
-float roundToNearest(float stepSize, float val);
-float radiansToDegrees (float radians);
-QString floatToString(float f);
+qreal roundToNearest(qreal stepSize, qreal val);
+qreal radiansToDegrees (qreal radians);
+bool almostZero(qreal f);
+bool almostEqual(qreal f1, qreal f2);
+QString floatToString(qreal f);
// angles
-float degreesToRadians(float degrees);
+qreal degreesToRadians(qreal degrees);
int normaliseAngleDeg (int degrees);
-float normaliseAngleRad (float rads);
+qreal normaliseAngleRad (qreal rads);
#endif // UTIL_H