157 lines
5.5 KiB
C++
157 lines
5.5 KiB
C++
#include <QApplication>
|
|
#include <QCommandLineOption>
|
|
#include <QCommandLineParser>
|
|
#include <QLocale>
|
|
#include <QMessageBox>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
#include <QTranslator>
|
|
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#include <wfassoc++.h>
|
|
|
|
#include "main_window.h"
|
|
#include "manifest_parser.h"
|
|
#include "scope.h"
|
|
|
|
namespace {
|
|
|
|
// Context used for translatable strings that live outside of any QObject.
|
|
constexpr const char* kTranslationContext = "qwfassoc-standalone";
|
|
|
|
// Show a modal error dialog with the given message and return a non-zero
|
|
// exit code. Used for the various fatal conditions that may occur before the
|
|
// main dialog can be shown.
|
|
int fatal(QWidget* parent, const QString& message) {
|
|
QMessageBox::critical(parent, QApplication::applicationName(), message);
|
|
return 1;
|
|
}
|
|
|
|
// Convert the --for command line value to a TargetScope. Throws if the value
|
|
// is not one of the accepted strings.
|
|
qwfassoc::TargetScope parseScope(const QString& value) {
|
|
const QString normalized = value.trimmed().toLower();
|
|
if (normalized == QStringLiteral("user")) {
|
|
return qwfassoc::TargetScope::User;
|
|
}
|
|
if (normalized == QStringLiteral("system")) {
|
|
return qwfassoc::TargetScope::System;
|
|
}
|
|
throw std::runtime_error(
|
|
"Invalid value for --for. Use \"user\" or \"system\".");
|
|
}
|
|
|
|
// Install the translation(s) matching the user's preferred UI language, if
|
|
// any. Both the qwfassoc library's .qm and this executable's .qm are loaded
|
|
// (their .ts files live under the per-project i18n/ directories and are
|
|
// embedded under the ":/i18n" resource prefix by qt6_add_translations).
|
|
void installTranslators(QApplication& app) {
|
|
const QStringList uiLanguages = QLocale::system().uiLanguages();
|
|
for (const QString& locale : uiLanguages) {
|
|
const QString name = QLocale(locale).name();
|
|
|
|
QTranslator* libTranslator = new QTranslator(&app);
|
|
if (libTranslator->load(QStringLiteral(":/i18n/qwfassoc_") + name)) {
|
|
app.installTranslator(libTranslator);
|
|
}
|
|
|
|
QTranslator* appTranslator = new QTranslator(&app);
|
|
if (appTranslator->load(
|
|
QStringLiteral(":/i18n/qwfassoc-standalone_") + name)) {
|
|
app.installTranslator(appTranslator);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char* argv[]) {
|
|
QApplication app(argc, argv);
|
|
QApplication::setApplicationName(QStringLiteral("qwfassoc-standalone"));
|
|
|
|
// Install available translations before any translatable string is
|
|
// resolved so that tr() and QCoreApplication::translate() pick up the
|
|
// right language.
|
|
installTranslators(app);
|
|
|
|
QApplication::setApplicationDisplayName(
|
|
QCoreApplication::translate(kTranslationContext,
|
|
"qwfassoc - wfassoc Configurator"));
|
|
|
|
// Parse command line arguments using Qt's built-in parser.
|
|
QCommandLineParser parser;
|
|
parser.setApplicationDescription(
|
|
QCoreApplication::translate(
|
|
kTranslationContext,
|
|
"Qt-based GUI executable for the wfassoc library."));
|
|
parser.addHelpOption();
|
|
|
|
QCommandLineOption manifestOption(
|
|
QStringList() << QStringLiteral("c") << QStringLiteral("manifest"),
|
|
QCoreApplication::translate(kTranslationContext,
|
|
"Path to the application manifest TOML file."),
|
|
QStringLiteral("manifest"));
|
|
QCommandLineOption forOption(
|
|
QStringList() << QStringLiteral("f") << QStringLiteral("for"),
|
|
QCoreApplication::translate(kTranslationContext,
|
|
"Target scope: \"user\" or \"system\"."),
|
|
QStringLiteral("scope"));
|
|
|
|
parser.addOption(manifestOption);
|
|
parser.addOption(forOption);
|
|
parser.process(app);
|
|
|
|
// Validate that both mandatory options were provided with sane values.
|
|
const QString manifestPath = parser.value(manifestOption);
|
|
const QString forValue = parser.value(forOption);
|
|
|
|
if (manifestPath.isEmpty()) {
|
|
return fatal(nullptr,
|
|
QCoreApplication::translate(
|
|
kTranslationContext,
|
|
"The --manifest/-c option is required."));
|
|
}
|
|
if (forValue.isEmpty()) {
|
|
return fatal(nullptr,
|
|
QCoreApplication::translate(
|
|
kTranslationContext,
|
|
"The --for/-f option is required."));
|
|
}
|
|
|
|
qwfassoc::TargetScope scope;
|
|
try {
|
|
scope = parseScope(forValue);
|
|
} catch (const std::exception& e) {
|
|
return fatal(nullptr, QString::fromUtf8(e.what()));
|
|
}
|
|
|
|
// Initialize the wfassoc runtime. WFStartup must run before most other
|
|
// wfassoc calls; if it fails we cannot proceed.
|
|
if (!wfassoc::WFStartup()) {
|
|
return fatal(nullptr,
|
|
QString::fromUtf8(wfassoc::WFGetLastError()));
|
|
}
|
|
|
|
// Build the manifest -> schema -> program pipeline and run the dialog.
|
|
// Program construction consumes the schema (move) and performs the deep
|
|
// validation (identifier format, dangling references, etc.).
|
|
int exitCode = 0;
|
|
try {
|
|
qwfassoc::Manifest manifest =
|
|
qwfassoc::parseManifestFile(manifestPath.toStdString());
|
|
wfassocpp::Schema schema = qwfassoc::buildSchema(manifest);
|
|
wfassocpp::Program program(std::move(schema));
|
|
|
|
qwfassoc::MainWindow window(std::move(program), scope);
|
|
exitCode = window.exec();
|
|
} catch (const std::exception& e) {
|
|
wfassoc::WFShutdown();
|
|
return fatal(nullptr, QString::fromUtf8(e.what()));
|
|
}
|
|
|
|
wfassoc::WFShutdown();
|
|
return exitCode;
|
|
}
|