405 lines
11 KiB
C++
Executable File
405 lines
11 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
*
|
|
* MainWindow for settings editor
|
|
*
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
#include "main_window.h"
|
|
#include "ui_main_window.h"
|
|
|
|
#include <NGLLogger/INGLLogger.h>
|
|
|
|
#include <QAction>
|
|
#include <QFile>
|
|
#include <QFileDialog>
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonValue>
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
|
|
namespace SettingsEditor
|
|
{
|
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
|
: NGLMainWindow(parent)
|
|
, m_ui(new Ui::MainWindow)
|
|
{
|
|
m_ui->setupUi(this);
|
|
|
|
enableTextEdit(false);
|
|
|
|
QMenu *fileMenu = m_menu[QLatin1String("File")];
|
|
fileMenu->addSeparator();
|
|
|
|
QAction *action;
|
|
action = new QAction(tr("Load Settings"), this);
|
|
action->setShortcut(QKeySequence(tr("Ctrl+l")));
|
|
connect(action, &QAction::triggered, this, &MainWindow::onLoadFromExport);
|
|
fileMenu->addAction(action);
|
|
|
|
action = new QAction(tr("Save Settings"), this);
|
|
action->setShortcut(QKeySequence(tr("Ctrl+s")));
|
|
connect(action, &QAction::triggered, this, &MainWindow::onSaveSettings);
|
|
fileMenu->addAction(action);
|
|
|
|
action = new QAction(tr("Export Settings"), this);
|
|
action->setShortcut(QKeySequence(tr("Ctrl+e")));
|
|
connect(action, &QAction::triggered, this, &MainWindow::onExportSettings);
|
|
fileMenu->addAction(action);
|
|
|
|
action = new QAction(tr("Load Default"), this);
|
|
action->setShortcut(QKeySequence(tr("Ctrl+d")));
|
|
connect(action, &QAction::triggered, this, &MainWindow::onLoadPresetFromTemplate);
|
|
fileMenu->addAction(action);
|
|
|
|
onLoadPresetFromTemplate();
|
|
|
|
m_ui->twPath->expandAll();
|
|
|
|
connect(m_ui->twPath, &QTreeWidget::currentItemChanged, this, &MainWindow::oncurrentItemChanged);
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete m_ui;
|
|
}
|
|
|
|
void MainWindow::oncurrentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
|
|
{
|
|
if (previous)
|
|
{
|
|
if (previous->parent())
|
|
{
|
|
QPair<SettingType, QVariant> value = m_data.value(craftKey(previous->parent()->text(0), previous->text(0)));
|
|
|
|
switch (value.first)
|
|
{
|
|
case StringList:
|
|
value.second = QVariant(m_ui->teValueEdit->toPlainText().split(QRegularExpression{QStringLiteral(R"-((\r\n?|\n))-")}));
|
|
break;
|
|
case FunctionKeys:
|
|
value.second = QVariant(m_ui->functionKeysEdit->value());
|
|
break;
|
|
case Json:
|
|
case String:
|
|
value.second = QVariant(m_ui->teValueEdit->toPlainText());
|
|
break;
|
|
case Boolean:
|
|
value.second = m_ui->cbBoolValue->isChecked();
|
|
break;
|
|
}
|
|
const QString key(craftKey(previous->parent()->text(0), previous->text(0)));
|
|
if (m_data[key] != value)
|
|
{
|
|
setWindowModified(true);
|
|
m_data[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (current)
|
|
{
|
|
if (current->parent())
|
|
{
|
|
const auto it = m_data.constFind(craftKey(current->parent()->text(0), current->text(0)));
|
|
const QVariant &val = it.value().second;
|
|
switch (it.value().first)
|
|
{
|
|
case StringList:
|
|
enableTextEdit();
|
|
m_ui->teValueEdit->setPlainText(val.toStringList().join(QLatin1Char('\n')));
|
|
break;
|
|
case Json:
|
|
enableTextEdit();
|
|
m_ui->teValueEdit->setPlainText(val.toString());
|
|
break;
|
|
case String:
|
|
enableTextEdit();
|
|
m_ui->teValueEdit->setPlainText(val.toString());
|
|
break;
|
|
case FunctionKeys:
|
|
enableFunctionKeyEdit();
|
|
m_ui->functionKeysEdit->setValue(val);
|
|
break;
|
|
case Boolean:
|
|
enableBoolEdit();
|
|
m_ui->cbBoolValue->setChecked(val.toBool());
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
enableTextEdit(false);
|
|
}
|
|
else
|
|
enableTextEdit(false);
|
|
}
|
|
|
|
void MainWindow::onSaveSettings()
|
|
{
|
|
if (saveSettings(m_settingsFileName))
|
|
setWindowModified(false);
|
|
}
|
|
|
|
void MainWindow::onExportSettings()
|
|
{
|
|
// save current data in edit field
|
|
oncurrentItemChanged(m_ui->twPath->currentItem(), m_ui->twPath->currentItem());
|
|
|
|
const QString filename = QFileDialog::getSaveFileName(this,
|
|
tr("Save File"),
|
|
QStringLiteral("project_settings.json"),
|
|
tr("Project Settings files (*.json)"));
|
|
saveSettings(filename);
|
|
}
|
|
|
|
bool MainWindow::saveSettings(const QString &filename)
|
|
{
|
|
if (filename.isEmpty())
|
|
return false;
|
|
|
|
QFile f(filename);
|
|
if (!f.open(QIODevice::WriteOnly))
|
|
{
|
|
QMessageBox::warning(this, tr("Error writing file"),
|
|
tr("Could not write file '%1', error: %2").arg(filename, f.errorString()));
|
|
return false;
|
|
}
|
|
|
|
QJsonObject obj;
|
|
for (int t = 0; t < m_ui->twPath->topLevelItemCount(); ++t)
|
|
{
|
|
QJsonObject tlObj;
|
|
const QTreeWidgetItem *tlItem = m_ui->twPath->topLevelItem(t);
|
|
for (int g = 0; g < tlItem->childCount(); ++g)
|
|
{
|
|
QJsonObject groupObj;
|
|
const QTreeWidgetItem *groupItem = tlItem->child(g);
|
|
const QString groupName = groupItem->text(0);
|
|
for (int i = 0; i < groupItem->childCount(); ++i)
|
|
{
|
|
const QTreeWidgetItem *item = groupItem->child(i);
|
|
const QString itemName = item->text(0);
|
|
const QString key = craftKey(groupName, itemName);
|
|
const QString type = typeToString(m_data[craftKey(groupName, itemName)].first);
|
|
QJsonValue val;
|
|
switch (m_data[craftKey(groupName, itemName)].first)
|
|
{
|
|
case String:
|
|
val = m_data[key].second.toString();
|
|
break;
|
|
case FunctionKeys:
|
|
val = m_data[key].second.toString();
|
|
break;
|
|
case Json:
|
|
val = QJsonDocument::fromJson(m_data[key].second.toByteArray()).object();
|
|
break;
|
|
case StringList:
|
|
{
|
|
QJsonArray arr;
|
|
const QStringList sl = m_data[key].second.toStringList();
|
|
for (const QString &s : sl)
|
|
arr.append(s);
|
|
val = arr;
|
|
break;
|
|
}
|
|
case Boolean:
|
|
val = m_data[key].second.toBool();
|
|
break;
|
|
}
|
|
groupObj[itemName] = QJsonObject{{QStringLiteral("Default"), val}, {QStringLiteral("Type"), type}};
|
|
}
|
|
tlObj[groupName] = groupObj;
|
|
}
|
|
obj[tlItem->text(0)] = tlObj;
|
|
}
|
|
QJsonDocument doc(obj);
|
|
f.write(doc.toJson());
|
|
f.close();
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::onLoadFromExport()
|
|
{
|
|
const QString filename = QFileDialog::getOpenFileName(this,
|
|
tr("Load File"),
|
|
QString(),
|
|
tr("Project Settings files (*.json)"));
|
|
|
|
if (filename.isEmpty())
|
|
return;
|
|
|
|
QFile input(filename);
|
|
if (!input.open(QIODevice::ReadOnly))
|
|
return;
|
|
|
|
if (loadSettings(filename))
|
|
{
|
|
m_settingsFileName = filename;
|
|
setWindowTitle(filename + QStringLiteral("[*]"));
|
|
}
|
|
}
|
|
|
|
void MainWindow::onLoadPresetFromTemplate()
|
|
{
|
|
loadSettings(QStringLiteral(":/project_settings.json"));
|
|
}
|
|
|
|
bool MainWindow::loadSettings(const QString &filename)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Loading settings from " << filename;
|
|
QFile f(filename);
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
{
|
|
QMessageBox::warning(this, tr("Error reading file"),
|
|
tr("Could not read file '%1', error: %2").arg(filename, f.errorString()));
|
|
return false;
|
|
}
|
|
|
|
bool firstLoad = m_data.isEmpty();
|
|
|
|
QJsonParseError jErr;
|
|
const QByteArray ba = f.readAll();
|
|
const QJsonObject settings = QJsonDocument::fromJson(ba, &jErr).object();
|
|
if (jErr.error != QJsonParseError::NoError)
|
|
{
|
|
if (jErr.offset >= 0)
|
|
{
|
|
QByteArray code = ba.mid(jErr.offset - 40, 80);
|
|
QMessageBox::warning(this, tr("Json parser error"),
|
|
tr("%1 (fragment: '%2')").arg(jErr.errorString(), QString::fromUtf8(code)));
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::warning(this, tr("Json parser error"),
|
|
jErr.errorString());
|
|
}
|
|
return false;
|
|
}
|
|
const QStringList &settingsKeys = settings.keys();
|
|
|
|
for (const auto &setting : settingsKeys)
|
|
{
|
|
QTreeWidgetItem *settingTreeItem = firstLoad ? new QTreeWidgetItem(m_ui->twPath, QStringList(setting)) : nullptr; // only build tree on first load
|
|
|
|
const QJsonObject groups = settings.value(setting).toObject();
|
|
const QStringList &groupKeys = groups.keys();
|
|
for (const auto &group : groupKeys)
|
|
{
|
|
QTreeWidgetItem *groupTreeItem = firstLoad ? new QTreeWidgetItem(settingTreeItem, QStringList(group)) : nullptr; // only build tree on first load
|
|
const QStringList &itemKeys = groups[group].toObject().keys();
|
|
for (const auto &key : itemKeys)
|
|
{
|
|
if (groupTreeItem)
|
|
groupTreeItem->addChild(new QTreeWidgetItem(QStringList(key)));
|
|
|
|
const QJsonObject obj = groups[group].toObject()[key].toObject();
|
|
const auto strType = obj[QStringLiteral("Type")].toString();
|
|
const auto type = stringToType(strType);
|
|
const QJsonValue jVal =obj[QStringLiteral("Default")];
|
|
QVariant val;
|
|
switch (type)
|
|
{
|
|
case Json:
|
|
val = QJsonDocument(jVal.toObject()).toJson();
|
|
break;
|
|
case StringList:
|
|
{
|
|
const QJsonArray arr = jVal.toArray();
|
|
QStringList sl;
|
|
sl.reserve(arr.size());
|
|
for (const auto &ele : arr)
|
|
sl.push_back(ele.toString());
|
|
val = sl;
|
|
break;
|
|
}
|
|
case FunctionKeys:
|
|
val = jVal.toVariant();
|
|
break;
|
|
case String:
|
|
val = jVal.toString();
|
|
break;
|
|
case Boolean:
|
|
val = jVal.toBool();
|
|
break;
|
|
}
|
|
m_data.insert(craftKey(group, key), qMakePair(type, val));
|
|
}
|
|
}
|
|
}
|
|
|
|
// load new data into edit field
|
|
oncurrentItemChanged(m_ui->twPath->currentItem(), nullptr); // nullptr -> prevent saving existing data
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::enableTextEdit(bool enabled)
|
|
{
|
|
m_ui->stackedWidget->setCurrentIndex(0);
|
|
m_ui->teValueEdit->setEnabled(enabled);
|
|
}
|
|
|
|
void MainWindow::enableFunctionKeyEdit()
|
|
{
|
|
m_ui->stackedWidget->setCurrentIndex(2);
|
|
}
|
|
|
|
void MainWindow::enableBoolEdit()
|
|
{
|
|
m_ui->stackedWidget->setCurrentIndex(1);
|
|
}
|
|
|
|
void MainWindow::setVersionView()
|
|
{
|
|
|
|
}
|
|
|
|
QString MainWindow::craftKey(const QString &group, const QString &key)
|
|
{
|
|
return group + QLatin1Char('.') + key;
|
|
}
|
|
|
|
QString MainWindow::typeToString(SettingType t)
|
|
{
|
|
switch (t)
|
|
{
|
|
case String:
|
|
return QStringLiteral("String");
|
|
case StringList:
|
|
return QStringLiteral("StringList");
|
|
case Json:
|
|
return QStringLiteral("Json");
|
|
case FunctionKeys:
|
|
return QStringLiteral("FunctionKeys");
|
|
case Boolean:
|
|
return QStringLiteral("Boolean");
|
|
}
|
|
Q_ASSERT(false);
|
|
return QStringLiteral("Unknown (%1)").arg(t);
|
|
}
|
|
|
|
MainWindow::SettingType MainWindow::stringToType(const QString &s)
|
|
{
|
|
if (s == typeToString(String))
|
|
return String;
|
|
if (s == typeToString(StringList))
|
|
return StringList;
|
|
if (s == typeToString(Json))
|
|
return Json;
|
|
if (s == typeToString(FunctionKeys))
|
|
return FunctionKeys;
|
|
if (s == typeToString(Boolean))
|
|
return Boolean;
|
|
Q_ASSERT(false);
|
|
return String;
|
|
}
|
|
|
|
|
|
|
|
}
|