/*
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 .
*/
#include
#include
#include
#include "tikzit.h"
#include "styleeditor.h"
#include "delimitedstringvalidator.h"
#include "ui_styleeditor.h"
#include "delimitedstringitemdelegate.h"
StyleEditor::StyleEditor(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::StyleEditor)
{
ui->setupUi(this);
_formWidgets << ui->name << ui->category <<
ui->fillColor << ui->hasTikzitFillColor << ui->tikzitFillColor <<
ui->drawColor << ui->hasTikzitDrawColor << ui->tikzitDrawColor <<
ui->shape << ui->hasTikzitShape << ui->tikzitShape <<
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;
ui->styleListView->setViewMode(QListView::IconMode);
ui->styleListView->setMovement(QListView::Static);
ui->styleListView->setGridSize(QSize(48,48));
ui->edgeStyleListView->setViewMode(QListView::IconMode);
ui->edgeStyleListView->setMovement(QListView::Static);
ui->edgeStyleListView->setGridSize(QSize(48,48));
connect(ui->category->lineEdit(),
SIGNAL(editingFinished()),
this, SLOT(categoryChanged()));
connect(ui->category,
SIGNAL(currentIndexChanged(int)),
this, SLOT(categoryChanged()));
connect(ui->shape->lineEdit(),
SIGNAL(editingFinished()),
this, SLOT(shapeChanged()));
connect(ui->shape,
SIGNAL(currentIndexChanged(int)),
this, SLOT(shapeChanged()));
refreshDisplay();
}
StyleEditor::~StyleEditor()
{
delete ui;
}
void StyleEditor::open() {
if (_styles != nullptr) delete _styles;
_styles = new TikzStyles;
_activeStyle = nullptr;
ui->styleListView->setModel(_styles->nodeStyles());
ui->edgeStyleListView->setModel(_styles->edgeStyles());
connect(ui->styleListView->selectionModel(),
SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this, SLOT(nodeItemChanged(QModelIndex)));
connect(ui->edgeStyleListView->selectionModel(),
SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this, SLOT(edgeItemChanged(QModelIndex)));
if (_styles->loadStyles(tikzit->styleFilePath())) {
setDirty(false);
refreshCategories();
refreshDisplay();
show();
} else {
QMessageBox::warning(0,
"Bad style file.",
"Bad style file: '" + tikzit->styleFile() + "'. Check that the file exists and is properly formatted.");
}
}
void StyleEditor::closeEvent(QCloseEvent *event)
{
if (dirty()) {
QMessageBox::StandardButton resBtn = QMessageBox::question(
this, "Save Changes",
"Do you wish to save changes to " + tikzit->styleFile() + "?",
QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);
if (resBtn == QMessageBox::Yes) {
save();
event->accept();
} else if (resBtn == QMessageBox::No) {
event->accept();
} else {
event->ignore();
}
} else {
event->accept();
}
}
void StyleEditor::nodeItemChanged(QModelIndex sel)
{
//qDebug() << "nodeItemChanged, new index:" << sel.row();
if (sel.isValid()) {
ui->edgeStyleListView->selectionModel()->clear();
_activeStyle = _styles->nodeStyles()->styleInCategory(sel.row());
}
_nodeStyleIndex = sel;
refreshDisplay();
}
void StyleEditor::edgeItemChanged(QModelIndex sel)
{
if (sel.isValid()) {
ui->styleListView->selectionModel()->clear();
_activeStyle = _styles->edgeStyles()->styleInCategory(sel.row());
}
_edgeStyleIndex = sel;
refreshDisplay();
}
void StyleEditor::categoryChanged()
{
Style *s = activeStyle();
QString cat = ui->category->currentText();
//qDebug() << "got category: " << cat;
if (s != nullptr && s->data()->property("tikzit category") != cat) {
if (cat.isEmpty()) s->data()->unsetProperty("tikzit category");
else s->data()->setProperty("tikzit category", cat);
setDirty(true);
refreshCategories();
if (_styles->nodeStyles()->category() != "") {
ui->currentCategory->setCurrentText(cat);
//qDebug() << "after cat change, cat reports:" << _styles->nodeStyles()->category();
}
//refreshDisplay();
}
}
void StyleEditor::currentCategoryChanged()
{
if (_styles != nullptr) {
QString cat = ui->currentCategory->currentText();
if (cat != _styles->nodeStyles()->category()) {
ui->styleListView->selectionModel()->clear();
_styles->nodeStyles()->setCategory(cat);
if (_activeStyle != nullptr && !_activeStyle->isEdgeStyle()) {
for (int i = 0; i < _styles->nodeStyles()->numInCategory(); ++i) {
if (_styles->nodeStyles()->styleInCategory(i) == _activeStyle) {
ui->styleListView->selectionModel()->setCurrentIndex(
_styles->nodeStyles()->index(i),
QItemSelectionModel::ClearAndSelect);
break;
}
}
if (!_nodeStyleIndex.isValid()) _activeStyle = nullptr;
}
}
}
}
void StyleEditor::shapeChanged()
{
Style *s = activeStyle();
if (s != 0) {
s->data()->setProperty("shape", ui->shape->currentText());
refreshActiveStyle();
refreshDisplay();
setDirty(true);
}
}
void StyleEditor::refreshCategories()
{
ui->currentCategory->blockSignals(true);
ui->category->blockSignals(true);
QString curCat = ui->currentCategory->currentText();
QString cat = ui->category->currentText();
ui->currentCategory->clear();
ui->category->clear();
if (_styles != nullptr) {
foreach(QString c, _styles->categories()) {
ui->category->addItem(c);
ui->currentCategory->addItem(c);
}
}
ui->currentCategory->setCurrentText(curCat);
ui->category->setCurrentText(cat);
ui->currentCategory->blockSignals(false);
ui->category->blockSignals(false);
}
void StyleEditor::propertyChanged()
{
if (_nodeStyleIndex.isValid()) {
emit _styles->nodeStyles()->dataChanged(_nodeStyleIndex, _nodeStyleIndex);
if (_activeStyle->category() != _styles->nodeStyles()->category()) {
refreshCategories();
if (_styles->nodeStyles()->category() != "")
ui->currentCategory->setCurrentText(_activeStyle->category());
}
} else if (_edgeStyleIndex.isValid()) {
emit _styles->edgeStyles()->dataChanged(_edgeStyleIndex, _edgeStyleIndex);
}
setDirty(true);
refreshDisplay();
}
void StyleEditor::refreshDisplay()
{
// enable all fields and block signals while we set their values
foreach (QWidget *w, _formWidgets) {
w->blockSignals(true);
}
// set to default values
ui->name->setText("none");
ui->category->setCurrentText("");
//ui->category->clear();
setColor(ui->fillColor, QColor(Qt::gray));
setColor(ui->drawColor, QColor(Qt::gray));
setColor(ui->tikzitFillColor, QColor(Qt::gray));
setColor(ui->tikzitDrawColor, QColor(Qt::gray));
ui->hasTikzitDrawColor->setChecked(false);
ui->hasTikzitFillColor->setChecked(false);
ui->shape->setCurrentText("");
ui->hasTikzitShape->setChecked(false);
ui->tikzitShape->setCurrentText("");
ui->leftArrow->setCurrentText("");
ui->rightArrow->setCurrentText("");
ui->properties->setModel(0);
Style *s = activeStyle();
// qDebug() << "style" << s;
if (s != nullptr && !s->isNone()) {
// qDebug() << "non-null style update";
// name
ui->name->setEnabled(true);
ui->name->setText(s->name());
// property list
ui->properties->setEnabled(true);
setPropertyModel(s->data());
// draw
QColor realDraw = s->strokeColor(false);
QColor draw = s->strokeColor();
ui->drawColor->setEnabled(true);
setColor(ui->drawColor, realDraw);
// tikzit draw
bool drawOverride = s->data()->hasProperty("tikzit draw");
ui->hasTikzitDrawColor->setEnabled(true);
ui->hasTikzitDrawColor->setChecked(drawOverride);
ui->tikzitDrawColor->setEnabled(drawOverride);
if (drawOverride) setColor(ui->tikzitDrawColor, draw);
if (!s->isEdgeStyle()) {
// qDebug() << "node style update";
// category
ui->category->setEnabled(true);
ui->category->setCurrentText(
s->propertyWithDefault("tikzit category", "", false));
// fill
QColor realFill = s->fillColor(false);
QColor fill = s->fillColor();
ui->fillColor->setEnabled(true);
setColor(ui->fillColor, realFill);
// tikzit fill
bool fillOverride = s->data()->hasProperty("tikzit fill");
ui->hasTikzitFillColor->setEnabled(true);
ui->hasTikzitFillColor->setChecked(fillOverride);
ui->tikzitFillColor->setEnabled(fillOverride);
if (fillOverride) setColor(ui->tikzitFillColor, fill);
// shape
QString realShape = s->propertyWithDefault("shape", "", false);
QString shape = s->propertyWithDefault("tikzit shape", "", false);
ui->shape->setEnabled(true);
ui->shape->setCurrentText(realShape);
// tikzit shape
bool shapeOverride = s->data()->hasProperty("tikzit shape");
ui->hasTikzitShape->setEnabled(true);
ui->hasTikzitShape->setChecked(shapeOverride);
ui->tikzitShape->setEnabled(shapeOverride);
if (shapeOverride) ui->tikzitShape->setCurrentText(shape);
} else {
// qDebug() << "edge style update";
// set fill to gray (disabled)
ui->fillColor->setEnabled(false);
ui->tikzitFillColor->setEnabled(false);
ui->hasTikzitFillColor->setEnabled(false);
ui->shape->setEnabled(false);
ui->tikzitShape->setEnabled(false);
ui->hasTikzitShape->setEnabled(false);
// arrow tail
ui->leftArrow->setEnabled(true);
switch (s->arrowTail()) {
case Style::NoTip:
ui->leftArrow->setCurrentText("");
break;
case Style::Pointer:
ui->leftArrow->setCurrentText("<");
break;
case Style::Flat:
ui->leftArrow->setCurrentText("|");
break;
}
// arrow head
ui->rightArrow->setEnabled(true);
switch (s->arrowHead()) {
case Style::NoTip:
ui->rightArrow->setCurrentText("");
break;
case Style::Pointer:
ui->rightArrow->setCurrentText(">");
break;
case Style::Flat:
ui->rightArrow->setCurrentText("|");
break;
}
}
} else {
// qDebug() << "null style update";
foreach (QWidget *w, _formWidgets) {
w->setEnabled(false);
}
}
// unblock signals so we are ready for user input
foreach (QWidget *w, _formWidgets) {
w->blockSignals(false);
}
}
void StyleEditor::on_fillColor_clicked()
{
updateColor(ui->fillColor, "Fill Color", "fill");
}
void StyleEditor::on_drawColor_clicked()
{
updateColor(ui->drawColor, "Draw Color", "draw");
}
void StyleEditor::on_tikzitFillColor_clicked()
{
updateColor(ui->tikzitFillColor, "TikZiT Fill Color", "tikzit fill");
}
void StyleEditor::on_tikzitDrawColor_clicked()
{
updateColor(ui->tikzitDrawColor, "TikZiT Draw Color", "tikzit draw");
}
void StyleEditor::on_hasTikzitFillColor_stateChanged(int state)
{
Style *s = activeStyle();
if (s != nullptr) {
if (state == Qt::Checked) s->data()->setProperty("tikzit fill", s->data()->property("fill"));
else s->data()->unsetProperty("tikzit fill");
refreshDisplay();
setDirty(true);
}
}
void StyleEditor::on_hasTikzitDrawColor_stateChanged(int state)
{
Style *s = activeStyle();
if (s != nullptr) {
if (state == Qt::Checked) s->data()->setProperty("tikzit draw", s->data()->property("draw"));
else s->data()->unsetProperty("tikzit draw");
refreshDisplay();
setDirty(true);
}
}
void StyleEditor::on_hasTikzitShape_stateChanged(int state)
{
Style *s = activeStyle();
if (s != nullptr) {
if (state == Qt::Checked) s->data()->setProperty("tikzit shape", s->data()->property("shape"));
else s->data()->unsetProperty("tikzit shape");
refreshDisplay();
setDirty(true);
}
}
void StyleEditor::on_tikzitShape_currentIndexChanged(int)
{
Style *s = activeStyle();
if (s != nullptr) {
s->data()->setProperty("tikzit shape", ui->tikzitShape->currentText());
refreshActiveStyle();
refreshDisplay();
setDirty(true);
}
}
void StyleEditor::on_leftArrow_currentIndexChanged(int)
{
Style *s = activeStyle();
if (s != nullptr) {
s->setArrowAtom(ui->leftArrow->currentText() + "-" +
ui->rightArrow->currentText());
refreshActiveStyle();
refreshDisplay();
setDirty(true);
}
}
void StyleEditor::on_rightArrow_currentIndexChanged(int)
{
Style *s = activeStyle();
if (s != nullptr) {
s->setArrowAtom(ui->leftArrow->currentText() + "-" +
ui->rightArrow->currentText());
refreshActiveStyle();
refreshDisplay();
setDirty(true);
}
}
void StyleEditor::on_addProperty_clicked()
{
Style *s = activeStyle();
if (s != nullptr) {
s->data()->add(GraphElementProperty("new property", ""));
setDirty(true);
}
}
void StyleEditor::on_addAtom_clicked()
{
Style *s = activeStyle();
if (s != 0) {
s->data()->add(GraphElementProperty("new atom"));
setDirty(true);
}
}
void StyleEditor::on_removeProperty_clicked()
{
Style *s = activeStyle();
if (s != 0) {
QModelIndexList sel = ui->properties->selectionModel()->selectedRows();
if (!sel.isEmpty()) {
s->data()->removeRows(sel[0].row(), 1, sel[0].parent());
setDirty(true);
}
}
}
void StyleEditor::on_propertyUp_clicked()
{
Style *s = activeStyle();
if (s != 0) {
QModelIndexList sel = ui->properties->selectionModel()->selectedRows();
if (!sel.isEmpty()) {
s->data()->moveRows(
sel[0].parent(),
sel[0].row(), 1,
sel[0].parent(),
sel[0].row() - 1);
setDirty(true);
}
}
}
void StyleEditor::on_propertyDown_clicked()
{
Style *s = activeStyle();
if (s != 0) {
QModelIndexList sel = ui->properties->selectionModel()->selectedRows();
if (!sel.isEmpty()) {
s->data()->moveRows(
sel[0].parent(),
sel[0].row(), 1,
sel[0].parent(),
sel[0].row() + 2);
setDirty(true);
}
}
}
void StyleEditor::on_addStyle_clicked()
{
int i = 0;
// get a fresh name
QString name;
while (true) {
name = QString("new style ") + QString::number(i);
if (_styles->nodeStyles()->style(name) == nullptr) break;
++i;
}
// add the style to the current category
Style *s;
if (_styles->nodeStyles()->category() == "") {
s = new Style(name, new GraphElementData({
GraphElementProperty("fill", "white"),
GraphElementProperty("draw", "black"),
GraphElementProperty("shape", "circle")
}));
} else {
s = new Style(name, new GraphElementData({
GraphElementProperty("fill", "white"),
GraphElementProperty("draw", "black"),
GraphElementProperty("shape", "circle"),
GraphElementProperty("category", _styles->nodeStyles()->category()),
}));
}
_styles->nodeStyles()->addStyle(s);
// set dirty flag and select the newly-added style
setDirty(true);
selectNodeStyle(_styles->nodeStyles()->numInCategory()-1);
}
void StyleEditor::on_removeStyle_clicked()
{
if (_nodeStyleIndex.isValid()) {
int i = _nodeStyleIndex.row();
if (i > 0) {
ui->styleListView->selectionModel()->clear();
_styles->nodeStyles()->removeNthStyle(i);
setDirty(true);
if (i < _styles->nodeStyles()->numInCategory()) {
selectNodeStyle(i);
}
}
}
}
void StyleEditor::on_styleUp_clicked()
{
if (_nodeStyleIndex.isValid()) {
int r = _nodeStyleIndex.row();
if (_styles->nodeStyles()->moveRows(
_nodeStyleIndex.parent(),
r, 1,
_nodeStyleIndex.parent(),
r - 1))
{
setDirty(true);
nodeItemChanged(_styles->nodeStyles()->index(r - 1));
}
}
}
void StyleEditor::on_styleDown_clicked()
{
if (_nodeStyleIndex.isValid()) {
int r = _nodeStyleIndex.row();
if (_styles->nodeStyles()->moveRows(
_nodeStyleIndex.parent(),
r, 1,
_nodeStyleIndex.parent(),
r + 2))
{
setDirty(true);
nodeItemChanged(_styles->nodeStyles()->index(r + 1));
}
}
}
void StyleEditor::on_addEdgeStyle_clicked()
{
int i = 0;
// get a fresh name
QString name;
while (true) {
name = QString("new edge style ") + QString::number(i);
if (_styles->edgeStyles()->style(name) == nullptr) break;
++i;
}
// add the style (edge styles only have one category: "")
Style *s = new Style(name, new GraphElementData({GraphElementProperty("-")}));
_styles->edgeStyles()->addStyle(s);
// set dirty flag and select the newly-added style
setDirty(true);
selectEdgeStyle(_styles->edgeStyles()->numInCategory()-1);
}
void StyleEditor::on_removeEdgeStyle_clicked()
{
if (_edgeStyleIndex.isValid()) {
int i = _edgeStyleIndex.row();
if (i > 0) {
ui->edgeStyleListView->selectionModel()->clear();
_styles->edgeStyles()->removeNthStyle(i);
setDirty(true);
if (i < _styles->edgeStyles()->numInCategory()) {
selectEdgeStyle(i);
}
}
}
}
void StyleEditor::on_edgeStyleUp_clicked()
{
if (_edgeStyleIndex.isValid()) {
int r = _edgeStyleIndex.row();
if (_styles->edgeStyles()->moveRows(
_edgeStyleIndex.parent(),
r, 1,
_edgeStyleIndex.parent(),
r - 1))
{
setDirty(true);
edgeItemChanged(_styles->edgeStyles()->index(r - 1));
}
}
}
void StyleEditor::on_edgeStyleDown_clicked()
{
if (_edgeStyleIndex.isValid()) {
int r = _edgeStyleIndex.row();
if (_styles->edgeStyles()->moveRows(
_edgeStyleIndex.parent(),
r, 1,
_edgeStyleIndex.parent(),
r + 2))
{
setDirty(true);
edgeItemChanged(_styles->edgeStyles()->index(r + 1));
}
}
}
void StyleEditor::on_save_clicked()
{
save();
close();
}
void StyleEditor::on_currentCategory_currentIndexChanged(int)
{
currentCategoryChanged();
}
void StyleEditor::save()
{
QString p = tikzit->styleFilePath();
if (_styles->saveStyles(p)) {
setDirty(false);
tikzit->loadStyles(p);
} else {
QMessageBox::warning(0,
"Unabled to save style file",
"Unable to write to file: '" + tikzit->styleFile() + "'.");
}
}
void StyleEditor::on_styleListView_clicked()
{
}
void StyleEditor::on_edgeStyleListView_clicked()
{
}
void StyleEditor::on_name_editingFinished()
{
Style *s = activeStyle();
if (s != 0) {
s->setName(ui->name->text());
refreshActiveStyle();
// refreshDisplay();
setDirty(true);
}
}
void StyleEditor::setColor(QPushButton *btn, QColor col)
{
QPalette pal = btn->palette();
pal.setColor(QPalette::Button, col);
btn->setPalette(pal);
btn->update();
}
void StyleEditor::setPropertyModel(GraphElementData *d)
{
if (ui->properties->model() != 0) {
disconnect(ui->properties->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)),
this, SLOT(propertyChanged()));
}
ui->properties->setModel(d);
connect(d, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)),
this, SLOT(propertyChanged()));
}
QColor StyleEditor::color(QPushButton *btn)
{
QPalette pal = btn->palette();
return pal.color(QPalette::Button);
}
Style *StyleEditor::activeStyle()
{
// if (_styles != nullptr) {
// if (_nodeStyleIndex.isValid())
// return _styles->nodeStyles()->styleInCategory(_nodeStyleIndex.row());
// if (_edgeStyleIndex.isValid())
// return _styles->edgeStyles()->styleInCategory(_edgeStyleIndex.row());
// }
return _activeStyle;
}
void StyleEditor::refreshActiveStyle()
{
if (_styles != nullptr) {
if (_nodeStyleIndex.isValid()) {
emit _styles->nodeStyles()->dataChanged(_nodeStyleIndex, _nodeStyleIndex);
// force a re-layout
ui->styleListView->setGridSize(QSize(48,48));
}
if (_edgeStyleIndex.isValid()) {
emit _styles->edgeStyles()->dataChanged(_edgeStyleIndex, _edgeStyleIndex);
// force a re-layout
ui->edgeStyleListView->setGridSize(QSize(48,48));
}
}
}
void StyleEditor::updateColor(QPushButton *btn, QString name, QString propName)
{
QColor col = QColorDialog::getColor(
color(btn),
this,
name,
QColorDialog::DontUseNativeDialog);
if (col.isValid()) {
setColor(btn, col);
Style *s = activeStyle();
if (s != nullptr) {
s->data()->setProperty(propName, tikzit->nameForColor(col));
refreshActiveStyle();
refreshDisplay();
setDirty(true);
}
}
}
void StyleEditor::selectNodeStyle(int i)
{
ui->styleListView->selectionModel()->setCurrentIndex(
_styles->nodeStyles()->index(i),
QItemSelectionModel::ClearAndSelect);
}
void StyleEditor::selectEdgeStyle(int i)
{
ui->edgeStyleListView->selectionModel()->setCurrentIndex(
_styles->edgeStyles()->index(i),
QItemSelectionModel::ClearAndSelect);
}
bool StyleEditor::dirty() const
{
return _dirty;
}
void StyleEditor::setDirty(bool dirty)
{
_dirty = dirty;
if (dirty) {
setWindowTitle("Style Editor* - TikZiT");
} else {
setWindowTitle("Style Editor - TikZiT");
}
}