summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGard Spreemann <gspreemann@gmail.com>2019-01-08 23:28:14 +0100
committerGard Spreemann <gspreemann@gmail.com>2019-01-08 23:28:14 +0100
commitd1728e33e48d258d72058082a22b0406f48c9e9e (patch)
treeea3c3bdfd2255c7f00296d65c5a0ee0f4b2620fb
parent4c6f7799004b4d10641dfe659a9a7b9af0653513 (diff)
parent7807d9c60d2574fb58069eb3faf65cd478905ff7 (diff)
Merge tag 'v2.1' into debian/sid
-rw-r--r--.appveyor.yml11
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml30
-rw-r--r--CMakeLists.txt112
-rw-r--r--Info.plist26
-rw-r--r--README.md61
-rwxr-xr-xdeploy-linux.sh9
-rwxr-xr-xdeploy-osx.sh13
-rwxr-xr-xdeploy-win.bat7
-rw-r--r--images/dialog-accept.svg63
-rw-r--r--images/dialog-error.svg316
-rw-r--r--images/loader.gifbin0 -> 1456 bytes
-rw-r--r--images/loader@2x.gifbin0 -> 1456 bytes
-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
-rw-r--r--tex/sample/figures/fig.tikz12
-rw-r--r--tex/sample/sample.tex23
-rw-r--r--tex/sample/sample.tikzdefs5
-rw-r--r--tex/sample/sample.tikzstyles6
-rw-r--r--tex/sample/tikzit.sty24
-rw-r--r--tikzit.pro46
-rw-r--r--tikzit.qrc5
62 files changed, 3118 insertions, 409 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index c8c4424..f54801e 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -13,16 +13,19 @@ configuration:
install:
- cmd: choco install winflexbison
- - cmd: 'C:\Qt\5.11.1\msvc2015_64\bin\qtenv2.bat'
- - cmd: call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
+ - cmd: 'C:\Qt\5.11.2\mingw53_32\bin\qtenv2.bat'
- cmd: cd C:\projects\tikzit
- sh: sudo apt-get update
- - sh: sudo apt-get -y install flex bison qt5-default
+ - sh: sudo apt-get -y install flex bison qt5-default libpoppler-dev libpoppler-qt5-dev
+
+before_build:
+ - ps: (new-object net.webclient).DownloadFile('http://tikzit.github.io/download/win32-deps.zip', 'c:\projects\tikzit\win32-deps.zip')
+ - cmd: 7z x win32-deps.zip
build_script:
- qmake -v
- qmake
- - cmd: nmake.exe
+ - cmd: mingw32-make
- sh: make
after_build:
diff --git a/.gitignore b/.gitignore
index 0fb2e43..2c5a2d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ tex/sample/sample.pdf
*.sublime-*
src-old
GeneratedFiles
+build
.vs
debug
release
diff --git a/.travis.yml b/.travis.yml
index 1c814d1..52de1f4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,39 +12,31 @@ matrix:
- FILE=tikzit-osx.dmg
- QTVER=511
- DEPLOY_TIKZIT=1
- - os: osx
- compiler: clang
- env:
- - FILE=tikzit-osx-mountain.dmg
- - QTVER=56
- - DEPLOY_TIKZIT=1
- # - os: linux
- # dist: trusty
+ # - os: osx
+ # compiler: clang
# env:
- # - FILE=tikzit-linux.tar.gz
- # - PPA=beineri/opt-qt-5.10.1-trusty
+ # - FILE=tikzit-osx-mountain.dmg
+ # - QTVER=56
+ # - DEPLOY_TIKZIT=1
before_install:
- # - '[[ "$TRAVIS_OS_NAME" != linux || -z "$PPA" ]] || sudo add-apt-repository -y ppa:$PPA'
- # - '[[ "$TRAVIS_OS_NAME" != linux ]] || sudo apt-get -qy update'
- - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || curl https://raw.githubusercontent.com/GiovanniBussi/macports-ci/master/macports-ci > macports-ci'
- - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || source macports-ci install'
+ # - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || curl https://raw.githubusercontent.com/GiovanniBussi/macports-ci/master/macports-ci > macports-ci'
+ # - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || source macports-ci install'
install:
- '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 511 ]] || brew install qt5'
- '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 511 ]] || brew link --force qt5'
- - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || sudo port -N -k install qt56'
- - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || export PATH=/opt/local/libexec/qt5/bin:$PATH'
- # - '[[ "$TRAVIS_OS_NAME" != linux ]] || sudo apt-get -qy install qt510base qt510xmlpatterns'
- # - '[[ "$TRAVIS_OS_NAME" != linux ]] || . /opt/qt510/bin/qt510-env.sh'
+ - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 511 ]] || brew install poppler --with-qt'
+ # - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || sudo port -N -k install qt56'
+ # - '[[ "$TRAVIS_OS_NAME" != osx || "$QTVER" != 56 ]] || export PATH=/opt/local/libexec/qt5/bin:$PATH'
script:
- qmake -v
- qmake -r
- make
- - '[[ "$TRAVIS_OS_NAME" != osx ]] || (macdeployqt tikzit.app -dmg && mv tikzit.dmg $FILE)'
+ - '[[ "$TRAVIS_OS_NAME" != osx ]] || (chmod +x deploy-osx.sh && ./deploy-osx.sh && mv tikzit.dmg $FILE)'
- '[[ "$DEPLOY_TIKZIT" != 1 ]] || curl --upload-file $FILE https://transfer.sh/$FILE'
notifications:
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..311dac4
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,112 @@
+cmake_minimum_required(VERSION 3.1.0)
+
+project(tikzit LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+#set(CMAKE_AUTOUIC ON)
+
+find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Network)
+find_package(BISON)
+find_package(FLEX)
+
+# use extra-cmake-modules to find poppler library
+find_package(ECM REQUIRED NO_MODULE)
+set(CMAKE_MODULE_PATH ${ECM_FIND_MODULE_DIR})
+find_package(Poppler REQUIRED COMPONENTS Core Qt5)
+
+include_directories(
+ ${PROJECT_SOURCE_DIR}
+ ${PROJECT_SOURCE_DIR}/src
+ ${PROJECT_SOURCE_DIR}/src/data
+ ${PROJECT_SOURCE_DIR}/src/gui
+)
+
+BISON_TARGET(PARSER src/data/tikzparser.y ${CMAKE_CURRENT_BINARY_DIR}/tikzparser.parser.cpp)
+FLEX_TARGET(LEXER src/data/tikzlexer.l ${CMAKE_CURRENT_BINARY_DIR}/tikzlexer.lexer.cpp)
+ADD_FLEX_BISON_DEPENDENCY(LEXER PARSER)
+cmake_policy(SET CMP0071 NEW) # run AUTOXXX on generated files ('NEW' default behaviour)
+
+set(SOURCES
+ src/data/edge.cpp
+ src/data/graph.cpp
+ src/data/graphelementdata.cpp
+ src/data/graphelementproperty.cpp
+ src/data/node.cpp
+ src/data/style.cpp
+ src/data/stylelist.cpp
+ src/data/tikzassembler.cpp
+ src/data/tikzdocument.cpp
+ src/data/tikzstyles.cpp
+ src/gui/commands.cpp
+ src/gui/edgeitem.cpp
+ src/gui/mainmenu.cpp
+ src/gui/mainwindow.cpp
+ src/gui/nodeitem.cpp
+ src/gui/previewwindow.cpp
+ src/gui/propertypalette.cpp
+ src/gui/styleeditor.cpp
+ src/gui/stylepalette.cpp
+ src/gui/tikzscene.cpp
+ src/gui/tikzview.cpp
+ src/gui/toolpalette.cpp
+ src/gui/undocommands.cpp
+ src/main.cpp
+ src/tikzit.cpp
+ src/util.cpp
+)
+
+set(HEADERS
+ src/data/edge.h
+ src/data/graph.h
+ src/data/graphelementdata.h
+ src/data/graphelementproperty.h
+ src/data/node.h
+ src/data/style.h
+ src/data/stylelist.h
+ src/data/tikzassembler.h
+ src/data/tikzdocument.h
+ src/data/tikzparserdefs.h
+ src/data/tikzstyles.h
+ src/gui/commands.h
+ src/gui/edgeitem.h
+ src/gui/mainmenu.h
+ src/gui/mainwindow.h
+ src/gui/nodeitem.h
+ src/gui/previewwindow.h
+ src/gui/propertypalette.h
+ src/gui/styleeditor.h
+ src/gui/stylepalette.h
+ src/gui/tikzscene.h
+ src/gui/tikzview.h
+ src/gui/toolpalette.h
+ src/gui/undocommands.h
+ src/tikzit.h
+ src/util.h
+)
+
+set(FORMS
+ src/gui/mainmenu.ui
+ src/gui/mainwindow.ui
+ src/gui/previewwindow.ui
+ src/gui/propertypalette.ui
+ src/gui/styleeditor.ui
+ src/gui/stylepalette.ui
+)
+
+qt5_wrap_ui(FORM_HEADERS ${FORMS})
+qt5_add_resources(QT_RESOURCES tikzit.qrc)
+
+
+
+add_executable(tikzit
+ ${SOURCES}
+ ${HEADERS}
+ ${FORM_HEADERS}
+ ${FLEX_LEXER_OUTPUTS}
+ ${BISON_PARSER_OUTPUTS}
+ ${QT_RESOURCES})
+
+target_link_libraries(${PROJECT_NAME}
+ Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network
+ ${Poppler_LIBRARIES})
diff --git a/Info.plist b/Info.plist
new file mode 100644
index 0000000..829cd67
--- /dev/null
+++ b/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>tikzit</string>
+ <key>CFBundleIconFile</key>
+ <string>tikzit.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.github.tikzit</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.12</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+ <key>CFBundleVersion</key>
+ <string>@FULL_VERSION@</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@FULL_VERSION@</string>
+</dict>
+</plist>
diff --git a/README.md b/README.md
index 797b653..badcd22 100644
--- a/README.md
+++ b/README.md
@@ -5,23 +5,22 @@ TikZiT is a graphical tool for rapidly creating graphs and string diagrams using
## Building on Windows
-TiKZiT can be built in Windows using Qt Creator (part of <a href="http://doc.qt.io/qt-5/windows-support.html">Qt for Windows</a>) or Visual Studio with the Qt VS Tools extension.
+TiKZiT can be built in Windows using Qt Creator (part of <a href="http://doc.qt.io/qt-5/windows-support.html">Qt for Windows</a>) or from the command line. In either case, it is recommended you compile with <a href="http://www.mingw.org/">mingw32</a>, which is included in the official Qt distribution. There is no reason, in principle, that you couldn't use mingw64 or MSVC, but these haven't been tested.
-To build with Qt Creator, simply click 'Open Project' and navigate to the `.pro` file in the TikZiT repo.
+In addition to Qt itself, TikZiT needs flex/bison, <a href="https://poppler.freedesktop.org/">Poppler</a> (with Qt bindings), and <a href="https://www.openssl.org/">OpenSSL</a>. For flex/bison, the simplest way to install this is to download <a href="https://github.com/lexxmark/winflexbison">WinFlexBison</a>, then make sure both are in your `%Path%` so the build tools can find them. Alternatively, you can install it via <a href="https://chocolatey.org">Chocolatey</a>, via:
-To install Qt VS Tools in Visual Studio 2017, go to `Tools > Extensions and Updates`, then click "Online" in the sidebar and search for Qt. Configure your Qt install under `Qt VS Tools > Qt Options`. If you installed Qt using the Windows package above, the path to Qt is probably something like `C:\Qt\5.XXX\msvc2017_64`. Once that is done, open the `.pro` file in the TikZiT repo via `Qt VS Tools > Open Qt Project File`.
+ > choco install winflexbison
-The only dependency besides Qt itself is flex/bison, which is used to build the TikZ parser. The simplest way to install this is to download <a href="https://github.com/lexxmark/winflexbison">WinFlexBison</a>, then make sure both are in your `%PATH%` so the build tools can find them.
+For convenience, I have packaged up some headers and pre-built DLLs to take care of the Poppler and OpenSSL dependencies in a single shot. If you wish to use these, download <a href="http://tikzit.github.io/download/win32-deps.zip">win32-deps.zip</a> and extract it into the source folder before building. At this point, you should be able to open `tikzit.pro` in Qt Creator and build the project. If you wish to build from the command line, make sure `mingw32-make.exe` is in your `%Path%`. For the version that comes with Qt, this is in `C:\Qt\Tools\mingw530_32\bin`. Then, from the command prompt, run:
-You can alternatively build from the command line with mingw or Visual Studio, and install necessary dependencies via <a href="https://chocolatey.org">Chocolatey</a>. This setup has been tested on Windows 10 with Visual Studio 2015 and Qt 5.11.1. After installing Qt 5.11 and Visual Studio, run the following commands in a `cmd` prompt:
+ > C:\Qt\5.XX.X\mingw53_32\bin\qtenv2.bat
+ > cd \path\to\tikzit
+ > qmake -r
+ > mingw32-make
-
- choco install winflexbison
- C:\Qt\5.11.1\msvc2015_64\bin\qtenv2.bat
- call "C:\ProgramFiles (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
- cd C:\path\to\tikzit
- qmake
- nmake
+To get a portable directory, you can then (optionally) run:
+
+ > deploy-win.bat
@@ -29,48 +28,44 @@ You can alternatively build from the command line with mingw or Visual Studio, a
This should be buildable in Linux using a "standard" dev setup (gcc, flex, bison, make) as well as Qt. It has been tested with Qt 5.9, which is packaged with Ubuntu 18.04 (Bionic Beaver). The setup on Ubuntu is:
- $ sudo apt-get install flex bison qt5-default
+ $ sudo apt -y install flex bison qt5-default libpoppler-dev libpoppler-qt5-dev
After that, building is:
- $ qmake
+ $ qmake -r
$ make
+To get a portable directory, you can then (optionally) run:
+
+ ./deploy-linux.sh
+
Building on other distributions should be similar. For Qt setup, you can find instructions for <a href="https://wiki.qt.io/Install_Qt_5_on_openSUSE">openSUSE</a> and <a href="https://wiki.archlinux.org/index.php/qt">Arch Linux</a> on the Qt wiki.
## Building on MacOS
-You'll need developer tools and Qt5 installed. The latter can be installed using e.g. Homebrew, as follows:
+You'll need developer tools, Qt5, and Poppler (with Qt bindings) installed. You can install these via Homebrew with the following commands:
$ brew install qt5
+ $ brew install poppler --with-qt
-This doesn't add Qt binaries to the PATH by default, so you may wish to add this to your shell startup script:
+This doesn't add Qt binaries to the `$PATH` by default, so you may wish to either run:
- export PATH="/usr/local/opt/qt/bin:$PATH"
+ $ brew link --force qt5
-Then, TikZiT is built just like a normal Qt project:
+or add `/usr/local/opt/qt/bin` to your `$PATH`. Once this is done, TikZiT can be built from the command line via:
- $ qmake
+ $ qmake -r
$ make
+To bundle the required libraries into `tikzit.app` and create a `.dmg` file, you can additionally run:
+
+ $ ./deploy-osx.sh
+
On older systems (pre-10.11), you can build with Qt 5.6, which <a href="http://doc.qt.io/qt-5/supported-platforms-and-configurations.html">claims</a> to support Mac OS as far back as Mountain Lion. It is installable via <a href="https://www.macports.org">MacPorts</a>:
$ sudo port -N -k install qt56
$ export PATH=/opt/local/libexec/qt5/bin:$PATH
-Then, you should be able to run `qmake && make`, as above.
-
-
-
-
-## Building Poppler with Qt bindings
-
-Although TikZiT doesn't currently support PDF preview, it probably will in the near future via Poppler. Here's the instructions for building it as a developer.
-
-Poppler should be built from source to get the Qt5 bindings. If Qt is setup correctly, the configure script included with Poppler should enable these automatically. Also, note that clang needs to have C++11 features enabled to build successfully. TikZiT has been tested on MacOS with poppler-0.50.0 (available <a href="https://poppler.freedesktop.org/releases.html">here</a>), built with the following commands:
-
- $ CXXFLAGS="-std=c++11" ./configure
- $ CXXFLAGS="-std=c++11" make
-
+I have only tested this with TikZiT 2.0, so to install Poppler (required by TikZiT >= 2.1), you are on your own.
diff --git a/deploy-linux.sh b/deploy-linux.sh
index 3a30f3b..373092a 100755
--- a/deploy-linux.sh
+++ b/deploy-linux.sh
@@ -65,16 +65,25 @@ cp --no-dereference $LIBDIR/libQt5Svg.so* lib
cp --no-dereference $LIBDIR/libQt5Network.so* lib
cp --no-dereference $LIBDIR/libQt5Gui.so* lib
cp --no-dereference $LIBDIR/libQt5XcbQpa.so* lib
+cp --no-dereference $LIBDIR/libQt5Xml.so* lib
# add libicu, which is required by Qt5 for unicode support
cp --no-dereference $LIBDIR/libicuuc.so* lib
cp --no-dereference $LIBDIR/libicui18n.so* lib
cp --no-dereference $LIBDIR/libicudata.so* lib
+# add a couple of libraries which are not installed by default on Ubuntu
+cp --no-dereference $LIBDIR/libdouble-conversion.so* lib
+cp --no-dereference $LIBDIR/libxcb-xinerama.so* lib
+
# add openssl from the build system, as this seems to create some problems if the wrong version
cp --no-dereference $LIBDIR/libssl.so* lib
cp --no-dereference $LIBDIR/libcrypto.so* lib
+# add poppler libs
+cp --no-dereference $LIBDIR/libpoppler.so* lib
+cp --no-dereference $LIBDIR/libpoppler-qt5.so* lib
+
# add Qt plugins used by TikZiT
cp -R $PLUGINDIR/platforms plugins
cp -R $PLUGINDIR/imageformats plugins
diff --git a/deploy-osx.sh b/deploy-osx.sh
index 15d69f7..98043c0 100755
--- a/deploy-osx.sh
+++ b/deploy-osx.sh
@@ -1,5 +1,16 @@
# deploy the Mac app bundle. Note the bin/ directory
# of Qt should be in your PATH
-macdeployqt tikzit.app -dmg
+# copy in libraries and set (most) library paths
+macdeployqt tikzit.app
+
+# macdeployqt misses this path for some reason, so fix it
+cd tikzit.app/Contents/Frameworks
+install_name_tool -id "@executable_path/../Frameworks/libpoppler.83.dylib" libpoppler.83.dylib
+install_name_tool -change /usr/local/Cellar/poppler/0.72.0/lib/libpoppler.83.dylib "@executable_path/../Frameworks/libpoppler.83.dylib" libpoppler-qt5.1.dylib
+cd ../../..
+
+# create DMG
+hdiutil create -volname TikZiT -srcfolder tikzit.app -ov -format UDZO tikzit.dmg
+
diff --git a/deploy-win.bat b/deploy-win.bat
index 1c19f40..b9871f2 100755
--- a/deploy-win.bat
+++ b/deploy-win.bat
@@ -7,12 +7,9 @@ mkdir icons
copy ..\..\tikzfiles.reg .
copy ..\..\release\tikzit.exe .
copy ..\..\images\tikzdoc.ico icons\
-copy C:\Windows\System32\msvcp140.dll .
-copy C:\Windows\System32\vcruntime140.dll .
-copy C:\OpenSSL-Win64\bin\libeay32.dll .
-copy C:\OpenSSL-Win64\bin\ssleay32.dll .
+copy ..\..\win32-deps\bin\*.dll .
-windeployqt.exe --no-compiler-runtime --no-webkit2 --no-angle --no-opengl-sw --no-system-d3d-compiler --no-translations --no-quick-import .\tikzit.exe
+windeployqt.exe --xml --no-webkit2 --no-angle --no-opengl-sw --no-system-d3d-compiler --no-translations --no-quick-import .\tikzit.exe
cd ..
7z a -tzip tikzit.zip tikzit
diff --git a/images/dialog-accept.svg b/images/dialog-accept.svg
new file mode 100644
index 0000000..287b11e
--- /dev/null
+++ b/images/dialog-accept.svg
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg1306" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="48px" width="48px" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs id="defs1308">
+ <radialGradient id="radialGradient3976" gradientUnits="userSpaceOnUse" cy="40" cx="23.857" gradientTransform="matrix(1 0 0 .5 0 20)" r="17.143">
+ <stop id="stop4128" offset="0"/>
+ <stop id="stop4130" stop-opacity="0" offset="1"/>
+ </radialGradient>
+ <linearGradient id="linearGradient3980" y2="-8.5627" gradientUnits="userSpaceOnUse" x2="20.065" y1="53.836" x1="43.936">
+ <stop id="stop2481" stop-color="#ffe69b" offset="0"/>
+ <stop id="stop2483" stop-color="#fff" offset="1"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3982" y2="15.815" gradientUnits="userSpaceOnUse" x2="20.917" gradientTransform="matrix(1.003 0 0 1.003 -.071859 .019684)" y1="33.955" x1="21.994">
+ <stop id="stop3959" stop-color="#fffeff" stop-opacity=".33333" offset="0"/>
+ <stop id="stop3961" stop-color="#fffeff" stop-opacity=".21569" offset="1"/>
+ </linearGradient>
+ </defs>
+ <metadata id="metadata1311">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Rodney Dawes</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:contributor>
+ <cc:Agent>
+ <dc:title>Jakub Steiner, Garrett LeSage</dc:title>
+ </cc:Agent>
+ </dc:contributor>
+ <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/"/>
+ <dc:title/>
+ </cc:Work>
+ <cc:License rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
+ <cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
+ <cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/>
+ <cc:requires rdf:resource="http://web.resource.org/cc/Notice"/>
+ <cc:requires rdf:resource="http://web.resource.org/cc/Attribution"/>
+ <cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
+ <cc:requires rdf:resource="http://web.resource.org/cc/ShareAlike"/>
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer2">
+ <path id="path6548" opacity=".6" style="color:#000000" d="m41 40a17.143 8.5714 0 1 1 -34.286 0 17.143 8.5714 0 1 1 34.286 0z" transform="matrix(1.0706 0 0 .525 -.89276 22.5)" display="block" fill="url(#radialGradient3976)"/>
+ </g>
+ <g id="layer1">
+ <g id="g4006">
+ <path id="path1314" d="m46.857 23.929c0 12.9-10.457 23.357-23.357 23.357s-23.357-10.457-23.357-23.357 10.457-23.357 23.357-23.357 23.357 10.457 23.357 23.357z" transform="matrix(.92049 0 0 .92049 2.3685 .97408)" stroke="#4e9a06" stroke-width="1.0864" fill="#73d216"/>
+ <path id="path3560" opacity=".34659" d="m49.902 26.635c0 13.25-10.741 23.991-23.991 23.991s-23.991-10.741-23.991-23.991 10.741-23.991 23.991-23.991 23.991 10.741 23.991 23.991z" fill-opacity="0" transform="matrix(.85609 0 0 .85609 1.8183 .19777)" stroke="url(#linearGradient3980)" stroke-width="1.1681"/>
+ </g>
+ </g>
+ <g id="layer3">
+ <g id="text4967" fill="#eeeeec" stroke="#eeeeec">
+ <path id="path4984" style="" d="m14.707 25.178c0.62921 0.000015 1.1052 0.5163 1.4278 1.5489 0.64534 1.9361 1.1052 2.9041 1.3794 2.9041 0.20973 0.000012 0.42753-0.16133 0.65342-0.48402 4.5336-7.2602 8.7284-13.133 12.584-17.618 1.0003-1.1616 2.5895-1.7424 4.7676-1.7425 0.51625 0.0000313 0.86313 0.048433 1.0406 0.1452 0.17744 0.096834 0.26618 0.21784 0.26621 0.36301-0.000033 0.2259-0.26624 0.66959-0.79863 1.331-6.2277 7.4861-12.004 15.392-17.328 23.717-0.37109 0.58082-1.1294 0.87124-2.2749 0.87123-1.1617 0.000005-1.8473-0.0484-2.0571-0.1452-0.54856-0.242-1.1939-1.4762-1.9361-3.7027-0.83897-2.4685-1.2585-4.0173-1.2584-4.6466-0.000008-0.67761 0.56468-1.331 1.6941-1.9603 0.69375-0.3872 1.3068-0.5808 1.8393-0.58082"/>
+ </g>
+ </g>
+ <g id="layer4">
+ <path id="path3955" fill="url(#linearGradient3982)" d="m43.429 21.8c0 10.863-10.386-6.285-18.731 0.388-8.151 6.517-20.659 12.227-20.659 1.364 0.0004-11.118 8.722-21.431 19.585-21.431 10.863-0.0002 19.805 8.816 19.805 19.679z"/>
+ </g>
+</svg>
diff --git a/images/dialog-error.svg b/images/dialog-error.svg
new file mode 100644
index 0000000..9071b53
--- /dev/null
+++ b/images/dialog-error.svg
@@ -0,0 +1,316 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg1306"
+ sodipodi:version="0.32"
+ inkscape:version="0.43+devel"
+ sodipodi:docbase="/home/garrett/Source/tango-icon-theme/scalable/status"
+ sodipodi:docname="dialog-error.svg">
+ <defs
+ id="defs1308">
+ <linearGradient
+ id="linearGradient3957">
+ <stop
+ style="stop-color:#fffeff;stop-opacity:0.33333334;"
+ offset="0"
+ id="stop3959" />
+ <stop
+ style="stop-color:#fffeff;stop-opacity:0.21568628;"
+ offset="1"
+ id="stop3961" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2536">
+ <stop
+ style="stop-color:#a40000;stop-opacity:1;"
+ offset="0"
+ id="stop2538" />
+ <stop
+ style="stop-color:#ff1717;stop-opacity:1;"
+ offset="1"
+ id="stop2540" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2479">
+ <stop
+ style="stop-color:#ffe69b;stop-opacity:1;"
+ offset="0"
+ id="stop2481" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop2483" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4126"
+ inkscape:collect="always">
+ <stop
+ id="stop4128"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4130"
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0;" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4126"
+ id="radialGradient2169"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.000000,0.000000,0.000000,0.500000,1.899196e-14,20.00000)"
+ cx="23.857143"
+ cy="40.000000"
+ fx="23.857143"
+ fy="40.000000"
+ r="17.142857" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2479"
+ id="linearGradient2485"
+ x1="43.93581"
+ y1="53.835983"
+ x2="20.064686"
+ y2="-8.5626707"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2536"
+ id="linearGradient2542"
+ x1="36.917976"
+ y1="66.288063"
+ x2="19.071495"
+ y2="5.5410109"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2536"
+ id="linearGradient3046"
+ gradientUnits="userSpaceOnUse"
+ x1="36.917976"
+ y1="66.288063"
+ x2="19.071495"
+ y2="5.5410109" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2479"
+ id="linearGradient3048"
+ gradientUnits="userSpaceOnUse"
+ x1="43.93581"
+ y1="53.835983"
+ x2="20.064686"
+ y2="-8.5626707" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2536"
+ id="linearGradient3064"
+ gradientUnits="userSpaceOnUse"
+ x1="36.917976"
+ y1="66.288063"
+ x2="19.071495"
+ y2="5.5410109" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2479"
+ id="linearGradient3066"
+ gradientUnits="userSpaceOnUse"
+ x1="43.93581"
+ y1="53.835983"
+ x2="20.064686"
+ y2="-8.5626707" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3957"
+ id="linearGradient3963"
+ x1="21.993773"
+ y1="33.955299"
+ x2="20.917078"
+ y2="15.814602"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4126"
+ id="radialGradient3976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,0.5,1.893048e-14,20)"
+ cx="23.857143"
+ cy="40.000000"
+ fx="23.857143"
+ fy="40.000000"
+ r="17.142857" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2536"
+ id="linearGradient3978"
+ gradientUnits="userSpaceOnUse"
+ x1="36.917976"
+ y1="66.288063"
+ x2="19.071495"
+ y2="5.5410109" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2479"
+ id="linearGradient3980"
+ gradientUnits="userSpaceOnUse"
+ x1="43.93581"
+ y1="53.835983"
+ x2="20.064686"
+ y2="-8.5626707" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3957"
+ id="linearGradient3982"
+ gradientUnits="userSpaceOnUse"
+ x1="21.993773"
+ y1="33.955299"
+ x2="20.917078"
+ y2="15.814602" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="0.21568627"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="27.043297"
+ inkscape:cy="20.463852"
+ inkscape:current-layer="layer2"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="925"
+ inkscape:window-height="846"
+ inkscape:window-x="234"
+ inkscape:window-y="52"
+ inkscape:showpageshadow="false"
+ fill="#ef2929"
+ gridempspacing="4" />
+ <metadata
+ id="metadata1311">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Rodney Dawes</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:contributor>
+ <cc:Agent>
+ <dc:title>Jakub Steiner, Garrett LeSage</dc:title>
+ </cc:Agent>
+ </dc:contributor>
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
+ <dc:title>Dialog Error</dc:title>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Attribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="Shadow">
+ <path
+ inkscape:r_cy="true"
+ inkscape:r_cx="true"
+ transform="matrix(1.070555,0,0,0.525,-0.892755,22.5)"
+ d="M 41 40 A 17.142857 8.5714283 0 1 1 6.7142868,40 A 17.142857 8.5714283 0 1 1 41 40 z"
+ sodipodi:ry="8.5714283"
+ sodipodi:rx="17.142857"
+ sodipodi:cy="40"
+ sodipodi:cx="23.857143"
+ id="path6548"
+ style="opacity:0.6;color:#000000;fill:url(#radialGradient3976);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <g
+ id="g4006">
+ <path
+ transform="matrix(0.920488,0,0,0.920488,2.368532,0.97408)"
+ d="M 46.857143 23.928572 A 23.357143 23.357143 0 1 1 0.1428566,23.928572 A 23.357143 23.357143 0 1 1 46.857143 23.928572 z"
+ sodipodi:ry="23.357143"
+ sodipodi:rx="23.357143"
+ sodipodi:cy="23.928572"
+ sodipodi:cx="23.5"
+ id="path1314"
+ style="fill:url(#linearGradient3978);fill-opacity:1;fill-rule:nonzero;stroke:#b20000;stroke-width:1.08638;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ <path
+ transform="matrix(0.856093,0,0,0.856093,1.818275,0.197769)"
+ d="M 49.901535 26.635273 A 23.991123 23.991123 0 1 1 1.9192886,26.635273 A 23.991123 23.991123 0 1 1 49.901535 26.635273 z"
+ sodipodi:ry="23.991123"
+ sodipodi:rx="23.991123"
+ sodipodi:cy="26.635273"
+ sodipodi:cx="25.910412"
+ id="path3560"
+ style="opacity:0.34659089;fill:#cc0000;fill-opacity:0;stroke:url(#linearGradient3980);stroke-width:1.16809607;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ </g>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="Error Box">
+ <rect
+ inkscape:r_cy="true"
+ inkscape:r_cx="true"
+ style="fill:#efefef;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.73876643;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.8627451"
+ id="rect2070"
+ width="27.836435"
+ height="7.1735945"
+ x="10.078821"
+ y="19.164932"
+ transform="matrix(1.005876,0,0,1.115201,-0.138045,-2.372708)" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer4"
+ inkscape:label="Glossy Shine">
+ <path
+ transform="matrix(1.002994,0,0,1.002994,-7.185874e-2,1.968356e-2)"
+ sodipodi:nodetypes="czssc"
+ id="path3955"
+ d="M 43.370686,21.715486 C 43.370686,32.546102 33.016357,15.449178 24.695948,22.101874 C 16.569626,28.599385 4.0989837,34.292422 4.0989837,23.461806 C 4.0989837,12.377753 12.79438,2.0948032 23.625,2.0948032 C 34.455619,2.0948032 43.370686,10.884868 43.370686,21.715486 z "
+ style="fill:url(#linearGradient3982);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ </g>
+</svg>
diff --git a/images/loader.gif b/images/loader.gif
new file mode 100644
index 0000000..078b55f
--- /dev/null
+++ b/images/loader.gif
Binary files differ
diff --git a/images/loader@2x.gif b/images/loader@2x.gif
new file mode 100644
index 0000000..078b55f
--- /dev/null
+++ b/images/loader@2x.gif
Binary files differ
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
diff --git a/tex/sample/figures/fig.tikz b/tex/sample/figures/fig.tikz
index e485d9f..f454de2 100644
--- a/tex/sample/figures/fig.tikz
+++ b/tex/sample/figures/fig.tikz
@@ -1,14 +1,14 @@
-\begin{tikzpicture}
+\begin{tikzpicture}[tikzfig]
\begin{pgfonlayer}{nodelayer}
\node [style=red node] (0) at (0, 1) {};
\node [style=blue node 2] (1) at (1, 0) {};
\node [style=blue node] (2) at (-1, 0) {};
- \node [style=yellow square] (3) at (0, -1) {foo};
+ \node [style=yellow square] (3) at (0, -1) {\anglevec{\alpha}};
\end{pgfonlayer}
\begin{pgfonlayer}{edgelayer}
- \draw [in=-90, out=0] (3.center) to (1.center);
- \draw [bend right=45, looseness=1.25] (3.center) to (2.center);
- \draw (2.center) to (0.center);
- \draw (0.center) to (1.center);
+ \draw [in=-90, out=0] (3) to (1);
+ \draw [bend right] (3) to (2);
+ \draw (2) to (0);
+ \draw (0) to (1);
\end{pgfonlayer}
\end{tikzpicture}
diff --git a/tex/sample/sample.tex b/tex/sample/sample.tex
index 57ba88c..c6fb3ca 100644
--- a/tex/sample/sample.tex
+++ b/tex/sample/sample.tex
@@ -1,10 +1,12 @@
\documentclass{article}
\usepackage{tikzit}
\input{sample.tikzstyles}
-
+\input{sample.tikzdefs}
\begin{document}
+This is a demonstration of \texttt{tikzit.sty}, which provides some convenience macros for including \texttt{.tikz} files generated by TikZiT. Note this file is optional, however if you choose to omit it from your \LaTeX{} source, you should at least declare the layers, dummy properties, and \texttt{none} style from \texttt{tikzit.sty} for TikZiT figures to build correctly.
+
A centered tikz picture:
\ctikzfig{fig}
@@ -14,5 +16,24 @@ A tikz picture as part of mathematics:
\tikzfig{fig}
\end{equation}
+It is also possible to paste a \texttt{tikzpicture} directly from TikZiT, without using the \texttt{$\backslash$tikzfig} macro. In that case, the \texttt{tikzfig} option should be given to the \texttt{tikzpicture} environment to get the same baseline and scaling as the other figures:
+\[
+\begin{tikzpicture}[tikzfig]
+ \begin{pgfonlayer}{nodelayer}
+ \node [style=red node] (0) at (0, 1) {};
+ \node [style=blue node 2] (1) at (1, 0) {};
+ \node [style=blue node] (2) at (-1, 0) {};
+ \node [style=yellow square] (3) at (0, -1) {foo};
+ \end{pgfonlayer}
+ \begin{pgfonlayer}{edgelayer}
+ \draw [in=-90, out=0] (3) to (1);
+ \draw [bend right] (3) to (2);
+ \draw (2) to (0);
+ \draw (0) to (1);
+ \end{pgfonlayer}
+\end{tikzpicture}
+\]
+
+
\end{document}
diff --git a/tex/sample/sample.tikzdefs b/tex/sample/sample.tikzdefs
new file mode 100644
index 0000000..4fd5bc9
--- /dev/null
+++ b/tex/sample/sample.tikzdefs
@@ -0,0 +1,5 @@
+% Optional: use this file for definitions that should be used by TikZiT to generate
+% LaTeX preview. It can also be included in the paper.
+
+\usepackage{bm}
+\newcommand{\param}[1]{\ensuremath{\vec{\bm{#1}}}}
diff --git a/tex/sample/sample.tikzstyles b/tex/sample/sample.tikzstyles
index df70248..f32728c 100644
--- a/tex/sample/sample.tikzstyles
+++ b/tex/sample/sample.tikzstyles
@@ -4,9 +4,9 @@
% \tikzstyle{NAME}=[PROPERTY LIST]
% Node styles
-\tikzstyle{red node}=[fill=red, tikzit category=nodes]
-\tikzstyle{blue node}=[fill=blue]
-\tikzstyle{blue node 2}=[tikzit fill=green, fill=blue]
+\tikzstyle{red node}=[fill=red, tikzit category=nodes, shape=circle, draw=black]
+\tikzstyle{blue node}=[fill=blue, shape=circle, draw=black, tikzit category=nodes]
+\tikzstyle{green node}=[tikzit fill=green, fill=green, shape=circle, draw=black, tikzit category=nodes]
\tikzstyle{yellow square}=[draw=black, fill=yellow, shape=rectangle]
% Edge styles
diff --git a/tex/sample/tikzit.sty b/tex/sample/tikzit.sty
index b893a4a..5ca1b46 100644
--- a/tex/sample/tikzit.sty
+++ b/tex/sample/tikzit.sty
@@ -3,30 +3,40 @@
\usetikzlibrary{arrows}
\usetikzlibrary{shapes,shapes.geometric,shapes.misc}
-\tikzstyle{every picture}=[baseline=-0.25em,scale=0.5]
+% this style is applied by default to any tikzpicture included via \tikzfig
+\tikzstyle{tikzfig}=[baseline=-0.25em,scale=0.5]
+% these are dummy properties used by TikZiT, but ignored by LaTex
\pgfkeys{/tikz/tikzit fill/.initial=0}
\pgfkeys{/tikz/tikzit draw/.initial=0}
\pgfkeys{/tikz/tikzit shape/.initial=0}
\pgfkeys{/tikz/tikzit category/.initial=0}
+% standard layers used in .tikz files
+\pgfdeclarelayer{edgelayer}
+\pgfdeclarelayer{nodelayer}
+\pgfsetlayers{background,edgelayer,nodelayer,main}
+
+% style for blank nodes
+\tikzstyle{none}=[inner sep=0mm]
+
+% include a .tikz file
\newcommand{\tikzfig}[1]{%
+{\tikzstyle{every picture}=[tikzfig]
\IfFileExists{#1.tikz}
{\input{#1.tikz}}
{%
\IfFileExists{./figures/#1.tikz}
{\input{./figures/#1.tikz}}
{\tikz[baseline=-0.5em]{\node[draw=red,font=\color{red},fill=red!10!white] {\textit{#1}};}}%
- }%
+ }}%
}
+
+% the same as \tikzfig, but in a {center} environment
\newcommand{\ctikzfig}[1]{%
\begin{center}\rm
\tikzfig{#1}
\end{center}}
-\pgfdeclarelayer{edgelayer}
-\pgfdeclarelayer{nodelayer}
-\pgfsetlayers{background,edgelayer,nodelayer,main}
-\tikzstyle{none}=[inner sep=0mm]
+% fix strange self-loops, which are PGF/TikZ default
\tikzstyle{every loop}=[]
-\tikzstyle{mark coordinate}=[inner sep=0pt,outer sep=0pt,minimum size=3pt,fill=black,circle] \ No newline at end of file
diff --git a/tikzit.pro b/tikzit.pro
index 9fa1d15..692433d 100644
--- a/tikzit.pro
+++ b/tikzit.pro
@@ -1,11 +1,9 @@
-#-------------------------------------------------
-#
-# Project created by QtCreator 2017-01-11T17:30:16
-#
-#-------------------------------------------------
+# CONFIG += debug
QT += core gui widgets network
+VERSION = 2.1
+
test {
CONFIG += testcase
}
@@ -29,6 +27,8 @@ win32:RC_ICONS += images/tikzdoc.ico
macx:ICON = images/tikzit.icns
# linux-g++:QMAKE_CXXFLAGS += -Wsuggest-override
+QMAKE_INFO_PLIST = Info.plist
+
# Qt 5.8 and above drop support for Mountain Lion
contains(QT_VERSION, ^5\\.[5-7].*) {
macx:QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.8
@@ -75,7 +75,14 @@ SOURCES += src/gui/mainwindow.cpp \
src/data/tikzstyles.cpp \
src/data/style.cpp \
src/gui/styleeditor.cpp \
- src/data/stylelist.cpp
+ src/data/stylelist.cpp \
+ src/gui/previewwindow.cpp \
+ src/gui/latexprocess.cpp \
+ src/data/pdfdocument.cpp \
+ src/gui/exportdialog.cpp \
+ src/data/delimitedstringvalidator.cpp \
+ src/gui/delimitedstringitemdelegate.cpp \
+ src/gui/preferencedialog.cpp
HEADERS += src/gui/mainwindow.h \
src/gui/toolpalette.h \
@@ -101,16 +108,39 @@ HEADERS += src/gui/mainwindow.h \
src/data/tikzstyles.h \
src/data/style.h \
src/gui/styleeditor.h \
- src/data/stylelist.h
+ src/data/stylelist.h \
+ src/gui/previewwindow.h \
+ src/gui/latexprocess.h \
+ src/data/pdfdocument.h \
+ src/gui/exportdialog.h \
+ src/data/delimitedstringvalidator.h \
+ src/gui/delimitedstringitemdelegate.h \
+ src/gui/preferencedialog.h
FORMS += src/gui/mainwindow.ui \
src/gui/propertypalette.ui \
src/gui/mainmenu.ui \
src/gui/stylepalette.ui \
- src/gui/styleeditor.ui
+ src/gui/styleeditor.ui \
+ src/gui/previewwindow.ui \
+ src/gui/exportdialog.ui \
+ src/gui/preferencedialog.ui
INCLUDEPATH += src src/gui src/data
+# link to pre-compiled poppler libs on windows
+win32 {
+ INCLUDEPATH += win32-deps/include
+ LIBS += -L"$$PWD/win32-deps/bin"
+}
+
+macx {
+ INCLUDEPATH += /usr/local/opt/poppler/include
+ LIBS += -L/usr/local/opt/poppler/lib
+}
+
+LIBS += -lpoppler-qt5
+
DISTFILES +=
RESOURCES += tikzit.qrc
diff --git a/tikzit.qrc b/tikzit.qrc
index 0484c2d..4b4defb 100644
--- a/tikzit.qrc
+++ b/tikzit.qrc
@@ -8,6 +8,11 @@
<file>images/refresh.svg</file>
<file>images/tikzit.png</file>
<file>images/text-x-generic_with_pencil.svg</file>
+ <file>tex/sample/tikzit.sty</file>
+ <file>images/loader.gif</file>
+ <file>images/loader@2x.gif</file>
+ <file>images/dialog-accept.svg</file>
+ <file>images/dialog-error.svg</file>
</qresource>
<qresource prefix="/qt/etc">
<file>qt.conf</file>