Files
qt5-legatus-legacy/Lts/Tools/ProjectSettingsEditor/main_window.cpp
T
2026-08-13 18:33:10 +02:00

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;
}
}