/** * @RDE_HEADER@ * * MainWindow for settings editor * * $Id$ * $HeadURL$ * $Author$ */ #include "main_window.h" #include "ui_main_window.h" #include #include #include #include #include #include #include #include #include #include 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 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; } }