// application-level instance of Tikzit
Tikzit *tikzit;
// font to use for node labels
QFont Tikzit::LABEL_FONT("Courrier", 9);
Tikzit::Tikzit() : _styleFile("[no styles]"), _activeWindow(nullptr)
{
}
void Tikzit::init()
{
QSettings settings("tikzit", "tikzit");
initColors();
initTexConstants();
_mainMenu = new MainMenu();
QMainWindow *dummy = new QMainWindow();
_toolPalette = new ToolPalette(dummy);
_propertyPalette = new PropertyPalette(dummy);
//_stylePalette = new StylePalette(dummy);
_styles = new TikzStyles(this);
_styleEditor = new StyleEditor();
//_stylePalette->show();
_windows << new MainWindow();
_windows[0]->show();
_windows[0]->restorePosition();
_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(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.)"),
QMessageBox::Yes | QMessageBox::Default,
QMessageBox::No,
QMessageBox::NoButton);
check.setValue(resp == QMessageBox::Yes);
}
setCheckForUpdates(check.toBool());
if (check.toBool()) {
checkForUpdates(false);
}
_preview = new PreviewWindow();
_latex = nullptr;
}
//QMenuBar *Tikzit::mainMenu() const
//{
// return _mainMenu;
//}
QColor Tikzit::colorByIndex(int i)
{
return _cols[i];
}
QColor Tikzit::colorByName(QString name)
{
for (int i = 0; i < _colNames.length(); ++i) {
if (_colNames[i] == name) return _cols[i];
}
QRegExp re(
"rgb\\s*,\\s*255\\s*:\\s*"
"red\\s*,\\s*([0-9]+)\\s*;\\s*"
"green\\s*,\\s*([0-9]+)\\s*;\\s*"
"blue\\s*,\\s*([0-9]+)\\s*"
);
if (re.exactMatch(name)) {
QStringList cap = re.capturedTexts();
//qDebug() << cap;
return QColor(
cap[1].toInt(),
cap[2].toInt(),
cap[3].toInt());
}
return QColor();
}
QString Tikzit::nameForColor(QColor col)
{
for (int i = 0; i < _colNames.length(); ++i) {
if (_cols[i] == col) return _colNames[i];
}
// if the color is not recognised, return it in tikz-readable RBG format
return "rgb,255: red,"+ QString::number(col.red()) +
"; green," + QString::number(col.green()) +
"; blue," + QString::number(col.blue());
}
void Tikzit::newTikzStyles()
{
QSettings settings("tikzit", "tikzit");
QFileDialog dialog;
dialog.setDefaultSuffix("tikzstyles");
dialog.setWindowTitle(tr("Create TikZ Style File"));
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setLabelText(QFileDialog::Accept, "Create");
dialog.setNameFilter(tr("TiKZ Style File (*.tikzstyles)"));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setDirectory(settings.value("previous-file-path").toString());
dialog.setOption(QFileDialog::DontUseNativeDialog);
if (dialog.exec() && !dialog.selectedFiles().isEmpty()) {
QString fileName = dialog.selectedFiles()[0];
TikzStyles *st = new TikzStyles(this);
if (st->saveStyles(fileName)) {
QFileInfo fi(fileName);
_styleFile = fi.fileName();
_styleFilePath = fi.absoluteFilePath();
settings.setValue("previous-tikzstyles-file", fileName);
settings.setValue("previous-tikzstyles-path", fi.absolutePath());
_styles->deleteLater();
_styles = st;
foreach (MainWindow *w, _windows) {
w->tikzScene()->reloadStyles();
}
} else {
QMessageBox::warning(nullptr,
"Could not write to style file.",
"Could not write to: '" + fileName + "'. Check file permissions or choose a new location.");
}
}
}
ToolPalette *Tikzit::toolPalette() const
{
return _toolPalette;
}
PropertyPalette *Tikzit::propertyPalette() const
{
return _propertyPalette;
}
void Tikzit::newDoc()
{
MainWindow *w = new MainWindow();
w->show();
w->restorePosition();
_windows << w;
}
MainWindow *Tikzit::activeWindow() const
{
return _activeWindow;
}
void Tikzit::setActiveWindow(MainWindow *activeWindow)
{
_activeWindow = activeWindow;
}
void Tikzit::removeWindow(MainWindow *w)
{
_windows.removeAll(w);
if (_activeWindow == w) {
if (_windows.isEmpty()) {
_activeWindow = nullptr;
// TODO: check if we should quit when last window closed
quit();
} else _activeWindow = _windows[0];
}
}
void Tikzit::open()
{
QSettings settings("tikzit", "tikzit");
QString fileName = QFileDialog::getOpenFileName(nullptr,
tr("Open File"),
settings.value("previous-file-path").toString(),
tr("TiKZ Files (*.tikz)"),
nullptr,
QFileDialog::DontUseNativeDialog);
open(fileName);
}
void Tikzit::open(QString fileName)
{
if (!fileName.isEmpty()) {
if (_windows.size() == 1 &&
_windows[0]->tikzDocument()->isClean() &&
_windows[0]->tikzDocument()->shortName().isEmpty())
{
_windows[0]->open(fileName);
_windows[0]->show();
}
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->restorePosition();
w->open(fileName);
}
}
}
}
void Tikzit::openTikzStyles() {
QSettings settings("tikzit", "tikzit");
QString fileName = QFileDialog::getOpenFileName(nullptr,
tr("Open File"),
settings.value("previous-tikzstyles-path").toString(),
tr("TiKZ Style Files (*.tikzstyles)"),
nullptr,
QFileDialog::DontUseNativeDialog);
if (!fileName.isEmpty()) {
QFileInfo fi(fileName);
if (fi.exists() && loadStyles(fileName)) {
QSettings settings("tikzit", "tikzit");
settings.setValue("previous-tikzstyles-path", fi.absolutePath());
settings.setValue("previous-tikzstyles-file", fileName);
}
}
}
bool Tikzit::loadStyles(QString fileName)
{
QFileInfo fi(fileName);
if (fi.exists()) {
TikzStyles *st = new TikzStyles(this);
if (st->loadStyles(fileName)) {
_styleFile = fi.fileName();
_styleFilePath = fi.absoluteFilePath();
_styles->deleteLater();
_styles = st;
foreach (MainWindow *w, _windows) {
w->tikzScene()->reloadStyles();
}
return true;
} else {
QMessageBox::warning(nullptr,
"Bad style file.",
"Bad style file: '" + fileName + "'. Check the file is properly formatted and try to load it again.");
return false;
}
} else {
//settings.setValue("previous-tikzstyles-file", "");
QMessageBox::warning(nullptr,
"Style file not found.", "Could not open style file: '" + fileName + "'.");
return false;
}
}
void Tikzit::showStyleEditor()
{
_styleEditor->open();
}
QString Tikzit::styleFile() const
{
return _styleFile;
}
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");
settings.setValue("check-for-updates", check);
foreach (MainWindow *w, _windows) {
w->menu()->updatesAction()->blockSignals(true);
w->menu()->updatesAction()->setChecked(check);
w->menu()->updatesAction()->blockSignals(false);
}
}
void Tikzit::checkForUpdates(bool manual)
{
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
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::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;
QByteArray data = reply->read(200);
QString strLatest = QString::fromUtf8(data).simplified();
// check for valid version string and capture optional RC suffix
QRegularExpression re("^[1-9]+(\\.[0-9]+)*(-[rR][cC]([0-9]+))?$");
QRegularExpressionMatch m;
m = re.match(TIKZIT_VERSION);
// any non-RC versions are considered later than RC versions.
int rcCurrent = (!m.captured(3).isEmpty()) ? m.captured(3).toInt() : 1000;
m = re.match(strLatest);
if (m.hasMatch()) {
QVersionNumber current = QVersionNumber::fromString(TIKZIT_VERSION).normalized();
QVersionNumber latest = QVersionNumber::fromString(strLatest).normalized();
int rcLatest = (!m.captured(3).isEmpty()) ? m.captured(3).toInt() : 1000;
if (latest > current || (latest == current && rcLatest > rcCurrent)) {
// give the version string in standard format
strLatest = QString::number(latest.majorVersion()) + "." +
QString::number(latest.minorVersion()) + "." +
QString::number(latest.microVersion());
if (rcLatest != 1000) strLatest += "-rc" + QString::number(rcLatest);
QMessageBox::information(nullptr,
tr("Update available"),
"A new version of TikZiT is available!
"
"current version: " TIKZIT_VERSION "
"
"latest version: " + strLatest + "
"
"Download it now from: "
"tikzit.github.io.
");
}
} else {
// don't complain of invalid response for auto update check
if (manual) {
QMessageBox::warning(nullptr,
tr("Invalid response"),
"Got invalid version response from "
"tikzit.github.io.
");
}
}
}
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();
// restores position from settings the first time this is called, otherwise
// do nothing.
_preview->restorePosition();
_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;
//}
TikzStyles *Tikzit::styles() const
{
return _styles;
}
void Tikzit::quit()
{
//_stylePalette->close();
QApplication::quit();
}