summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
authorAleks Kissinger <aleks0@gmail.com>2018-01-04 16:00:52 +0100
committerAleks Kissinger <aleks0@gmail.com>2018-01-04 16:00:52 +0100
commit738ecbd5fad2b46836bfd6a94aeebf165ae2bbca (patch)
treedf04709807cc9ec8481a3ebc7d80ac25e5b2f457 /src/gui
parent0421a96749743868554d44585050b1b3d04864d2 (diff)
relocated source code to the root
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/commands.cpp0
-rw-r--r--src/gui/commands.h4
-rw-r--r--src/gui/edgeitem.cpp131
-rw-r--r--src/gui/edgeitem.h36
-rw-r--r--src/gui/mainmenu.cpp96
-rw-r--r--src/gui/mainmenu.h43
-rw-r--r--src/gui/mainmenu.ui187
-rw-r--r--src/gui/mainwindow.cpp99
-rw-r--r--src/gui/mainwindow.h48
-rw-r--r--src/gui/mainwindow.ui199
-rw-r--r--src/gui/nodeitem.cpp124
-rw-r--r--src/gui/nodeitem.h32
-rw-r--r--src/gui/propertypalette.cpp43
-rw-r--r--src/gui/propertypalette.h28
-rw-r--r--src/gui/propertypalette.ui30
-rw-r--r--src/gui/tikzscene.cpp384
-rw-r--r--src/gui/tikzscene.h62
-rw-r--r--src/gui/tikzview.cpp84
-rw-r--r--src/gui/tikzview.h32
-rw-r--r--src/gui/toolpalette.cpp50
-rw-r--r--src/gui/toolpalette.h34
-rw-r--r--src/gui/undocommands.cpp162
-rw-r--r--src/gui/undocommands.h80
23 files changed, 1988 insertions, 0 deletions
diff --git a/src/gui/commands.cpp b/src/gui/commands.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/gui/commands.cpp
diff --git a/src/gui/commands.h b/src/gui/commands.h
new file mode 100644
index 0000000..73bfaa7
--- /dev/null
+++ b/src/gui/commands.h
@@ -0,0 +1,4 @@
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+#endif // COMMANDS_H
diff --git a/src/gui/edgeitem.cpp b/src/gui/edgeitem.cpp
new file mode 100644
index 0000000..497fa07
--- /dev/null
+++ b/src/gui/edgeitem.cpp
@@ -0,0 +1,131 @@
+#include "tikzit.h"
+#include "edgeitem.h"
+
+#include <QPainterPath>
+#include <QPen>
+
+EdgeItem::EdgeItem(Edge *edge)
+{
+ _edge = edge;
+ setFlag(QGraphicsItem::ItemIsSelectable);
+
+ QPen pen(Qt::black);
+ pen.setWidth(2);
+ setPen(pen);
+ _cp1Item = new QGraphicsEllipseItem(this);
+ _cp1Item->setParentItem(this);
+ _cp1Item->setRect(GLOBAL_SCALEF * (-0.05), GLOBAL_SCALEF * (-0.05),
+ GLOBAL_SCALEF * 0.1, GLOBAL_SCALEF * 0.1);
+ _cp1Item->setVisible(false);
+
+ _cp2Item = new QGraphicsEllipseItem(this);
+ _cp2Item->setParentItem(this);
+ _cp2Item->setRect(GLOBAL_SCALEF * (-0.05), GLOBAL_SCALEF * (-0.05),
+ GLOBAL_SCALEF * 0.1, GLOBAL_SCALEF * 0.1);
+ _cp2Item->setVisible(false);
+
+ readPos();
+}
+
+void EdgeItem::readPos()
+{
+ //_edge->setAttributesFromData();
+ _edge->updateControls();
+ QPainterPath path;
+
+ path.moveTo (toScreen(_edge->tail()));
+ path.cubicTo(toScreen(_edge->cp1()),
+ toScreen(_edge->cp2()),
+ toScreen(_edge->head()));
+ setPath(path);
+
+ _cp1Item->setPos(toScreen(_edge->cp1()));
+ _cp2Item->setPos(toScreen(_edge->cp2()));
+}
+
+void EdgeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ //QGraphicsPathItem::paint(painter, option, widget);
+ painter->setPen(pen());
+ painter->setBrush(Qt::NoBrush);
+ painter->drawPath(path());
+
+ if (isSelected()) {
+ QColor draw;
+ QColor draw1;
+ QColor fill;
+
+ if (_edge->basicBendMode()) {
+ draw = Qt::blue;
+ draw1 = QColor(100,100,255,100);
+ fill = QColor(200,200,255,50);
+ } else {
+ draw = Qt::darkGreen;
+ draw1 = QColor(0, 150, 0, 50);
+ fill = QColor(200,255,200,150);
+ }
+
+ painter->setPen(QPen(draw1));
+
+ float r = GLOBAL_SCALEF * _edge->cpDist();
+ painter->drawEllipse(toScreen(_edge->source()->point()), r, r);
+ painter->drawEllipse(toScreen(_edge->target()->point()), r, r);
+
+ painter->setPen(QPen(draw));
+ painter->setBrush(QBrush(fill));
+
+ painter->drawLine(toScreen(_edge->tail()), toScreen(_edge->cp1()));
+ painter->drawLine(toScreen(_edge->head()), toScreen(_edge->cp2()));
+
+ //painter->drawEllipse(toScreen(_edge->cp1()), r, r);
+ //painter->drawEllipse(toScreen(_edge->cp2()), r, r);
+
+ _cp1Item->setPen(QPen(draw));
+ _cp1Item->setBrush(QBrush(fill));
+ _cp1Item->setVisible(true);
+
+ _cp2Item->setPen(QPen(draw));
+ _cp2Item->setBrush(QBrush(fill));
+ _cp2Item->setVisible(true);
+
+ r = GLOBAL_SCALEF * 0.05;
+ painter->setPen(QPen(Qt::black));
+ painter->setBrush(QBrush(QColor(255,255,255,200)));
+ painter->drawEllipse(toScreen(_edge->mid()), r, r);
+ } else {
+ _cp1Item->setVisible(false);
+ _cp2Item->setVisible(false);
+ }
+}
+
+QRectF EdgeItem::boundingRect() const
+{
+ float r = GLOBAL_SCALEF * (_edge->cpDist() + 0.2);
+ return shape().boundingRect().adjusted(-r,-r,r,r);
+}
+
+QPainterPath EdgeItem::shape() const
+{
+ // get the shape of the edge, and expand a bit to make selection easier
+ QPainterPath oldShape = QGraphicsPathItem::shape();
+ QPainterPathStroker stroker;
+ stroker.setWidth(5);
+ stroker.setJoinStyle(Qt::MiterJoin);
+ QPainterPath newShape = (stroker.createStroke(oldShape) + oldShape).simplified();
+ return newShape;
+}
+
+Edge *EdgeItem::edge() const
+{
+ return _edge;
+}
+
+QGraphicsEllipseItem *EdgeItem::cp1Item() const
+{
+ return _cp1Item;
+}
+
+QGraphicsEllipseItem *EdgeItem::cp2Item() const
+{
+ return _cp2Item;
+}
diff --git a/src/gui/edgeitem.h b/src/gui/edgeitem.h
new file mode 100644
index 0000000..b017265
--- /dev/null
+++ b/src/gui/edgeitem.h
@@ -0,0 +1,36 @@
+/**
+ * A QGraphicsItem that handles drawing a single edge.
+ */
+
+#ifndef EDGEITEM_H
+#define EDGEITEM_H
+
+#include "edge.h"
+
+#include <QObject>
+#include <QGraphicsPathItem>
+#include <QPainter>
+#include <QStyleOptionGraphicsItem>
+#include <QWidget>
+#include <QGraphicsEllipseItem>
+
+class EdgeItem : public QGraphicsPathItem
+{
+public:
+ EdgeItem(Edge *edge);
+ void readPos();
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget);
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+ Edge *edge() const;
+ QGraphicsEllipseItem *cp1Item() const;
+ QGraphicsEllipseItem *cp2Item() const;
+
+private:
+ Edge *_edge;
+ QGraphicsEllipseItem *_cp1Item;
+ QGraphicsEllipseItem *_cp2Item;
+};
+
+#endif // EDGEITEM_H
diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp
new file mode 100644
index 0000000..c9e83ba
--- /dev/null
+++ b/src/gui/mainmenu.cpp
@@ -0,0 +1,96 @@
+#include "mainmenu.h"
+#include "tikzit.h"
+
+MainMenu::MainMenu()
+{
+ ui.setupUi(this);
+}
+
+// File
+void MainMenu::on_actionNew_triggered()
+{
+ tikzit->newDoc();
+}
+
+void MainMenu::on_actionOpen_triggered()
+{
+ tikzit->open();
+}
+
+void MainMenu::on_actionClose_triggered()
+{
+ // TODO
+}
+
+void MainMenu::on_actionSave_triggered()
+{
+ // TODO
+}
+
+void MainMenu::on_actionSave_As_triggered()
+{
+ // TODO
+}
+
+
+// Edit
+void MainMenu::on_actionUndo_triggered()
+{
+ if (tikzit->activeWindow() != 0)
+ tikzit->activeWindow()->tikzDocument()->undoStack()->undo();
+}
+
+void MainMenu::on_actionRedo_triggered()
+{
+ if (tikzit->activeWindow() != 0)
+ tikzit->activeWindow()->tikzDocument()->undoStack()->redo();
+}
+
+void MainMenu::on_actionCut_triggered()
+{
+ // TODO
+}
+
+void MainMenu::on_actionCopy_triggered()
+{
+ // TODO
+}
+
+void MainMenu::on_actionPaste_triggered()
+{
+ // TODO
+}
+
+void MainMenu::on_actionDelete_triggered()
+{
+ // TODO
+}
+
+void MainMenu::on_actionSelect_All_triggered()
+{
+ // TODO
+}
+
+void MainMenu::on_actionDeselect_All_triggered()
+{
+ // TODO
+}
+
+
+// Tikz
+void MainMenu::on_actionParse_triggered()
+{
+ // TODO
+}
+
+
+// View
+void MainMenu::on_actionZoom_In_triggered()
+{
+ if (tikzit->activeWindow() != 0) tikzit->activeWindow()->tikzView()->zoomIn();
+}
+
+void MainMenu::on_actionZoom_Out_triggered()
+{
+ if (tikzit->activeWindow() != 0) tikzit->activeWindow()->tikzView()->zoomOut();
+}
diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h
new file mode 100644
index 0000000..d85e271
--- /dev/null
+++ b/src/gui/mainmenu.h
@@ -0,0 +1,43 @@
+#ifndef MAINMENU_H
+#define MAINMENU_H
+
+#include "ui_mainmenu.h"
+
+#include <QMenuBar>
+
+class MainMenu : public QMenuBar
+{
+ Q_OBJECT
+public:
+ MainMenu();
+
+private:
+ Ui::MainMenu ui;
+
+public slots:
+ // File
+ void on_actionNew_triggered();
+ void on_actionOpen_triggered();
+ void on_actionClose_triggered();
+ void on_actionSave_triggered();
+ void on_actionSave_As_triggered();
+
+ // Edit
+ void on_actionUndo_triggered();
+ void on_actionRedo_triggered();
+ void on_actionCut_triggered();
+ void on_actionCopy_triggered();
+ void on_actionPaste_triggered();
+ void on_actionDelete_triggered();
+ void on_actionSelect_All_triggered();
+ void on_actionDeselect_All_triggered();
+
+ // Tikz
+ void on_actionParse_triggered();
+
+ // View
+ void on_actionZoom_In_triggered();
+ void on_actionZoom_Out_triggered();
+};
+
+#endif // MAINMENU_H
diff --git a/src/gui/mainmenu.ui b/src/gui/mainmenu.ui
new file mode 100644
index 0000000..c9b6f44
--- /dev/null
+++ b/src/gui/mainmenu.ui
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainMenu</class>
+ <widget class="QMenuBar" name="MainMenu">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>476</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ <addaction name="actionNew"/>
+ <addaction name="actionOpen"/>
+ <addaction name="separator"/>
+ <addaction name="actionClose"/>
+ <addaction name="actionSave"/>
+ <addaction name="actionSave_As"/>
+ </widget>
+ <widget class="QMenu" name="menuEdit">
+ <property name="title">
+ <string>Edit</string>
+ </property>
+ <addaction name="actionUndo"/>
+ <addaction name="actionRedo"/>
+ <addaction name="separator"/>
+ <addaction name="actionCut"/>
+ <addaction name="actionCopy"/>
+ <addaction name="actionPaste"/>
+ <addaction name="actionDelete"/>
+ <addaction name="separator"/>
+ <addaction name="actionSelect_All"/>
+ <addaction name="actionDeselect_All"/>
+ </widget>
+ <widget class="QMenu" name="menuTikz">
+ <property name="title">
+ <string>Tikz</string>
+ </property>
+ <addaction name="actionParse"/>
+ </widget>
+ <widget class="QMenu" name="menuView">
+ <property name="title">
+ <string>View</string>
+ </property>
+ <addaction name="actionZoom_In"/>
+ <addaction name="actionZoom_Out"/>
+ </widget>
+ <action name="actionNew">
+ <property name="text">
+ <string>New...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+N</string>
+ </property>
+ </action>
+ <action name="actionOpen">
+ <property name="text">
+ <string>Open...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+O</string>
+ </property>
+ </action>
+ <action name="actionClose">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+W</string>
+ </property>
+ </action>
+ <action name="actionSave">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+S</string>
+ </property>
+ </action>
+ <action name="actionSave_As">
+ <property name="text">
+ <string>Save As...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+S</string>
+ </property>
+ </action>
+ <action name="actionUndo">
+ <property name="text">
+ <string>Undo</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Z</string>
+ </property>
+ </action>
+ <action name="actionRedo">
+ <property name="text">
+ <string>Redo</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+Z</string>
+ </property>
+ </action>
+ <action name="actionCut">
+ <property name="text">
+ <string>Cut</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+X</string>
+ </property>
+ </action>
+ <action name="actionCopy">
+ <property name="text">
+ <string>Copy</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+C</string>
+ </property>
+ </action>
+ <action name="actionPaste">
+ <property name="text">
+ <string>Paste</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+V</string>
+ </property>
+ </action>
+ <action name="actionDelete">
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ <property name="shortcut">
+ <string>Backspace</string>
+ </property>
+ </action>
+ <action name="actionSelect_All">
+ <property name="text">
+ <string>Select All</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+A</string>
+ </property>
+ </action>
+ <action name="actionDeselect_All">
+ <property name="text">
+ <string>Deselect All</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+D</string>
+ </property>
+ </action>
+ <action name="actionParse">
+ <property name="text">
+ <string>Parse</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+T</string>
+ </property>
+ </action>
+ <action name="actionZoom_In">
+ <property name="text">
+ <string>Zoom In</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+=</string>
+ </property>
+ </action>
+ <action name="actionZoom_Out">
+ <property name="text">
+ <string>Zoom Out</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+-</string>
+ </property>
+ </action>
+ <addaction name="menuFile"/>
+ <addaction name="menuEdit"/>
+ <addaction name="menuView"/>
+ <addaction name="menuTikz"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp
new file mode 100644
index 0000000..19b6a59
--- /dev/null
+++ b/src/gui/mainwindow.cpp
@@ -0,0 +1,99 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+#include "tikzgraphassembler.h"
+#include "toolpalette.h"
+#include "tikzit.h"
+
+#include <QDebug>
+#include <QFile>
+#include <QList>
+#include <QSettings>
+#include <QMessageBox>
+#include <QFileDialog>
+
+int MainWindow::_numWindows = 0;
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow)
+{
+ _windowId = _numWindows;
+ _numWindows++;
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ _tikzDocument = new TikzDocument(this);
+ _tikzScene = new TikzScene(_tikzDocument, this);
+ ui->tikzView->setScene(_tikzScene);
+ _fileName = "";
+ _pristine = true;
+
+ // initially, the source view should be collapsed
+ QList<int> sz = ui->splitter->sizes();
+ sz[0] = sz[0] + sz[1];
+ sz[1] = 0;
+ ui->splitter->setSizes(sz);
+}
+
+MainWindow::~MainWindow()
+{
+ tikzit->removeWindow(this);
+ delete ui;
+}
+
+void MainWindow::open(QString fileName)
+{
+ _pristine = false;
+ _tikzDocument->open(fileName);
+ ui->tikzSource->setText(_tikzDocument->tikz());
+
+
+ if (_tikzDocument->parseSuccess()) {
+ statusBar()->showMessage("TiKZ parsed successfully", 2000);
+ setWindowTitle("TiKZiT - " + _tikzDocument->shortName());
+ _tikzScene->setTikzDocument(_tikzDocument);
+ } else {
+ statusBar()->showMessage("Cannot read TiKZ source");
+ }
+
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ //qDebug() << "got close event";
+ QMainWindow::closeEvent(event);
+}
+
+void MainWindow::changeEvent(QEvent *event)
+{
+ if (event->type() == QEvent::ActivationChange && isActiveWindow()) {
+ tikzit->setActiveWindow(this);
+ }
+ QMainWindow::changeEvent(event);
+}
+
+TikzDocument *MainWindow::tikzDocument() const
+{
+ return _tikzDocument;
+}
+
+TikzScene *MainWindow::tikzScene() const
+{
+ return _tikzScene;
+}
+
+int MainWindow::windowId() const
+{
+ return _windowId;
+}
+
+TikzView *MainWindow::tikzView() const
+{
+ return ui->tikzView;
+}
+
+bool MainWindow::pristine() const
+{
+ return _pristine;
+}
+
+
diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h
new file mode 100644
index 0000000..f27677a
--- /dev/null
+++ b/src/gui/mainwindow.h
@@ -0,0 +1,48 @@
+/**
+ * A top-level window, which contains a single TikzDocument.
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "tikzscene.h"
+#include "tikzview.h"
+#include "graph.h"
+#include "tikzdocument.h"
+
+#include <QMainWindow>
+#include <QGraphicsView>
+
+namespace Ui {
+class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+ void open(QString fileName);
+ bool pristine() const;
+ int windowId() const;
+ TikzView *tikzView() const;
+ TikzScene *tikzScene() const;
+ TikzDocument *tikzDocument() const;
+
+protected:
+ void closeEvent(QCloseEvent *event);
+ void changeEvent(QEvent *event);
+private:
+ TikzScene *_tikzScene;
+ TikzDocument *_tikzDocument;
+ Ui::MainWindow *ui;
+ QString _fileName;
+ bool _pristine;
+ int _windowId;
+ static int _numWindows;
+};
+
+#endif // MAINWINDOW_H
diff --git a/src/gui/mainwindow.ui b/src/gui/mainwindow.ui
new file mode 100644
index 0000000..56a5c2d
--- /dev/null
+++ b/src/gui/mainwindow.ui
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>640</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>TikZiT - untitled</string>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <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="QSplitter" name="splitter">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <widget class="TikzView" name="tikzView"/>
+ <widget class="QTextEdit" name="tikzSource">
+ <property name="font">
+ <font>
+ <family>Courier New</family>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="lineWrapMode">
+ <enum>QTextEdit::NoWrap</enum>
+ </property>
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Courier New'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'.SF NS Text'; font-size:13pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="tabStopWidth">
+ <number>20</number>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ <action name="actionNew">
+ <property name="text">
+ <string>New...</string>
+ </property>
+ </action>
+ <action name="actionOpen">
+ <property name="text">
+ <string>Open...</string>
+ </property>
+ </action>
+ <action name="actionClose">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </action>
+ <action name="actionSave">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ </action>
+ <action name="actionSave_As">
+ <property name="text">
+ <string>Save As...</string>
+ </property>
+ </action>
+ <action name="actionUndo">
+ <property name="text">
+ <string>Undo</string>
+ </property>
+ </action>
+ <action name="actionRedo">
+ <property name="text">
+ <string>Redo</string>
+ </property>
+ </action>
+ <action name="actionCut">
+ <property name="text">
+ <string>Cut</string>
+ </property>
+ </action>
+ <action name="actionCopy">
+ <property name="text">
+ <string>Copy</string>
+ </property>
+ </action>
+ <action name="actionPase">
+ <property name="text">
+ <string>Paste</string>
+ </property>
+ </action>
+ <action name="actionDelete">
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </action>
+ <action name="actionSelect_All">
+ <property name="text">
+ <string>Select All</string>
+ </property>
+ </action>
+ <action name="actionDeselect_All">
+ <property name="text">
+ <string>Deselect All</string>
+ </property>
+ </action>
+ <action name="actionParse">
+ <property name="text">
+ <string>Parse</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+T</string>
+ </property>
+ </action>
+ <action name="actionZoom_In">
+ <property name="text">
+ <string>Zoom In</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+=</string>
+ </property>
+ </action>
+ <action name="actionZoom_Out">
+ <property name="text">
+ <string>Zoom Out</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+-</string>
+ </property>
+ </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+ <customwidget>
+ <class>TikzView</class>
+ <extends>QGraphicsView</extends>
+ <header>tikzview.h</header>
+ <slots>
+ <slot>zoomIn()</slot>
+ <slot>zoomOut()</slot>
+ </slots>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>actionZoom_In</sender>
+ <signal>triggered()</signal>
+ <receiver>tikzView</receiver>
+ <slot>zoomIn()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>-1</x>
+ <y>-1</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>237</x>
+ <y>103</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>actionZoom_Out</sender>
+ <signal>triggered()</signal>
+ <receiver>tikzView</receiver>
+ <slot>zoomOut()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>-1</x>
+ <y>-1</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>237</x>
+ <y>103</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/nodeitem.cpp b/src/gui/nodeitem.cpp
new file mode 100644
index 0000000..71226f3
--- /dev/null
+++ b/src/gui/nodeitem.cpp
@@ -0,0 +1,124 @@
+#include "tikzit.h"
+#include "nodeitem.h"
+#include "tikzscene.h"
+#include <cmath>
+
+#include <QPen>
+#include <QApplication>
+#include <QBrush>
+#include <QDebug>
+#include <QFont>
+#include <QFontMetrics>
+#include <QPainterPathStroker>
+
+NodeItem::NodeItem(Node *node)
+{
+ _node = node;
+ setFlag(QGraphicsItem::ItemIsSelectable);
+ setFlag(QGraphicsItem::ItemIsMovable);
+ setFlag(QGraphicsItem::ItemSendsGeometryChanges);
+ readPos();
+}
+
+void NodeItem::readPos()
+{
+ setPos(toScreen(_node->point()));
+}
+
+void NodeItem::writePos()
+{
+ _node->setPoint(fromScreen(pos()));
+}
+
+QRectF NodeItem::labelRect() const {
+ QString label = _node->label();
+ //QFont f("Courier", 9);
+ QFontMetrics fm(Tikzit::LABEL_FONT);
+
+ QRectF rect = fm.boundingRect(label);
+ //rect.adjust(-2,-2,2,2);
+ rect.moveCenter(QPointF(0,0));
+ return rect;
+}
+
+void NodeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ if (_node->style()->isNone()) {
+ QColor c(180,180,200);
+ painter->setPen(QPen(c));
+ painter->setBrush(QBrush(c));
+ painter->drawEllipse(QPointF(0,0), 1,1);
+
+ QPen pen(QColor(180,180,220));
+ QVector<qreal> p;
+ p << 2.0 << 2.0;
+ pen.setDashPattern(p);
+ painter->setPen(pen);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawPath(shape());
+ } else {
+ QPen pen(_node->style()->strokeColor);
+ pen.setWidth(_node->style()->strokeThickness);
+ painter->setPen(pen);
+ painter->setBrush(QBrush(_node->style()->fillColor));
+ painter->drawPath(shape());
+ }
+
+ if (_node->label() != "") {
+ QRectF rect = labelRect();
+ QPen pen(QColor(200,0,0,120));
+ QVector<qreal> d;
+ d << 2.0 << 2.0;
+ pen.setDashPattern(d);
+ painter->setPen(pen);
+ painter->setBrush(QBrush(QColor(255,255,100,120)));
+ painter->drawRect(rect);
+
+ painter->setPen(QPen(Qt::black));
+ painter->setFont(Tikzit::LABEL_FONT);
+ painter->drawText(rect, Qt::AlignCenter, _node->label());
+ }
+
+ if (isSelected()) {
+ QPainterPath sh = shape();
+ QPainterPathStroker stroker;
+ stroker.setWidth(4);
+ QPainterPath outline = (stroker.createStroke(sh) + sh).simplified();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(QBrush(QColor(150,200,255,100)));
+ painter->drawPath(outline);
+ }
+
+}
+
+QPainterPath NodeItem::shape() const
+{
+ QPainterPath path;
+ path.addEllipse(QPointF(0,0), GLOBAL_SCALEF * 0.1, GLOBAL_SCALEF * 0.1);
+ return path;
+}
+
+QRectF NodeItem::boundingRect() const
+{
+ QRectF r = labelRect();
+ return r.united(shape().boundingRect()).adjusted(-4,-4,4,4);
+}
+
+Node *NodeItem::node() const
+{
+ return _node;
+}
+
+QVariant NodeItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ if (change == ItemPositionChange) {
+ QPointF newPos = value.toPointF();
+ int gridSize = GLOBAL_SCALE / 8;
+ QPointF gridPos(round(newPos.x()/gridSize)*gridSize, round(newPos.y()/gridSize)*gridSize);
+ _node->setPoint(fromScreen(gridPos));
+
+ return gridPos;
+ } else {
+ return QGraphicsItem::itemChange(change, value);
+ }
+}
diff --git a/src/gui/nodeitem.h b/src/gui/nodeitem.h
new file mode 100644
index 0000000..9a3edb0
--- /dev/null
+++ b/src/gui/nodeitem.h
@@ -0,0 +1,32 @@
+/**
+ * A QGraphicsItem that handles drawing a single node.
+ */
+
+#ifndef NODEITEM_H
+#define NODEITEM_H
+
+#include "node.h"
+
+#include <QObject>
+#include <QGraphicsItem>
+#include <QPainterPath>
+#include <QRectF>
+
+class NodeItem : public QGraphicsItem
+{
+public:
+ NodeItem(Node *node);
+ void readPos();
+ void writePos();
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *);
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+ QPainterPath shape() const;
+ QRectF boundingRect() const;
+ Node *node() const;
+
+private:
+ Node *_node;
+ QRectF labelRect() const;
+};
+
+#endif // NODEITEM_H
diff --git a/src/gui/propertypalette.cpp b/src/gui/propertypalette.cpp
new file mode 100644
index 0000000..5fc763f
--- /dev/null
+++ b/src/gui/propertypalette.cpp
@@ -0,0 +1,43 @@
+#include "propertypalette.h"
+#include "graphelementdata.h"
+#include "ui_propertypalette.h"
+
+#include <QModelIndex>
+#include <QDebug>
+#include <QCloseEvent>
+#include <QSettings>
+
+PropertyPalette::PropertyPalette(QWidget *parent) :
+ QDockWidget(parent),
+ ui(new Ui::PropertyPalette)
+{
+ setWindowFlags(Qt::Window
+ | Qt::CustomizeWindowHint
+ | Qt::WindowTitleHint);
+ //setFocusPolicy(Qt::NoFocus);
+ ui->setupUi(this);
+ GraphElementData *d = new GraphElementData();
+ d->setProperty("key 1", "value 1");
+ d->setAtom("atom 1");
+ d->setProperty("key 2", "value 2");
+
+ QModelIndex i = d->index(0,0);
+ ui->treeView->setModel(d);
+
+ QSettings settings("tikzit", "tikzit");
+ QVariant geom = settings.value("property-palette-geometry");
+ if (geom != QVariant()) {
+ restoreGeometry(geom.toByteArray());
+ }
+}
+
+PropertyPalette::~PropertyPalette()
+{
+ delete ui;
+}
+
+void PropertyPalette::closeEvent(QCloseEvent *event) {
+ QSettings settings("tikzit", "tikzit");
+ settings.setValue("property-palette-geometry", saveGeometry());
+ QDockWidget::closeEvent(event);
+}
diff --git a/src/gui/propertypalette.h b/src/gui/propertypalette.h
new file mode 100644
index 0000000..7910d70
--- /dev/null
+++ b/src/gui/propertypalette.h
@@ -0,0 +1,28 @@
+/**
+ * Enables the user to edit properties of the graph, as well as the selected node/edge.
+ */
+
+#ifndef PROPERTYPALETTE_H
+#define PROPERTYPALETTE_H
+
+#include <QDockWidget>
+
+namespace Ui {
+class PropertyPalette;
+}
+
+class PropertyPalette : public QDockWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PropertyPalette(QWidget *parent = 0);
+ ~PropertyPalette();
+
+protected:
+ void closeEvent(QCloseEvent *event);
+private:
+ Ui::PropertyPalette *ui;
+};
+
+#endif // PROPERTYPALETTE_H
diff --git a/src/gui/propertypalette.ui b/src/gui/propertypalette.ui
new file mode 100644
index 0000000..83d586e
--- /dev/null
+++ b/src/gui/propertypalette.ui
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PropertyPalette</class>
+ <widget class="QDockWidget" name="PropertyPalette">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>194</width>
+ <height>341</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Properties</string>
+ </property>
+ <widget class="QWidget" name="dockWidgetContents">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTreeView" name="treeView">
+ <property name="indentation">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp
new file mode 100644
index 0000000..3431c0c
--- /dev/null
+++ b/src/gui/tikzscene.cpp
@@ -0,0 +1,384 @@
+#include "tikzit.h"
+#include "util.h"
+#include "tikzscene.h"
+#include "undocommands.h"
+
+#include <QPen>
+#include <QBrush>
+#include <QDebug>
+
+
+TikzScene::TikzScene(TikzDocument *tikzDocument, QObject *parent) :
+ QGraphicsScene(parent), _tikzDocument(tikzDocument)
+{
+ _modifyEdgeItem = 0;
+ _drawEdgeItem = new QGraphicsLineItem();
+ setSceneRect(-310,-230,620,450);
+
+ QPen pen;
+ pen.setColor(QColor::fromRgbF(0.5f, 0.0f, 0.5f));
+ pen.setWidth(3);
+ _drawEdgeItem->setPen(pen);
+ _drawEdgeItem->setLine(0,0,0,0);
+ _drawEdgeItem->setVisible(false);
+ addItem(_drawEdgeItem);
+}
+
+TikzScene::~TikzScene() {
+}
+
+Graph *TikzScene::graph()
+{
+ return _tikzDocument->graph();
+}
+
+void TikzScene::graphReplaced()
+{
+ foreach (NodeItem *ni, _nodeItems) {
+ removeItem(ni);
+ delete ni;
+ }
+ _nodeItems.clear();
+
+ foreach (EdgeItem *ei, _edgeItems) {
+ removeItem(ei);
+ delete ei;
+ }
+ _edgeItems.clear();
+
+ foreach (Edge *e, graph()->edges()) {
+ EdgeItem *ei = new EdgeItem(e);
+ _edgeItems.insert(e, ei);
+ addItem(ei);
+ }
+
+ foreach (Node *n, graph()->nodes()) {
+ NodeItem *ni = new NodeItem(n);
+ _nodeItems.insert(n, ni);
+ addItem(ni);
+ }
+}
+
+void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ // current mouse position, in scene coordinates
+ QPointF mousePos = event->scenePos();
+
+ // disable rubber band drag, which will clear the selection. Only re-enable it
+ // for the SELECT tool, and when no control point has been clicked.
+ views()[0]->setDragMode(QGraphicsView::NoDrag);
+
+ // radius of a control point for bezier edges, in scene coordinates
+ qreal cpR = GLOBAL_SCALEF * (0.05);
+ qreal cpR2 = cpR * cpR;
+
+ switch (tikzit->toolPalette()->currentTool()) {
+ case ToolPalette::SELECT:
+ // check if we grabbed a control point of an edge
+ foreach (QGraphicsItem *gi, selectedItems()) {
+ if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) {
+ qreal dx, dy;
+
+ dx = ei->cp1Item()->pos().x() - mousePos.x();
+ dy = ei->cp1Item()->pos().y() - mousePos.y();
+
+ if (dx*dx + dy*dy <= cpR2) {
+ _modifyEdgeItem = ei;
+ _firstControlPoint = true;
+ break;
+ }
+
+ dx = ei->cp2Item()->pos().x() - mousePos.x();
+ dy = ei->cp2Item()->pos().y() - mousePos.y();
+
+ if (dx*dx + dy*dy <= cpR2) {
+ _modifyEdgeItem = ei;
+ _firstControlPoint = false;
+ break;
+ }
+ }
+ }
+
+ if (_modifyEdgeItem != 0) {
+ // store for undo purposes
+ Edge *e = _modifyEdgeItem->edge();
+ _oldBend = e->bend();
+ _oldInAngle = e->inAngle();
+ _oldOutAngle = e->outAngle();
+ _oldWeight = e->weight();
+ } else {
+ // since we are not dragging a control point, process the click normally
+ views()[0]->setDragMode(QGraphicsView::RubberBandDrag);
+ QGraphicsScene::mousePressEvent(event);
+
+ // save current node positions for undo support
+ _oldNodePositions.clear();
+ foreach (QGraphicsItem *gi, selectedItems()) {
+ if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) {
+ _oldNodePositions.insert(ni->node(), ni->node()->point());
+ }
+ }
+ }
+
+ break;
+ case ToolPalette::VERTEX:
+ break;
+ case ToolPalette::EDGE:
+ break;
+ case ToolPalette::CROP:
+ break;
+ }
+}
+
+void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ // current mouse position, in scene coordinates
+ QPointF mousePos = event->scenePos();
+
+ switch (tikzit->toolPalette()->currentTool()) {
+ case ToolPalette::SELECT:
+ if (_modifyEdgeItem != 0) {
+ 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;
+ if (_firstControlPoint) {
+ dx2 = mousePos.x() - src.x();
+ dy2 = mousePos.y() - src.y();
+ } else {
+ dx2 = mousePos.x() - targ.x();
+ dy2 = mousePos.y() - targ.y();
+ }
+
+ float baseDist = sqrt(dx1*dx1 + dy1*dy1);
+ float handleDist = sqrt(dx2*dx2 + dy2*dy2);
+ float wcoarseness = 0.1f;
+
+ if (!e->isSelfLoop()) {
+ if (baseDist != 0) {
+ e->setWeight(roundToNearest(wcoarseness, handleDist/baseDist));
+ } else {
+ e->setWeight(roundToNearest(wcoarseness, handleDist/GLOBAL_SCALEF));
+ }
+ }
+
+ float control_angle = atan2(-dy2, dx2);
+
+ int bcoarseness = 15;
+
+ if(e->basicBendMode()) {
+ float bnd;
+ float base_angle = atan2(-dy1, dx1);
+ if (_firstControlPoint) {
+ bnd = base_angle - control_angle;
+ } else {
+ bnd = control_angle - base_angle + M_PI;
+ if (bnd > M_PI) bnd -= 2*M_PI;
+ }
+
+ e->setBend(round(bnd * (180.0f / M_PI) * (1.0f / (float)bcoarseness)) * bcoarseness);
+
+ } else {
+ int bnd = round(control_angle * (180.0f / M_PI) *
+ (1.0f / (float)bcoarseness)) *
+ bcoarseness;
+ if (_firstControlPoint) {
+ // TODO: enable moving both control points
+// if ([theEvent modifierFlags] & NSAlternateKeyMask) {
+// if ([modifyEdge isSelfLoop]) {
+// [modifyEdge setInAngle:[modifyEdge inAngle] +
+// (bnd - [modifyEdge outAngle])];
+// } else {
+// [modifyEdge setInAngle:[modifyEdge inAngle] -
+// (bnd - [modifyEdge outAngle])];
+// }
+// }
+
+ e->setOutAngle(bnd);
+ } else {
+// if (theEvent.modifierFlags & NSAlternateKeyMask) {
+// if ([modifyEdge isSelfLoop]) {
+// [modifyEdge setOutAngle:[modifyEdge outAngle] +
+// (bnd - [modifyEdge inAngle])];
+// } else {
+// [modifyEdge setOutAngle:[modifyEdge outAngle] -
+// (bnd - [modifyEdge inAngle])];
+// }
+// }
+
+ e->setInAngle(bnd);
+ }
+ }
+
+ _modifyEdgeItem->readPos();
+
+ } else {
+ // otherwise, process mouse move normally
+ QGraphicsScene::mouseMoveEvent(event);
+ refreshAdjacentEdges(_oldNodePositions.keys());
+ }
+
+ break;
+ case ToolPalette::VERTEX:
+ break;
+ case ToolPalette::EDGE:
+ break;
+ case ToolPalette::CROP:
+ break;
+ }
+}
+
+void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ // current mouse position, in scene coordinates
+ QPointF mousePos = event->scenePos();
+
+ switch (tikzit->toolPalette()->currentTool()) {
+ case ToolPalette::SELECT:
+ if (_modifyEdgeItem != 0) {
+ // finished dragging a control point
+ Edge *e = _modifyEdgeItem->edge();
+
+ if (_oldWeight != e->weight() ||
+ _oldBend != e->bend() ||
+ _oldInAngle != e->inAngle() ||
+ _oldOutAngle != e->outAngle())
+ {
+ EdgeBendCommand *cmd = new EdgeBendCommand(this, e, _oldWeight, _oldBend, _oldInAngle, _oldOutAngle);
+ _tikzDocument->undoStack()->push(cmd);
+ }
+
+ _modifyEdgeItem = 0;
+ } else {
+ // otherwise, process mouse move normally
+ QGraphicsScene::mouseReleaseEvent(event);
+
+ if (!_oldNodePositions.empty()) {
+ QMap<Node*,QPointF> newNodePositions;
+
+ foreach (QGraphicsItem *gi, selectedItems()) {
+ if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) {
+ ni->writePos();
+ newNodePositions.insert(ni->node(), ni->node()->point());
+ }
+ }
+
+ //qDebug() << _oldNodePositions;
+ //qDebug() << newNodePositions;
+
+ _tikzDocument->undoStack()->push(new MoveCommand(this, _oldNodePositions, newNodePositions));
+ _oldNodePositions.clear();
+ }
+ }
+
+ break;
+ case ToolPalette::VERTEX:
+ {
+ int gridSize = GLOBAL_SCALE / 8;
+ QPointF gridPos(round(mousePos.x()/gridSize)*gridSize, round(mousePos.y()/gridSize)*gridSize);
+ Node *n = new Node();
+ n->setPoint(fromScreen(gridPos));
+
+ QRectF grow(gridPos.x() - GLOBAL_SCALEF, gridPos.y() - GLOBAL_SCALEF, 2 * GLOBAL_SCALEF, 2 * GLOBAL_SCALEF);
+ QRectF newBounds = sceneRect().united(grow);
+ qDebug() << grow;
+ qDebug() << newBounds;
+
+ AddNodeCommand *cmd = new AddNodeCommand(this, n, newBounds);
+ _tikzDocument->undoStack()->push(cmd);
+ }
+ break;
+ case ToolPalette::EDGE:
+ break;
+ case ToolPalette::CROP:
+ break;
+ }
+}
+
+void TikzScene::keyReleaseEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) {
+ QSet<Node*> selNodes;
+ QSet<Edge*> selEdges;
+ getSelection(selNodes, selEdges);
+
+ QMap<int,Node*> deleteNodes;
+ QMap<int,Edge*> deleteEdges;
+
+ for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) {
+ Node *n = _tikzDocument->graph()->nodes()[i];
+ if (selNodes.contains(n)) deleteNodes.insert(i, n);
+ }
+
+ for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) {
+ Edge *e = _tikzDocument->graph()->edges()[i];
+ if (selEdges.contains(e) ||
+ selNodes.contains(e->source()) ||
+ selNodes.contains(e->target())) deleteEdges.insert(i, e);
+ }
+
+ //qDebug() << "nodes:" << deleteNodes;
+ //qDebug() << "edges:" << deleteEdges;
+ DeleteCommand *cmd = new DeleteCommand(this, deleteNodes, deleteEdges, selEdges);
+ _tikzDocument->undoStack()->push(cmd);
+ }
+}
+
+void TikzScene::getSelection(QSet<Node *> &selNodes, QSet<Edge *> &selEdges)
+{
+ foreach (QGraphicsItem *gi, selectedItems()) {
+ if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) selNodes << ni->node();
+ if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) selEdges << ei->edge();
+ }
+}
+
+
+TikzDocument *TikzScene::tikzDocument() const
+{
+ return _tikzDocument;
+}
+
+void TikzScene::setTikzDocument(TikzDocument *tikzDocument)
+{
+ _tikzDocument = tikzDocument;
+ graphReplaced();
+}
+
+void TikzScene::refreshAdjacentEdges(QList<Node*> nodes)
+{
+ if (nodes.empty()) return;
+ foreach (EdgeItem *ei, _edgeItems) {
+ if (nodes.contains(ei->edge()->source()) || nodes.contains(ei->edge()->target())) {
+ ei->edge()->updateControls();
+ ei->readPos();
+ }
+ }
+}
+
+void TikzScene::setBounds(QRectF bounds)
+{
+ if (bounds != sceneRect()) {
+ if (!views().empty()) {
+ QGraphicsView *v = views().first();
+ QPointF c = v->mapToScene(v->viewport()->rect().center());
+ setSceneRect(bounds);
+ v->centerOn(c);
+ } else {
+ setSceneRect(bounds);
+ }
+ }
+}
+
+QMap<Node*,NodeItem *> &TikzScene::nodeItems()
+{
+ return _nodeItems;
+}
+
+QMap<Edge*,EdgeItem*> &TikzScene::edgeItems()
+{
+ return _edgeItems;
+}
diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h
new file mode 100644
index 0000000..6817792
--- /dev/null
+++ b/src/gui/tikzscene.h
@@ -0,0 +1,62 @@
+/**
+ * Manage the scene, which contains a single Graph, and respond to user input. This serves as
+ * the controller for the MVC (TikzDocument, TikzView, TikzScene).
+ */
+
+#ifndef TIKZSCENE_H
+#define TIKZSCENE_H
+
+#include "graph.h"
+#include "nodeitem.h"
+#include "edgeitem.h"
+#include "tikzdocument.h"
+
+#include <QWidget>
+#include <QGraphicsScene>
+#include <QPainter>
+#include <QRectF>
+#include <QVector>
+#include <QGraphicsEllipseItem>
+#include <QGraphicsSceneMouseEvent>
+
+class TikzScene : public QGraphicsScene
+{
+ Q_OBJECT
+public:
+ TikzScene(TikzDocument *tikzDocument, QObject *parent);
+ ~TikzScene();
+ Graph *graph();
+ QMap<Node*,NodeItem*> &nodeItems();
+ QMap<Edge*,EdgeItem*> &edgeItems();
+ void refreshAdjacentEdges(QList<Node*> nodes);
+ void setBounds(QRectF bounds);
+
+ TikzDocument *tikzDocument() const;
+ void setTikzDocument(TikzDocument *tikzDocument);
+
+public slots:
+ void graphReplaced();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+private:
+ TikzDocument *_tikzDocument;
+ QMap<Node*,NodeItem*> _nodeItems;
+ QMap<Edge*,EdgeItem*> _edgeItems;
+ QGraphicsLineItem *_drawEdgeItem;
+ EdgeItem *_modifyEdgeItem;
+ bool _firstControlPoint;
+
+ QMap<Node*,QPointF> _oldNodePositions;
+ float _oldWeight;
+ int _oldBend;
+ int _oldInAngle;
+ int _oldOutAngle;
+
+ void getSelection(QSet<Node*> &selNodes, QSet<Edge*> &selEdges);
+};
+
+#endif // TIKZSCENE_H
diff --git a/src/gui/tikzview.cpp b/src/gui/tikzview.cpp
new file mode 100644
index 0000000..fe6c401
--- /dev/null
+++ b/src/gui/tikzview.cpp
@@ -0,0 +1,84 @@
+#include "tikzview.h"
+#include "tikzit.h"
+
+#include <QDebug>
+
+TikzView::TikzView(QWidget *parent) : QGraphicsView(parent)
+{
+ setRenderHint(QPainter::Antialiasing);
+ setDragMode(QGraphicsView::RubberBandDrag);
+
+ _scale = 1.0f;
+}
+
+void TikzView::zoomIn()
+{
+ _scale *= 1.6f;
+ scale(1.6,1.6);
+}
+
+void TikzView::zoomOut()
+{
+ _scale *= 0.625f;
+ scale(0.625,0.625);
+}
+
+void TikzView::drawBackground(QPainter *painter, const QRectF &rect)
+{
+ // draw the grid
+ int step = GLOBAL_SCALE / 8;
+
+ QPen pen1;
+ pen1.setWidth(1);
+ pen1.setCosmetic(true);
+ pen1.setColor(QColor(230,230,230));
+
+ QPen pen2 = pen1;
+ pen2.setColor(QColor(200,200,200));
+
+ QPen pen3 = pen1;
+ pen3.setColor(QColor(160,160,160));
+
+ painter->setPen(pen1);
+
+ if (_scale > 0.2f) {
+ for (int x = -step; x > rect.left(); x -= step) {
+ if (x % (step * 8) != 0) painter->drawLine(x, rect.top(), x, rect.bottom());
+ }
+
+ for (int x = step; x < rect.right(); x += step) {
+ if (x % (step * 8) != 0) painter->drawLine(x, rect.top(), x, rect.bottom());
+ }
+
+ for (int y = -step; y > rect.top(); y -= step) {
+ if (y % (step * 8) != 0) painter->drawLine(rect.left(), y, rect.right(), y);
+ }
+
+ for (int y = step; y < rect.bottom(); y += step) {
+ if (y % (step * 8) != 0) painter->drawLine(rect.left(), y, rect.right(), y);
+ }
+ }
+
+ painter->setPen(pen2);
+
+ for (int x = -step*8; x > rect.left(); x -= step*8) {
+ painter->drawLine(x, rect.top(), x, rect.bottom());
+ }
+
+ for (int x = step*8; x < rect.right(); x += step*8) {
+ painter->drawLine(x, rect.top(), x, rect.bottom());
+ }
+
+ for (int y = -step*8; y > rect.top(); y -= step*8) {
+ painter->drawLine(rect.left(), y, rect.right(), y);
+ }
+
+ for (int y = step*8; y < rect.bottom(); y += step*8) {
+ painter->drawLine(rect.left(), y, rect.right(), y);
+ }
+
+ painter->setPen(pen3);
+ painter->drawLine(rect.left(), 0, rect.right(), 0);
+ painter->drawLine(0, rect.top(), 0, rect.bottom());
+}
+
diff --git a/src/gui/tikzview.h b/src/gui/tikzview.h
new file mode 100644
index 0000000..fc3cba4
--- /dev/null
+++ b/src/gui/tikzview.h
@@ -0,0 +1,32 @@
+/**
+ * Display a Graph, and manage any user input that purely changes the view (e.g. Zoom). This
+ * serves as the view in the MVC (TikzDocument, TikzView, TikzScene).
+ */
+
+#ifndef TIKZVIEW_H
+#define TIKZVIEW_H
+
+#include <QObject>
+#include <QWidget>
+#include <QGraphicsView>
+#include <QPainter>
+#include <QGraphicsItem>
+#include <QStyleOptionGraphicsItem>
+#include <QRectF>
+#include <QMouseEvent>
+
+class TikzView : public QGraphicsView
+{
+ Q_OBJECT
+public:
+ explicit TikzView(QWidget *parent = 0);
+public slots:
+ void zoomIn();
+ void zoomOut();
+protected:
+ void drawBackground(QPainter *painter, const QRectF &rect);
+private:
+ float _scale;
+};
+
+#endif // TIKZVIEW_H
diff --git a/src/gui/toolpalette.cpp b/src/gui/toolpalette.cpp
new file mode 100644
index 0000000..3c08bce
--- /dev/null
+++ b/src/gui/toolpalette.cpp
@@ -0,0 +1,50 @@
+#include "toolpalette.h"
+
+#include <QVector>
+#include <QLayout>
+#include <QVBoxLayout>
+#include <QDebug>
+
+ToolPalette::ToolPalette(QWidget *parent) :
+ QToolBar(parent)
+{
+ setWindowFlags(Qt::Window
+ | Qt::CustomizeWindowHint
+ | Qt::WindowDoesNotAcceptFocus);
+ setOrientation(Qt::Vertical);
+ setFocusPolicy(Qt::NoFocus);
+ setGeometry(100,200,30,195);
+
+ tools = new QActionGroup(this);
+
+ select = new QAction(QIcon(":/images/select-rectangular.png"), "Select");
+ vertex = new QAction(QIcon(":/images/draw-ellipse.png"), "Add Vertex");
+ edge = new QAction(QIcon(":/images/draw-path.png"), "Add Edge");
+ crop = new QAction(QIcon(":/images/transform-crop-and-resize.png"), "Bounding Box");
+
+ tools->addAction(select);
+ tools->addAction(vertex);
+ tools->addAction(edge);
+ tools->addAction(crop);
+
+ select->setCheckable(true);
+ vertex->setCheckable(true);
+ edge->setCheckable(true);
+ crop->setCheckable(true);
+ select->setChecked(true);
+
+ addAction(select);
+ addAction(vertex);
+ addAction(edge);
+ addAction(crop);
+}
+
+ToolPalette::Tool ToolPalette::currentTool() const
+{
+ QAction *a = tools->checkedAction();
+ if (a == vertex) return VERTEX;
+ else if (a == edge) return EDGE;
+ else if (a == crop) return CROP;
+ else return SELECT;
+}
+
diff --git a/src/gui/toolpalette.h b/src/gui/toolpalette.h
new file mode 100644
index 0000000..ba6aed5
--- /dev/null
+++ b/src/gui/toolpalette.h
@@ -0,0 +1,34 @@
+/**
+ * A small window that lets the user select the current editing tool.
+ */
+
+#ifndef TOOLPALETTE_H
+#define TOOLPALETTE_H
+
+#include <QObject>
+#include <QToolBar>
+#include <QAction>
+#include <QActionGroup>
+
+class ToolPalette : public QToolBar
+{
+ Q_OBJECT
+public:
+ ToolPalette(QWidget *parent = 0);
+ enum Tool {
+ SELECT,
+ VERTEX,
+ EDGE,
+ CROP
+ };
+
+ Tool currentTool() const;
+private:
+ QActionGroup *tools;
+ QAction *select;
+ QAction *vertex;
+ QAction *edge;
+ QAction *crop;
+};
+
+#endif // TOOLPALETTE_H
diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp
new file mode 100644
index 0000000..736c258
--- /dev/null
+++ b/src/gui/undocommands.cpp
@@ -0,0 +1,162 @@
+#include "undocommands.h"
+#include "nodeitem.h"
+#include "edgeitem.h"
+
+#include <QGraphicsView>
+
+MoveCommand::MoveCommand(TikzScene *scene,
+ QMap<Node*, QPointF> oldNodePositions,
+ QMap<Node*, QPointF> newNodePositions,
+ QUndoCommand *parent) :
+ QUndoCommand(parent),
+ _scene(scene),
+ _oldNodePositions(oldNodePositions),
+ _newNodePositions(newNodePositions)
+{}
+
+
+void MoveCommand::undo()
+{
+ foreach (NodeItem *ni, _scene->nodeItems()) {
+ if (_oldNodePositions.contains(ni->node())) {
+ ni->node()->setPoint(_oldNodePositions.value(ni->node()));
+ ni->readPos();
+ }
+ }
+
+ _scene->refreshAdjacentEdges(_oldNodePositions.keys());
+}
+
+void MoveCommand::redo()
+{
+ foreach (NodeItem *ni, _scene->nodeItems()) {
+ if (_newNodePositions.contains(ni->node())) {
+ ni->node()->setPoint(_newNodePositions.value(ni->node()));
+ ni->readPos();
+ }
+ }
+
+ _scene->refreshAdjacentEdges(_newNodePositions.keys());
+}
+
+EdgeBendCommand::EdgeBendCommand(TikzScene *scene, Edge *edge,
+ float oldWeight, int oldBend,
+ int oldInAngle, int oldOutAngle) :
+ _scene(scene), _edge(edge),
+ _oldWeight(oldWeight), _oldBend(oldBend),
+ _oldInAngle(oldInAngle), _oldOutAngle(oldOutAngle)
+{
+ _newWeight = edge->weight();
+ _newBend = edge->bend();
+ _newInAngle = edge->inAngle();
+ _newOutAngle = edge->outAngle();
+}
+
+void EdgeBendCommand::undo()
+{
+ _edge->setWeight(_oldWeight);
+ _edge->setBend(_oldBend);
+ _edge->setInAngle(_oldInAngle);
+ _edge->setOutAngle(_oldOutAngle);
+
+ foreach(EdgeItem *ei, _scene->edgeItems()) {
+ if (ei->edge() == _edge) {
+ ei->readPos();
+ break;
+ }
+ }
+}
+
+void EdgeBendCommand::redo()
+{
+ _edge->setWeight(_newWeight);
+ _edge->setBend(_newBend);
+ _edge->setInAngle(_newInAngle);
+ _edge->setOutAngle(_newOutAngle);
+
+ foreach(EdgeItem *ei, _scene->edgeItems()) {
+ if (ei->edge() == _edge) {
+ ei->readPos();
+ break;
+ }
+ }
+}
+
+DeleteCommand::DeleteCommand(TikzScene *scene,
+ QMap<int, Node *> deleteNodes,
+ QMap<int, Edge *> deleteEdges,
+ QSet<Edge *> selEdges) :
+ _scene(scene), _deleteNodes(deleteNodes),
+ _deleteEdges(deleteEdges), _selEdges(selEdges)
+{}
+
+void DeleteCommand::undo()
+{
+ for (auto it = _deleteNodes.begin(); it != _deleteNodes.end(); ++it) {
+ Node *n = it.value();
+ _scene->graph()->addNode(n, it.key());
+ NodeItem *ni = new NodeItem(n);
+ _scene->nodeItems().insert(n, ni);
+ _scene->addItem(ni);
+ ni->setSelected(true);
+ }
+
+ for (auto it = _deleteEdges.begin(); it != _deleteEdges.end(); ++it) {
+ Edge *e = it.value();
+ _scene->graph()->addEdge(e, it.key());
+ EdgeItem *ei = new EdgeItem(e);
+ _scene->edgeItems().insert(e, ei);
+ _scene->addItem(ei);
+
+ if (_selEdges.contains(e)) ei->setSelected(true);
+ }
+}
+
+void DeleteCommand::redo()
+{
+ foreach (Edge *e, _deleteEdges.values()) {
+ EdgeItem *ei = _scene->edgeItems()[e];
+ _scene->edgeItems().remove(e);
+ _scene->removeItem(ei);
+ delete ei;
+
+ _scene->graph()->removeEdge(e);
+ }
+
+ foreach (Node *n, _deleteNodes.values()) {
+ NodeItem *ni = _scene->nodeItems()[n];
+ _scene->nodeItems().remove(n);
+ _scene->removeItem(ni);
+ delete ni;
+
+ _scene->graph()->removeNode(n);
+ }
+}
+
+AddNodeCommand::AddNodeCommand(TikzScene *scene, Node *node, QRectF newBounds) :
+ _scene(scene), _node(node), _oldBounds(_scene->sceneRect()), _newBounds(newBounds)
+{
+}
+
+void AddNodeCommand::undo()
+{
+ NodeItem *ni = _scene->nodeItems()[_node];
+ _scene->removeItem(ni);
+ _scene->nodeItems().remove(_node);
+ delete ni;
+
+ _scene->graph()->removeNode(_node);
+
+ _scene->setBounds(_oldBounds);
+}
+
+void AddNodeCommand::redo()
+{
+ // TODO: get the current style
+ _scene->graph()->addNode(_node);
+ NodeItem *ni = new NodeItem(_node);
+ _scene->nodeItems().insert(_node, ni);
+ _scene->addItem(ni);
+
+ _scene->setBounds(_newBounds);
+}
diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h
new file mode 100644
index 0000000..ffff876
--- /dev/null
+++ b/src/gui/undocommands.h
@@ -0,0 +1,80 @@
+/**
+ * All changes to a TikzDocument are done via subclasses of QUndoCommand. When a controller
+ * (e.g. TikzScene) gets input from the user to change the document, it will push one of
+ * these commands onto the TikzDocument's undo stack, which automatically calls the redo()
+ * method of the command.
+ */
+
+#ifndef UNDOCOMMANDS_H
+#define UNDOCOMMANDS_H
+
+#include "tikzscene.h"
+
+#include <QUndoCommand>
+
+class MoveCommand : public QUndoCommand
+{
+public:
+ explicit MoveCommand(TikzScene *scene,
+ QMap<Node*,QPointF> oldNodePositions,
+ QMap<Node*,QPointF> newNodePositions,
+ QUndoCommand *parent = 0);
+ void undo() override;
+ void redo() override;
+private:
+ TikzScene *_scene;
+ QMap<Node*,QPointF> _oldNodePositions;
+ QMap<Node*,QPointF> _newNodePositions;
+};
+
+class EdgeBendCommand : public QUndoCommand
+{
+public:
+ explicit EdgeBendCommand(TikzScene *scene, Edge *edge,
+ float oldWeight, int oldBend,
+ int oldInAngle, int oldOutAngle);
+ void undo() override;
+ void redo() override;
+private:
+ TikzScene *_scene;
+ Edge *_edge;
+ float _oldWeight;
+ int _oldBend;
+ int _oldInAngle;
+ int _oldOutAngle;
+ float _newWeight;
+ int _newBend;
+ int _newInAngle;
+ int _newOutAngle;
+};
+
+class DeleteCommand : public QUndoCommand
+{
+public:
+ explicit DeleteCommand(TikzScene *scene,
+ QMap<int,Node*> deleteNodes,
+ QMap<int,Edge*> deleteEdges,
+ QSet<Edge*> selEdges);
+ void undo() override;
+ void redo() override;
+private:
+ TikzScene *_scene;
+ QMap<int,Node*> _deleteNodes;
+ QMap<int,Edge*> _deleteEdges;
+ QSet<Edge*> _selEdges;
+};
+
+class AddNodeCommand : public QUndoCommand
+{
+public:
+ explicit AddNodeCommand(TikzScene *scene, Node *node, QRectF newBounds);
+ void undo() override;
+ void redo() override;
+private:
+ TikzScene *_scene;
+ Node *_node;
+ QRectF _oldBounds;
+ QRectF _newBounds;
+};
+
+#endif // UNDOCOMMANDS_H