Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
add_executable(kconfig_compiler)
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
if(BUILD_TESTING)
|
||||
message(WARNING "Testing should be disabled on cross-compilation")
|
||||
endif()
|
||||
else()
|
||||
add_executable(KF6::kconfig_compiler ALIAS kconfig_compiler)
|
||||
endif()
|
||||
|
||||
target_sources(kconfig_compiler PRIVATE
|
||||
KConfigParameters.cpp
|
||||
KConfigCodeGeneratorBase.cpp
|
||||
KConfigHeaderGenerator.cpp
|
||||
KConfigSourceGenerator.cpp
|
||||
KConfigXmlParser.cpp
|
||||
kconfig_compiler.cpp
|
||||
)
|
||||
|
||||
set_target_properties(kconfig_compiler PROPERTIES
|
||||
OUTPUT_NAME "kconfig_compiler_kf6"
|
||||
)
|
||||
|
||||
target_link_libraries(kconfig_compiler Qt6::Xml)
|
||||
|
||||
ecm_mark_nongui_executable(kconfig_compiler)
|
||||
|
||||
install(TARGETS kconfig_compiler EXPORT KF6ConfigCompilerTargets DESTINATION ${KDE_INSTALL_LIBEXECDIR_KF})
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigCodeGeneratorBase.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QLatin1Char>
|
||||
|
||||
#include <QDebug>
|
||||
#include <ostream>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
KConfigCodeGeneratorBase::KConfigCodeGeneratorBase(const QString &inputFile,
|
||||
const QString &baseDir,
|
||||
const QString &fileName,
|
||||
const KConfigParameters ¶meters,
|
||||
ParseResult &parseResult)
|
||||
: parseResult(parseResult)
|
||||
, m_inputFile(inputFile)
|
||||
, m_baseDir(baseDir)
|
||||
, m_fileName(fileName)
|
||||
, m_cfg(parameters)
|
||||
{
|
||||
m_file.setFileName(m_fileName);
|
||||
if (!m_file.open(QIODevice::WriteOnly)) {
|
||||
std::cerr << "Can not open '" << qPrintable(m_fileName) << "for writing." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
m_stream.setDevice(&m_file);
|
||||
|
||||
if (m_cfg.staticAccessors) {
|
||||
m_this = QStringLiteral("self()->");
|
||||
} else {
|
||||
m_const = QStringLiteral(" const");
|
||||
}
|
||||
}
|
||||
|
||||
KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase()
|
||||
{
|
||||
save();
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::save()
|
||||
{
|
||||
m_file.close();
|
||||
}
|
||||
|
||||
// TODO: Remove this weird logic and adapt the testcases
|
||||
void KConfigCodeGeneratorBase::indent()
|
||||
{
|
||||
if (m_indentLevel >= 4) {
|
||||
m_indentLevel += 2;
|
||||
} else {
|
||||
m_indentLevel += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::unindent()
|
||||
{
|
||||
if (m_indentLevel > 4) {
|
||||
m_indentLevel -= 2;
|
||||
} else {
|
||||
m_indentLevel -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
QString KConfigCodeGeneratorBase::whitespace() const
|
||||
{
|
||||
QString spaces;
|
||||
for (int i = 0; i < m_indentLevel; i++) {
|
||||
spaces.append(QLatin1Char(' '));
|
||||
}
|
||||
return spaces;
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::startScope()
|
||||
{
|
||||
m_stream << whitespace() << QLatin1Char('{');
|
||||
m_stream << '\n';
|
||||
indent();
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer)
|
||||
{
|
||||
unindent();
|
||||
m_stream << whitespace() << QLatin1Char('}');
|
||||
if (finalizer == ScopeFinalizer::Semicolon) {
|
||||
m_stream << ';';
|
||||
}
|
||||
m_stream << '\n';
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::start()
|
||||
{
|
||||
const QString m_fileName = QFileInfo(m_inputFile).fileName();
|
||||
m_stream << "// This file is generated by kconfig_compiler_kf6 from " << m_fileName << ".kcfg"
|
||||
<< ".\n";
|
||||
m_stream << "// All changes you do to this file will be lost.\n";
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::addHeaders(const QStringList &headerList)
|
||||
{
|
||||
for (const auto &include : headerList) {
|
||||
if (include.startsWith(QLatin1Char('"'))) {
|
||||
m_stream << "#include " << include << '\n';
|
||||
} else {
|
||||
m_stream << "#include <" << include << ">\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adds as many 'namespace foo {' lines to p_out as
|
||||
// there are namespaces in p_ns
|
||||
void KConfigCodeGeneratorBase::beginNamespaces()
|
||||
{
|
||||
if (!m_cfg.nameSpace.isEmpty()) {
|
||||
const auto nameSpaceList = m_cfg.nameSpace.split(QStringLiteral("::"));
|
||||
for (const QString &ns : nameSpaceList) {
|
||||
m_stream << "namespace " << ns << " {\n";
|
||||
}
|
||||
m_stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// adds as many '}' lines to p_out as
|
||||
// there are namespaces in p_ns
|
||||
void KConfigCodeGeneratorBase::endNamespaces()
|
||||
{
|
||||
if (!m_cfg.nameSpace.isEmpty()) {
|
||||
m_stream << '\n';
|
||||
const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1;
|
||||
for (int i = 0; i < namespaceCount; ++i) {
|
||||
m_stream << "}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns the member accessor implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const
|
||||
{
|
||||
QString result;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
QString n = e->name;
|
||||
QString t = e->type;
|
||||
bool useEnumType = m_cfg.useEnumTypes && t == QLatin1String("Enum");
|
||||
|
||||
out << "return ";
|
||||
if (useEnumType) {
|
||||
out << "static_cast<" << enumType(e, globalEnums) << ">(";
|
||||
}
|
||||
out << m_this << varPath(n, m_cfg);
|
||||
if (!e->param.isEmpty()) {
|
||||
out << "[i]";
|
||||
}
|
||||
if (useEnumType) {
|
||||
out << ")";
|
||||
}
|
||||
out << ";\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::memberImmutableBody(const CfgEntry *e, bool globalEnums)
|
||||
{
|
||||
stream() << whitespace() << "return " << m_this << "isImmutable( QStringLiteral( \"";
|
||||
if (!e->param.isEmpty()) {
|
||||
stream() << QString(e->paramName).replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("%1")) << "\" ).arg( ";
|
||||
if (e->paramType == QLatin1String("Enum")) {
|
||||
stream() << "QLatin1String( ";
|
||||
|
||||
if (globalEnums) {
|
||||
stream() << enumName(e->param) << "ToString[i]";
|
||||
} else {
|
||||
stream() << enumName(e->param) << "::enumToString[i]";
|
||||
}
|
||||
|
||||
stream() << " )";
|
||||
} else {
|
||||
stream() << "i";
|
||||
}
|
||||
stream() << " )";
|
||||
} else {
|
||||
stream() << e->name << "\" )";
|
||||
}
|
||||
stream() << " );\n";
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression)
|
||||
{
|
||||
const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
|
||||
|
||||
m_stream << whitespace() << "if (";
|
||||
if (hasBody) {
|
||||
m_stream << "v != " << varExpression << " && ";
|
||||
}
|
||||
|
||||
const auto immutablefunction = immutableFunction(e->name, m_cfg.dpointer ? m_cfg.className : QString{});
|
||||
m_stream << "!" << m_this << immutablefunction << "(";
|
||||
if (!e->param.isEmpty()) {
|
||||
m_stream << " i ";
|
||||
}
|
||||
m_stream << "))";
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e)
|
||||
{
|
||||
// HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation.
|
||||
if (!e->min.isEmpty()) {
|
||||
if (e->min != QLatin1String("0") || !isUnsigned(e->type)) { // skip writing "if uint<0" (#187579)
|
||||
m_stream << whitespace() << "if (v < " << e->min << ")\n";
|
||||
m_stream << whitespace() << "{\n";
|
||||
m_stream << whitespace();
|
||||
addDebugMethod(m_stream, m_cfg, e->name);
|
||||
m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";\n";
|
||||
m_stream << whitespace() << " v = " << e->min << ";\n";
|
||||
m_stream << whitespace() << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!e->max.isEmpty()) {
|
||||
m_stream << '\n';
|
||||
m_stream << whitespace() << "if (v > " << e->max << ")\n";
|
||||
m_stream << whitespace() << "{\n";
|
||||
m_stream << whitespace();
|
||||
addDebugMethod(m_stream, m_cfg, e->name);
|
||||
m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";\n";
|
||||
m_stream << whitespace() << " v = " << e->max << ";\n";
|
||||
m_stream << whitespace() << "}\n\n";
|
||||
}
|
||||
|
||||
const QString varExpression = m_this + varPath(e->name, m_cfg) + (e->param.isEmpty() ? QString{} : QStringLiteral("[i]"));
|
||||
|
||||
// TODO: Remove this `hasBody` logic, always use an '{' for the if.
|
||||
const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
|
||||
|
||||
// m_this call creates an `if (someTest ...) that's just to long to throw over the code.
|
||||
createIfSetLogic(e, varExpression);
|
||||
m_stream << (hasBody ? " {" : "") << '\n';
|
||||
m_stream << whitespace() << " " << varExpression << " = v;\n";
|
||||
|
||||
const auto listSignal = e->signalList;
|
||||
for (const Signal &signal : std::as_const(listSignal)) {
|
||||
if (signal.modify) {
|
||||
m_stream << whitespace() << " Q_EMIT " << m_this << signal.name << "();\n";
|
||||
} else {
|
||||
m_stream << whitespace() << " " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg) << ".insert(" << signalEnumName(signal.name) << ");\n";
|
||||
}
|
||||
}
|
||||
if (hasBody) {
|
||||
m_stream << whitespace() << "}\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGCODEGENERATORBASE_H
|
||||
#define KCONFIGCODEGENERATORBASE_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "KConfigCommonStructs.h"
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
class CfgEntry;
|
||||
struct ParseResult;
|
||||
|
||||
/* This class manages the base of writing a C - Based code */
|
||||
class KConfigCodeGeneratorBase
|
||||
{
|
||||
public:
|
||||
enum ScopeFinalizer {
|
||||
None,
|
||||
Semicolon,
|
||||
};
|
||||
|
||||
KConfigCodeGeneratorBase(const QString &inputFileName, // The kcfg file
|
||||
const QString &baseDir, // where we should store the generated file
|
||||
const QString &fileName, // the name of the generated file
|
||||
const KConfigParameters ¶meters, // parameters passed to the generator
|
||||
ParseResult &parseResult // The pre processed configuration entries
|
||||
);
|
||||
virtual ~KConfigCodeGeneratorBase();
|
||||
|
||||
// iterates over the header list adding an #include directive.
|
||||
void addHeaders(const QStringList &header);
|
||||
|
||||
// Create all the namespace indentation levels based on the parsed result and parameters */
|
||||
void beginNamespaces();
|
||||
|
||||
// Closes all the namespaces adding lines with single '}'
|
||||
void endNamespaces();
|
||||
|
||||
// Add the correct amount of whitespace in the code.
|
||||
QString whitespace() const;
|
||||
|
||||
// start a block scope `{` and increase indentation level.
|
||||
void endScope(ScopeFinalizer finalizer = None);
|
||||
|
||||
// end a block scope `}` and decrease indentation level.
|
||||
void startScope();
|
||||
|
||||
// start writing to the output file
|
||||
virtual void start();
|
||||
|
||||
// save the result on the disk
|
||||
void save();
|
||||
|
||||
// Code Implementations
|
||||
// Implements the `Get` methods for the CfgEntry
|
||||
// TODO: write to the stream directly without returning a QString.
|
||||
QString memberAccessorBody(const CfgEntry *e, bool globalEnums) const;
|
||||
|
||||
// Implements the is<Param>Immutable for the CfgEntry
|
||||
void memberImmutableBody(const CfgEntry *e, bool globalEnums);
|
||||
|
||||
// Implements the `Set` methods for the CfgEntry
|
||||
void memberMutatorBody(const CfgEntry *e);
|
||||
|
||||
// This is the code that creates the logic for the Setter / Mutator.
|
||||
// It *just* creates the if test, no body. The reason is that just
|
||||
// the if test was more than 20 lines of code and hard to understand
|
||||
// what was happening in a bigger function.
|
||||
void createIfSetLogic(const CfgEntry *e, const QString &varExpression);
|
||||
|
||||
protected:
|
||||
/* advance the number of spaces for the indentation level */
|
||||
void indent();
|
||||
|
||||
/* reduce the number of spaces for the indentation level */
|
||||
void unindent();
|
||||
|
||||
QString inputFile() const
|
||||
{
|
||||
return m_inputFile;
|
||||
}
|
||||
QString fileName() const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
QString baseDir() const
|
||||
{
|
||||
return m_baseDir;
|
||||
}
|
||||
QString This() const
|
||||
{
|
||||
return m_this;
|
||||
}
|
||||
QString Const() const
|
||||
{
|
||||
return m_const;
|
||||
}
|
||||
KConfigParameters cfg() const
|
||||
{
|
||||
return m_cfg;
|
||||
}
|
||||
|
||||
// Can't be const.
|
||||
QTextStream &stream()
|
||||
{
|
||||
return m_stream;
|
||||
}
|
||||
|
||||
// HACK: This needs to be accessible because the HeaderGenerator actually modifies
|
||||
// it while running. Considering that this is a the result of the xml Parse, and not
|
||||
// the result of generating code, I consider this to be quite wrong - but moving the
|
||||
// changes away from the header generator only created more problems and I have to
|
||||
// investigate more.
|
||||
ParseResult &parseResult; // the result of the parsed kcfg file
|
||||
|
||||
private:
|
||||
QString m_inputFile; // the base file name, input file is based on this.
|
||||
|
||||
QString m_baseDir; // Where we are going to save the file
|
||||
QString m_fileName; // The file name
|
||||
|
||||
KConfigParameters m_cfg; // The parameters passed via the kcfgc file
|
||||
QTextStream m_stream; // the stream that operates in the file to write data.
|
||||
QFile m_file; // The file handler.
|
||||
|
||||
// Special access to `this->` and `const` thru the code.
|
||||
QString m_this;
|
||||
QString m_const;
|
||||
int m_indentLevel = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGCOMMONSTRUCTS_H
|
||||
#define KCONFIGCOMMONSTRUCTS_H
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
struct Param {
|
||||
QString name;
|
||||
QString type;
|
||||
};
|
||||
|
||||
struct Signal {
|
||||
QString name;
|
||||
QString label;
|
||||
QList<Param> arguments;
|
||||
bool modify = false;
|
||||
};
|
||||
|
||||
class CfgEntry
|
||||
{
|
||||
public:
|
||||
struct Choice {
|
||||
QString name;
|
||||
QString context;
|
||||
QString label;
|
||||
QString toolTip;
|
||||
QString whatsThis;
|
||||
QString val;
|
||||
|
||||
QString value() const
|
||||
{
|
||||
return !val.isEmpty() ? val : name;
|
||||
}
|
||||
};
|
||||
class Choices
|
||||
{
|
||||
public:
|
||||
Choices()
|
||||
{
|
||||
}
|
||||
Choices(const QList<Choice> &d, const QString &n, const QString &p)
|
||||
: prefix(p)
|
||||
, choices(d)
|
||||
, mName(n)
|
||||
{
|
||||
int i = n.indexOf(QLatin1String("::"));
|
||||
if (i >= 0) {
|
||||
mExternalQual = n.left(i + 2);
|
||||
}
|
||||
}
|
||||
QString prefix;
|
||||
QList<Choice> choices;
|
||||
const QString &name() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
const QString &externalQualifier() const
|
||||
{
|
||||
return mExternalQual;
|
||||
}
|
||||
bool external() const
|
||||
{
|
||||
return !mExternalQual.isEmpty();
|
||||
}
|
||||
|
||||
private:
|
||||
QString mName;
|
||||
QString mExternalQual;
|
||||
};
|
||||
|
||||
public:
|
||||
QString group;
|
||||
QString parentGroup;
|
||||
QString type;
|
||||
QString key;
|
||||
QString name;
|
||||
QString labelContext;
|
||||
QString label;
|
||||
QString toolTipContext;
|
||||
QString toolTip;
|
||||
QString whatsThisContext;
|
||||
QString whatsThis;
|
||||
QString code;
|
||||
QString defaultValue;
|
||||
QString param;
|
||||
QString paramName;
|
||||
QString paramType;
|
||||
Choices choices;
|
||||
QList<Signal> signalList;
|
||||
QStringList paramValues;
|
||||
QStringList paramDefaultValues;
|
||||
int paramMax;
|
||||
bool hidden;
|
||||
QString min;
|
||||
QString max;
|
||||
};
|
||||
|
||||
struct ParseResult {
|
||||
QString cfgFileName;
|
||||
bool cfgFileNameArg = false;
|
||||
bool cfgStateConfig = false;
|
||||
QList<Param> parameters;
|
||||
QList<Signal> signalList;
|
||||
QStringList includes;
|
||||
QList<CfgEntry *> entries;
|
||||
bool hasNonModifySignals = false;
|
||||
};
|
||||
|
||||
// TODO: Move those methods
|
||||
QString enumName(const QString &n);
|
||||
QString enumName(const QString &n, const CfgEntry::Choices &c);
|
||||
QString param(const QString &t);
|
||||
QString cppType(const QString &t);
|
||||
QString itemType(const QString &type);
|
||||
QString changeSignalName(const QString &n);
|
||||
|
||||
QString enumType(const CfgEntry *e, bool globalEnums);
|
||||
|
||||
QString getDefaultFunction(const QString &n, const QString &className = QString());
|
||||
QString setFunction(const QString &n, const QString &className = QString());
|
||||
QString getFunction(const QString &n, const QString &className = QString());
|
||||
QString immutableFunction(const QString &n, const QString &className = QString());
|
||||
|
||||
QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
QString signalEnumName(const QString &signalName);
|
||||
|
||||
bool isUnsigned(const QString &type);
|
||||
|
||||
// returns the name of an member variable
|
||||
// use itemPath to know the full path
|
||||
// like using d-> in case of dpointer
|
||||
QString varName(const QString &n, const KConfigParameters &cfg);
|
||||
|
||||
QString varPath(const QString &n, const KConfigParameters &cfg);
|
||||
|
||||
// returns the name of an item variable
|
||||
// use itemPath to know the full path
|
||||
// like using d-> in case of dpointer
|
||||
QString itemVar(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
// returns the name of the local inner item if there is one
|
||||
// (before wrapping with KConfigCompilerSignallingItem)
|
||||
// Otherwise return itemVar()
|
||||
QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
QString itemPath(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
QString filenameOnly(const QString &path);
|
||||
|
||||
QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
QString translatedString(const KConfigParameters &cfg,
|
||||
const QString &string,
|
||||
const QString &context = QString(),
|
||||
const QString ¶m = QString(),
|
||||
const QString ¶mValue = QString());
|
||||
|
||||
// TODO: Sanitize those functions.
|
||||
QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m = QString());
|
||||
|
||||
QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m = QString());
|
||||
|
||||
QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr = QString(), const QString &i = QString());
|
||||
|
||||
QString paramString(const QString &s, const CfgEntry *e, int i);
|
||||
QString paramString(const QString &group, const QList<Param> ¶meters);
|
||||
|
||||
QString defaultValue(const QString &t);
|
||||
QString memberGetDefaultBody(const CfgEntry *e);
|
||||
QString literalString(const QString &s);
|
||||
QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c);
|
||||
|
||||
void addQuotes(QString &s);
|
||||
void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigHeaderGenerator.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
#include <iostream>
|
||||
|
||||
KConfigHeaderGenerator::KConfigHeaderGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &result)
|
||||
: KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.headerExtension, cfg, result)
|
||||
{
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::start()
|
||||
{
|
||||
KConfigCodeGeneratorBase::start();
|
||||
startHeaderGuards();
|
||||
createHeaders();
|
||||
|
||||
beginNamespaces();
|
||||
|
||||
createForwardDeclarations();
|
||||
|
||||
doClassDefinition();
|
||||
|
||||
endNamespaces();
|
||||
endHeaderGuards();
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::doClassDefinition()
|
||||
{
|
||||
stream() << "class " << cfg().visibility << cfg().className << " : public " << cfg().inherits << '\n';
|
||||
startScope();
|
||||
|
||||
// Add Q_OBJECT macro if the config need signals.
|
||||
if (!parseResult.signalList.isEmpty() || cfg().generateProperties) {
|
||||
stream() << " Q_OBJECT\n";
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration) {
|
||||
stream() << " QML_ELEMENT\n";
|
||||
|
||||
if (cfg().singleton) {
|
||||
stream() << " QML_SINGLETON\n";
|
||||
}
|
||||
}
|
||||
stream() << " public:\n";
|
||||
implementEnums();
|
||||
createConstructor();
|
||||
createDestructor();
|
||||
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const QString returnType = (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) ? enumType(entry, cfg().globalEnums) : cppType(entry->type);
|
||||
|
||||
createSetters(entry);
|
||||
createProperties(entry, returnType);
|
||||
createImmutableProperty(entry);
|
||||
createGetters(entry, returnType);
|
||||
createImmutableGetters(entry);
|
||||
createDefaultValueMember(entry);
|
||||
createItemAcessors(entry, returnType);
|
||||
}
|
||||
|
||||
createSignals();
|
||||
stream() << " protected:\n";
|
||||
createSingleton();
|
||||
|
||||
// TODO: Move those to functions too.
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << whitespace() << "bool usrSave() override;\n";
|
||||
}
|
||||
|
||||
// Member variables
|
||||
if (!cfg().memberVariables.isEmpty() //
|
||||
&& cfg().memberVariables != QLatin1String("private") //
|
||||
&& cfg().memberVariables != QLatin1String("dpointer")) {
|
||||
stream() << " " << cfg().memberVariables << ":\n";
|
||||
}
|
||||
|
||||
// Class Parameters
|
||||
for (const auto ¶meter : std::as_const(parseResult.parameters)) {
|
||||
stream() << whitespace() << "" << cppType(parameter.type) << " mParam" << parameter.name << ";\n";
|
||||
}
|
||||
|
||||
createNonDPointerHelpers();
|
||||
createDPointer();
|
||||
|
||||
if (cfg().customAddons) {
|
||||
stream() << whitespace() << "// Include custom additions\n";
|
||||
stream() << whitespace() << "#include \"" << cfg().baseName << "_addons." << cfg().headerExtension << "\"\n";
|
||||
}
|
||||
|
||||
endScope(ScopeFinalizer::Semicolon);
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createHeaders()
|
||||
{
|
||||
addHeaders(cfg().headerIncludes);
|
||||
if (cfg().headerIncludes.size()) {
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
if (!cfg().singleton && parseResult.parameters.isEmpty()) {
|
||||
addHeaders({QStringLiteral("qglobal.h")});
|
||||
}
|
||||
|
||||
if (cfg().inherits == QLatin1String("KCoreConfigSkeleton")) {
|
||||
addHeaders({QStringLiteral("kcoreconfigskeleton.h")});
|
||||
} else {
|
||||
addHeaders({QStringLiteral("kconfigskeleton.h")});
|
||||
}
|
||||
|
||||
addHeaders({QStringLiteral("QCoreApplication"), QStringLiteral("QDebug")});
|
||||
if (!cfg().dpointer && parseResult.hasNonModifySignals) {
|
||||
addHeaders({QStringLiteral("QSet")});
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration) {
|
||||
addHeaders({QStringLiteral("qqmlintegration.h")});
|
||||
}
|
||||
|
||||
stream() << '\n';
|
||||
|
||||
addHeaders(parseResult.includes);
|
||||
if (parseResult.includes.size()) {
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::startHeaderGuards()
|
||||
{
|
||||
const bool hasNamespace = !cfg().nameSpace.isEmpty();
|
||||
const QString namespaceName = QString(QString(cfg().nameSpace).replace(QLatin1String("::"), QLatin1String("_"))).toUpper();
|
||||
const QString namespaceStr = hasNamespace ? namespaceName + QLatin1Char('_') : QString{};
|
||||
const QString defineName = namespaceStr + cfg().className.toUpper() + QStringLiteral("_H");
|
||||
|
||||
stream() << "#ifndef " << defineName << '\n';
|
||||
stream() << "#define " << defineName << '\n';
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::endHeaderGuards()
|
||||
{
|
||||
stream() << '\n';
|
||||
stream() << "#endif";
|
||||
stream() << '\n';
|
||||
// HACK: Original files ended with two last newlines, add them.
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices)
|
||||
{
|
||||
const QList<CfgEntry::Choice> chlist = choices.choices;
|
||||
|
||||
if (chlist.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList values;
|
||||
for (const auto &choice : std::as_const(chlist)) {
|
||||
values.append(choices.prefix + choice.name);
|
||||
}
|
||||
|
||||
if (choices.name().isEmpty()) {
|
||||
if (cfg().globalEnums) {
|
||||
stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
|
||||
if (cfg().generateProperties) {
|
||||
stream() << whitespace() << "Q_ENUM(" << enumName(entry->name, entry->choices) << ")\n";
|
||||
}
|
||||
} else {
|
||||
// Create an automatically named enum
|
||||
stream() << whitespace() << "class " << enumName(entry->name, entry->choices) << '\n';
|
||||
stream() << whitespace() << "{\n";
|
||||
stream() << whitespace() << " public:\n";
|
||||
stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n";
|
||||
stream() << whitespace() << "};\n";
|
||||
}
|
||||
} else if (!choices.external()) {
|
||||
// Create a named enum
|
||||
stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::implementValueEnums(const CfgEntry *entry, const QStringList &values)
|
||||
{
|
||||
if (values.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg().globalEnums) {
|
||||
// ### FIXME!!
|
||||
// make the following string table an index-based string search!
|
||||
// ###
|
||||
stream() << whitespace() << "enum " << enumName(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };\n";
|
||||
stream() << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];\n";
|
||||
} else {
|
||||
stream() << whitespace() << "class " << enumName(entry->param) << '\n';
|
||||
stream() << whitespace() << "{\n";
|
||||
stream() << whitespace() << " public:\n";
|
||||
stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n";
|
||||
stream() << whitespace() << " static const char* const enumToString[];\n";
|
||||
stream() << whitespace() << "};\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::implementEnums()
|
||||
{
|
||||
if (!parseResult.entries.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const CfgEntry::Choices &choices = entry->choices;
|
||||
const QStringList values = entry->paramValues;
|
||||
|
||||
implementChoiceEnums(entry, choices);
|
||||
implementValueEnums(entry, values);
|
||||
}
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createSignals()
|
||||
{
|
||||
// Signal definition.
|
||||
if (parseResult.signalList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << "\n enum {\n";
|
||||
|
||||
// HACK: Use C-Style for add a comma in all but the last element,
|
||||
// just to make the source generated code equal to the old one.
|
||||
// When we are sure, revert this to a range-based-for and just add
|
||||
// a last comma, as it's valid c++.
|
||||
for (int i = 0, end = parseResult.signalList.size(); i < end; i++) {
|
||||
auto signal = parseResult.signalList.at(i);
|
||||
stream() << whitespace() << " " << signalEnumName(signal.name) << " = " << (i + 1);
|
||||
if (i != end - 1) {
|
||||
stream() << ",\n";
|
||||
}
|
||||
}
|
||||
stream() << '\n';
|
||||
stream() << whitespace() << "};\n\n";
|
||||
|
||||
stream() << " Q_SIGNALS:";
|
||||
for (const Signal &signal : std::as_const(parseResult.signalList)) {
|
||||
stream() << '\n';
|
||||
if (!signal.label.isEmpty()) {
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " " << signal.label << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
}
|
||||
stream() << whitespace() << "void " << signal.name << "(";
|
||||
|
||||
auto it = signal.arguments.cbegin();
|
||||
const auto itEnd = signal.arguments.cend();
|
||||
while (it != itEnd) {
|
||||
Param argument = *it;
|
||||
QString type = param(argument.type);
|
||||
if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) {
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->name == argument.name) {
|
||||
type = enumType(entry, cfg().globalEnums);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stream() << type << " " << argument.name;
|
||||
if (++it != itEnd) {
|
||||
stream() << ", ";
|
||||
}
|
||||
}
|
||||
stream() << ");\n";
|
||||
}
|
||||
stream() << '\n';
|
||||
|
||||
stream() << " private:\n";
|
||||
stream() << whitespace() << "void itemChanged(quint64 signalFlag);\n";
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createDPointer()
|
||||
{
|
||||
if (!cfg().dpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use a private class for both member variables and items
|
||||
stream() << " private:\n";
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
|
||||
stream() << whitespace() << "";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << "static ";
|
||||
}
|
||||
stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << ";\n";
|
||||
}
|
||||
}
|
||||
stream() << whitespace() << "" << cfg().className << "Private *d;\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createConstructor()
|
||||
{
|
||||
if (cfg().singleton) {
|
||||
stream() << whitespace() << "static " << cfg().className << " *self();\n";
|
||||
|
||||
if (cfg().qmlRegistration) {
|
||||
stream() << whitespace() << "static " << cfg().className << " *create(QQmlEngine *, QJSEngine *);\n";
|
||||
}
|
||||
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << whitespace() << "static void instance(const QString& cfgfilename);\n";
|
||||
stream() << whitespace() << "static void instance(KSharedConfig::Ptr config);\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << whitespace() << "" << cfg().className << "(";
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
if (cfg().forceStringFilename) {
|
||||
stream() << " const QString &cfgfilename" << (parseResult.parameters.isEmpty() ? " = QString()" : ", ");
|
||||
} else if (parseResult.cfgStateConfig) {
|
||||
stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openStateConfig()" : ", ");
|
||||
} else {
|
||||
stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", ");
|
||||
}
|
||||
}
|
||||
if (cfg().forceStringFilename && parseResult.cfgStateConfig) {
|
||||
std::cerr << "One can not use ForceStringFilename and use the stateConfig attribute, consider "
|
||||
"removing the ForceStringFilename kcfgc option if you want to use state data"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
for (const auto ¶meter : std::as_const(parseResult.parameters)) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
stream() << ",";
|
||||
}
|
||||
|
||||
stream() << " " << param(parameter.type) << " " << parameter.name;
|
||||
}
|
||||
|
||||
if (cfg().parentInConstructor) {
|
||||
if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) {
|
||||
stream() << ",";
|
||||
}
|
||||
stream() << " QObject *parent = nullptr";
|
||||
}
|
||||
stream() << " );\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createDestructor()
|
||||
{
|
||||
stream() << whitespace() << "~" << cfg().className << "() override;\n\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createForwardDeclarations()
|
||||
{
|
||||
// Private class declaration
|
||||
if (cfg().dpointer) {
|
||||
stream() << "class " << cfg().className << "Private;\n\n";
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration && cfg().singleton) {
|
||||
stream() << "class QQmlEngine;\n";
|
||||
stream() << "class QJSEngine;\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createProperties(const CfgEntry *entry, const QString &returnType)
|
||||
{
|
||||
if (!cfg().generateProperties) {
|
||||
return;
|
||||
}
|
||||
stream() << whitespace() << "Q_PROPERTY(" << returnType << ' ' << getFunction(entry->name);
|
||||
stream() << " READ " << getFunction(entry->name);
|
||||
|
||||
if (cfg().allMutators || cfg().mutators.contains(entry->name)) {
|
||||
const QString signal = changeSignalName(entry->name);
|
||||
stream() << " WRITE " << setFunction(entry->name);
|
||||
stream() << " NOTIFY " << signal;
|
||||
|
||||
// If we have the modified signal, we'll also need
|
||||
// the changed signal as well
|
||||
Signal s;
|
||||
s.name = signal;
|
||||
s.modify = true;
|
||||
parseResult.signalList.append(s);
|
||||
} else {
|
||||
stream() << " CONSTANT";
|
||||
}
|
||||
stream() << ")\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createImmutableProperty(const CfgEntry *entry)
|
||||
{
|
||||
if (!cfg().generateProperties) {
|
||||
return;
|
||||
}
|
||||
stream() << whitespace();
|
||||
stream() << "Q_PROPERTY(bool " << immutableFunction(entry->name);
|
||||
stream() << " READ " << immutableFunction(entry->name);
|
||||
stream() << " CONSTANT)\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createSetters(const CfgEntry *entry)
|
||||
{
|
||||
// Manipulator
|
||||
if (!cfg().allMutators && !cfg().mutators.contains(entry->name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Set " << entry->label << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
|
||||
stream() << whitespace() << "void " << setFunction(entry->name) << "( ";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << cppType(entry->paramType) << " i, ";
|
||||
}
|
||||
|
||||
stream() << (cfg().useEnumTypes && entry->type == QLatin1String("Enum") ? enumType(entry, cfg().globalEnums) : param(entry->type));
|
||||
|
||||
stream() << " v )";
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
memberMutatorBody(entry);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
} else {
|
||||
stream() << ";\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createGetters(const CfgEntry *entry, const QString &returnType)
|
||||
{
|
||||
// Accessor
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Get " << entry->label << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
stream() << whitespace() << "";
|
||||
stream() << returnType;
|
||||
stream() << " " << getFunction(entry->name) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const();
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
stream() << whitespace() << memberAccessorBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
} else {
|
||||
stream() << ";\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createImmutableGetters(const CfgEntry *entry)
|
||||
{
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Is " << entry->label << " Immutable\n";
|
||||
stream() << whitespace() << "*/\n";
|
||||
// Immutable
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
stream() << whitespace() << "";
|
||||
stream() << "bool " << immutableFunction(entry->name) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const();
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
memberImmutableBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
} else {
|
||||
stream() << ";\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createItemAcessors(const CfgEntry *entry, const QString &returnType)
|
||||
{
|
||||
Q_UNUSED(returnType)
|
||||
|
||||
// Item accessor
|
||||
if (!cfg().itemAccessors) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString declType = entry->signalList.isEmpty() ? QStringLiteral("Item") + itemType(entry->type) : QStringLiteral("KConfigCompilerSignallingItem");
|
||||
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Get Item object corresponding to " << entry->name << "()" << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
stream() << whitespace() << declType << " *" << getFunction(entry->name) << "Item(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")";
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
stream() << whitespace() << itemAccessorBody(entry, cfg());
|
||||
endScope();
|
||||
} else {
|
||||
stream() << ";\n";
|
||||
}
|
||||
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createDefaultValueMember(const CfgEntry *entry)
|
||||
{
|
||||
// Default value Accessor
|
||||
if (!((cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) && !entry->defaultValue.isEmpty())) {
|
||||
return;
|
||||
}
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Get " << entry->label << " default value\n";
|
||||
stream() << whitespace() << "*/\n";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
stream() << whitespace() << "";
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << enumType(entry, cfg().globalEnums);
|
||||
} else {
|
||||
stream() << cppType(entry->type);
|
||||
}
|
||||
stream() << " " << getDefaultFunction(entry->name) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
stream() << whitespace() << "{\n";
|
||||
stream() << whitespace() << " return ";
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << "static_cast<" << enumType(entry, cfg().globalEnums) << ">(";
|
||||
}
|
||||
stream() << getDefaultFunction(entry->name) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " i ";
|
||||
}
|
||||
stream() << ")";
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << ")";
|
||||
}
|
||||
stream() << ";\n";
|
||||
stream() << whitespace() << "}\n";
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createSingleton()
|
||||
{
|
||||
// Private constructor for singleton
|
||||
if (!cfg().singleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << whitespace() << "" << cfg().className << "(";
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << "KSharedConfig::Ptr config";
|
||||
}
|
||||
if (cfg().parentInConstructor) {
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << ", ";
|
||||
}
|
||||
stream() << "QObject *parent = nullptr";
|
||||
}
|
||||
stream() << ");\n";
|
||||
stream() << whitespace() << "friend class " << cfg().className << "Helper;\n\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createNonDPointerHelpers()
|
||||
{
|
||||
if (cfg().memberVariables == QLatin1String("dpointer")) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString group;
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->group != group) {
|
||||
group = entry->group;
|
||||
stream() << '\n';
|
||||
stream() << whitespace() << "// " << group << '\n';
|
||||
}
|
||||
stream() << whitespace() << "" << cppType(entry->type) << " " << varName(entry->name, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
|
||||
if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
|
||||
stream() << whitespace() << "";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << "static ";
|
||||
}
|
||||
stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
stream() << "\n private:\n";
|
||||
if (cfg().itemAccessors) {
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const QString declType =
|
||||
entry->signalList.isEmpty() ? QStringLiteral("Item") + itemType(entry->type) : QStringLiteral("KConfigCompilerSignallingItem");
|
||||
stream() << whitespace() << declType << " *" << itemVar(entry, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << whitespace() << "QSet<quint64> " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGHEADERGENERATOR_H
|
||||
#define KCONFIGHEADERGENERATOR_H
|
||||
|
||||
#include "KConfigCodeGeneratorBase.h"
|
||||
#include "KConfigCommonStructs.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
class KConfigParameters;
|
||||
class CfgEntry;
|
||||
class QTextStream;
|
||||
struct ParseResult;
|
||||
|
||||
class KConfigHeaderGenerator : public KConfigCodeGeneratorBase
|
||||
{
|
||||
public:
|
||||
KConfigHeaderGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters ¶meters, ParseResult &parseResult);
|
||||
|
||||
void start() override;
|
||||
|
||||
private:
|
||||
void startHeaderGuards();
|
||||
void endHeaderGuards();
|
||||
|
||||
void implementEnums();
|
||||
void implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices);
|
||||
void implementValueEnums(const CfgEntry *entry, const QStringList &values);
|
||||
|
||||
void doClassDefinition();
|
||||
void createHeaders();
|
||||
void createDPointer();
|
||||
void createNonDPointerHelpers();
|
||||
|
||||
void createConstructor();
|
||||
void createDestructor();
|
||||
void createForwardDeclarations();
|
||||
void createSingleton();
|
||||
void createSignals();
|
||||
|
||||
void createSetters(const CfgEntry *entry);
|
||||
void createItemAcessors(const CfgEntry *entry, const QString &returnType);
|
||||
void createGetters(const CfgEntry *entry, const QString &returnType);
|
||||
void createImmutableGetters(const CfgEntry *entry);
|
||||
void createProperties(const CfgEntry *entry, const QString &returnType);
|
||||
void createImmutableProperty(const CfgEntry *entry);
|
||||
void createDefaultValueMember(const CfgEntry *entry);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
KConfigParameters::KConfigParameters(const QString &codegenFilename)
|
||||
{
|
||||
if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) {
|
||||
std::cerr << "Codegen options file must have extension .kcfgc" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
baseName = QFileInfo(codegenFilename).fileName();
|
||||
baseName = baseName.left(baseName.length() - 6);
|
||||
|
||||
// Configure the compiler with some settings
|
||||
QSettings codegenConfig(codegenFilename, QSettings::IniFormat);
|
||||
|
||||
nameSpace = codegenConfig.value(QStringLiteral("NameSpace")).toString();
|
||||
className = codegenConfig.value(QStringLiteral("ClassName")).toString();
|
||||
if (className.isEmpty()) {
|
||||
std::cerr << "Class name missing" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
inherits = codegenConfig.value(QStringLiteral("Inherits")).toString();
|
||||
if (inherits.isEmpty()) {
|
||||
inherits = QStringLiteral("KConfigSkeleton");
|
||||
}
|
||||
visibility = codegenConfig.value(QStringLiteral("Visibility")).toString();
|
||||
if (!visibility.isEmpty()) {
|
||||
visibility += QLatin1Char(' ');
|
||||
}
|
||||
parentInConstructor = codegenConfig.value(QStringLiteral("ParentInConstructor"), false).toBool();
|
||||
forceStringFilename = codegenConfig.value(QStringLiteral("ForceStringFilename"), false).toBool();
|
||||
singleton = codegenConfig.value(QStringLiteral("Singleton"), false).toBool();
|
||||
staticAccessors = singleton;
|
||||
customAddons = codegenConfig.value(QStringLiteral("CustomAdditions"), false).toBool();
|
||||
memberVariables = codegenConfig.value(QStringLiteral("MemberVariables")).toString();
|
||||
dpointer = (memberVariables == QLatin1String("dpointer"));
|
||||
headerIncludes = codegenConfig.value(QStringLiteral("IncludeFiles"), QStringList()).toStringList();
|
||||
sourceIncludes = codegenConfig.value(QStringLiteral("SourceIncludeFiles"), QStringList()).toStringList();
|
||||
mutators = codegenConfig.value(QStringLiteral("Mutators"), QStringList()).toStringList();
|
||||
allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == QLatin1String("true")));
|
||||
itemAccessors = codegenConfig.value(QStringLiteral("ItemAccessors"), false).toBool();
|
||||
setUserTexts = codegenConfig.value(QStringLiteral("SetUserTexts"), false).toBool();
|
||||
defaultGetters = codegenConfig.value(QStringLiteral("DefaultValueGetters"), QStringList()).toStringList();
|
||||
allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == QLatin1String("true"));
|
||||
notifiers = codegenConfig.value(QStringLiteral("Notifiers"), QStringList()).toStringList();
|
||||
allNotifiers = ((notifiers.count() == 1) && (notifiers.at(0).toLower() == QLatin1String("true")));
|
||||
globalEnums = codegenConfig.value(QStringLiteral("GlobalEnums"), false).toBool();
|
||||
useEnumTypes = codegenConfig.value(QStringLiteral("UseEnumTypes"), false).toBool();
|
||||
const QString trString = codegenConfig.value(QStringLiteral("TranslationSystem")).toString().toLower();
|
||||
generateProperties = codegenConfig.value(QStringLiteral("GenerateProperties"), false).toBool();
|
||||
if (trString == QLatin1String("kde")) {
|
||||
translationSystem = KdeTranslation;
|
||||
translationDomain = codegenConfig.value(QStringLiteral("TranslationDomain")).toString();
|
||||
} else {
|
||||
if (!trString.isEmpty() && trString != QLatin1String("qt")) {
|
||||
std::cerr << "Unknown translation system, falling back to Qt tr()" << std::endl;
|
||||
}
|
||||
translationSystem = QtTranslation;
|
||||
}
|
||||
qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString();
|
||||
headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString();
|
||||
qmlRegistration = codegenConfig.value(QStringLiteral("QmlRegistration")).toBool();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGXTPARAMETERS_H
|
||||
#define KCONFIGXTPARAMETERS_H
|
||||
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
Configuration Compiler Configuration
|
||||
*/
|
||||
class KConfigParameters
|
||||
{
|
||||
public:
|
||||
KConfigParameters(const QString &codegenFilename);
|
||||
|
||||
public:
|
||||
enum TranslationSystem {
|
||||
QtTranslation,
|
||||
KdeTranslation,
|
||||
};
|
||||
|
||||
// These are read from the .kcfgc configuration file
|
||||
QString nameSpace; // The namespace for the class to be generated
|
||||
QString className; // The class name to be generated
|
||||
QString inherits; // The class the generated class inherits (if empty, from KConfigSkeleton)
|
||||
QString visibility;
|
||||
bool parentInConstructor; // The class has the optional parent parameter in its constructor
|
||||
bool forceStringFilename;
|
||||
bool singleton; // The class will be a singleton
|
||||
bool staticAccessors; // provide or not static accessors
|
||||
bool customAddons;
|
||||
QString memberVariables;
|
||||
QStringList headerIncludes;
|
||||
QStringList sourceIncludes;
|
||||
QStringList mutators;
|
||||
QStringList defaultGetters;
|
||||
QStringList notifiers;
|
||||
QString qCategoryLoggingName;
|
||||
QString headerExtension;
|
||||
bool allMutators;
|
||||
bool setUserTexts;
|
||||
bool allDefaultGetters;
|
||||
bool dpointer;
|
||||
bool globalEnums;
|
||||
bool useEnumTypes;
|
||||
bool itemAccessors;
|
||||
bool allNotifiers;
|
||||
TranslationSystem translationSystem;
|
||||
QString translationDomain;
|
||||
bool generateProperties;
|
||||
QString baseName;
|
||||
bool qmlRegistration;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigSourceGenerator.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
KConfigSourceGenerator::KConfigSourceGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &parseResult)
|
||||
: KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1String(".cpp"), cfg, parseResult)
|
||||
{
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::start()
|
||||
{
|
||||
KConfigCodeGeneratorBase::start();
|
||||
stream() << '\n';
|
||||
createHeaders();
|
||||
|
||||
if (!cfg().nameSpace.isEmpty()) {
|
||||
stream() << "using namespace " << cfg().nameSpace << ";";
|
||||
stream() << "\n\n";
|
||||
}
|
||||
|
||||
createPrivateDPointerImplementation();
|
||||
createSingletonImplementation();
|
||||
createPreamble();
|
||||
doConstructor();
|
||||
doGetterSetterDPointerMode();
|
||||
createDefaultValueGetterSetter();
|
||||
createDestructor();
|
||||
createNonModifyingSignalsHelper();
|
||||
createSignalFlagsHandler();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createHeaders()
|
||||
{
|
||||
QString headerName = cfg().baseName + QLatin1Char('.') + cfg().headerExtension;
|
||||
|
||||
// TODO: Make addQuotes return a string instead of replacing it inplace.
|
||||
addQuotes(headerName);
|
||||
|
||||
addHeaders({headerName});
|
||||
stream() << '\n';
|
||||
|
||||
addHeaders(cfg().sourceIncludes);
|
||||
if (cfg().setUserTexts && cfg().translationSystem == KConfigParameters::KdeTranslation) {
|
||||
addHeaders({QStringLiteral("klocalizedstring.h")});
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
// Header required by singleton implementation
|
||||
if (cfg().singleton) {
|
||||
addHeaders({QStringLiteral("qglobal.h"), QStringLiteral("QFile")});
|
||||
|
||||
// HACK: Add single line to fix test.
|
||||
if (cfg().singleton && parseResult.cfgFileNameArg) {
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg().singleton && parseResult.cfgFileNameArg) {
|
||||
addHeaders({QStringLiteral("QDebug")});
|
||||
}
|
||||
|
||||
if (cfg().dpointer && parseResult.hasNonModifySignals) {
|
||||
addHeaders({QStringLiteral("QSet")});
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration && cfg().singleton) {
|
||||
addHeaders({QStringLiteral("QQmlEngine")});
|
||||
}
|
||||
|
||||
if (cfg().singleton) {
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createPrivateDPointerImplementation()
|
||||
{
|
||||
// private class implementation
|
||||
if (!cfg().dpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString group;
|
||||
beginNamespaces();
|
||||
stream() << "class " << cfg().className << "Private\n";
|
||||
stream() << "{\n";
|
||||
stream() << " public:\n";
|
||||
|
||||
// Create Members
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->group != group) {
|
||||
group = entry->group;
|
||||
stream() << '\n';
|
||||
stream() << " // " << group << '\n';
|
||||
}
|
||||
stream() << " " << cppType(entry->type) << " " << varName(entry->name, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
}
|
||||
stream() << "\n // items\n";
|
||||
|
||||
// Create Items.
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const QString declType = entry->signalList.isEmpty() ? QString(cfg().inherits + QStringLiteral("::Item") + itemType(entry->type))
|
||||
: QStringLiteral("KConfigCompilerSignallingItem");
|
||||
|
||||
stream() << " " << declType << " *" << itemVar(entry, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
}
|
||||
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << " QSet<quint64> " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n";
|
||||
}
|
||||
|
||||
stream() << "};\n\n";
|
||||
endNamespaces();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createSingletonImplementation()
|
||||
{
|
||||
// Singleton implementation
|
||||
if (!cfg().singleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginNamespaces();
|
||||
stream() << "class " << cfg().className << "Helper\n";
|
||||
stream() << '{' << '\n';
|
||||
stream() << " public:\n";
|
||||
stream() << " " << cfg().className << "Helper() : q(nullptr) {}\n";
|
||||
stream() << " ~" << cfg().className << "Helper() { delete q; q = nullptr; }\n";
|
||||
stream() << " " << cfg().className << "Helper(const " << cfg().className << "Helper&) = delete;\n";
|
||||
stream() << " " << cfg().className << "Helper& operator=(const " << cfg().className << "Helper&) = delete;\n";
|
||||
stream() << " " << cfg().className << " *q;\n";
|
||||
stream() << "};\n";
|
||||
endNamespaces();
|
||||
|
||||
stream() << "Q_GLOBAL_STATIC(" << cfg().className << "Helper, s_global" << cfg().className << ")\n";
|
||||
|
||||
stream() << cfg().className << " *" << cfg().className << "::self()\n";
|
||||
stream() << "{\n";
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << " if (!s_global" << cfg().className << "()->q)\n";
|
||||
stream() << " qFatal(\"you need to call " << cfg().className << "::instance before using\");\n";
|
||||
} else {
|
||||
stream() << " if (!s_global" << cfg().className << "()->q) {\n";
|
||||
stream() << " new " << cfg().className << ';' << '\n';
|
||||
stream() << " s_global" << cfg().className << "()->q->read();\n";
|
||||
stream() << " }\n\n";
|
||||
}
|
||||
stream() << " return s_global" << cfg().className << "()->q;\n";
|
||||
stream() << "}\n\n";
|
||||
|
||||
if (cfg().qmlRegistration && cfg().singleton) {
|
||||
stream() << cfg().className << " *" << cfg().className << "::create(QQmlEngine *, QJSEngine *)\n";
|
||||
stream() << "{\n";
|
||||
stream() << " QQmlEngine::setObjectOwnership(self(), QQmlEngine::CppOwnership);\n";
|
||||
stream() << " return self();\n";
|
||||
stream() << "}\n\n";
|
||||
}
|
||||
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
auto instance = [this](const QString &type, const QString &arg, bool isString) {
|
||||
stream() << "void " << cfg().className << "::instance(" << type << " " << arg << ")\n";
|
||||
stream() << "{\n";
|
||||
stream() << " if (s_global" << cfg().className << "()->q) {\n";
|
||||
stream() << " qDebug() << \"" << cfg().className << "::instance called after the first use - ignoring\";\n";
|
||||
stream() << " return;\n";
|
||||
stream() << " }\n";
|
||||
stream() << " new " << cfg().className << "(";
|
||||
if (parseResult.cfgStateConfig) {
|
||||
stream() << "KSharedConfig::openStateConfig(" << arg << ")";
|
||||
} else if (isString) {
|
||||
stream() << "KSharedConfig::openConfig(" << arg << ")";
|
||||
} else {
|
||||
stream() << "std::move(" << arg << ")";
|
||||
}
|
||||
stream() << ");\n";
|
||||
stream() << " s_global" << cfg().className << "()->q->read();\n";
|
||||
stream() << "}\n\n";
|
||||
};
|
||||
instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true);
|
||||
instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false);
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createPreamble()
|
||||
{
|
||||
QString cppPreamble;
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->paramValues.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cppPreamble += QStringLiteral("const char* const ") + cfg().className + QStringLiteral("::") + enumName(entry->param);
|
||||
cppPreamble += cfg().globalEnums
|
||||
? QStringLiteral("ToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n")
|
||||
: QStringLiteral("::enumToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n");
|
||||
}
|
||||
|
||||
if (!cppPreamble.isEmpty()) {
|
||||
stream() << cppPreamble << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createConstructorParameterList()
|
||||
{
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
if (!cfg().forceStringFilename) {
|
||||
stream() << " KSharedConfig::Ptr config";
|
||||
} else {
|
||||
stream() << " const QString& config";
|
||||
}
|
||||
stream() << (parseResult.parameters.isEmpty() ? "" : ",");
|
||||
}
|
||||
|
||||
for (auto it = parseResult.parameters.cbegin(); it != parseResult.parameters.cend(); ++it) {
|
||||
if (it != parseResult.parameters.cbegin()) {
|
||||
stream() << ",";
|
||||
}
|
||||
stream() << " " << param((*it).type) << " " << (*it).name;
|
||||
}
|
||||
|
||||
if (cfg().parentInConstructor) {
|
||||
if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) {
|
||||
stream() << ",";
|
||||
}
|
||||
stream() << " QObject *parent";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createParentConstructorCall()
|
||||
{
|
||||
stream() << cfg().inherits << "(";
|
||||
if (parseResult.cfgStateConfig) {
|
||||
stream() << " KSharedConfig::openStateConfig(QStringLiteral( \"" << parseResult.cfgFileName << "\") ";
|
||||
} else if (!parseResult.cfgFileName.isEmpty()) {
|
||||
stream() << " QStringLiteral( \"" << parseResult.cfgFileName << "\" ";
|
||||
}
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
if (!cfg().forceStringFilename) {
|
||||
stream() << " std::move( config ) ";
|
||||
} else {
|
||||
stream() << " config ";
|
||||
}
|
||||
}
|
||||
if (!parseResult.cfgFileName.isEmpty()) {
|
||||
stream() << ") ";
|
||||
}
|
||||
stream() << ")\n";
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createInitializerList()
|
||||
{
|
||||
for (const auto ¶meter : std::as_const(parseResult.parameters)) {
|
||||
stream() << " , mParam" << parameter.name << "(" << parameter.name << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createEnums(const CfgEntry *entry)
|
||||
{
|
||||
if (entry->type != QLatin1String("Enum")) {
|
||||
return;
|
||||
}
|
||||
stream() << " QList<" << cfg().inherits << "::ItemEnum::Choice> values" << entry->name << ";\n";
|
||||
|
||||
for (const auto &choice : std::as_const(entry->choices.choices)) {
|
||||
stream() << " {\n";
|
||||
stream() << " " << cfg().inherits << "::ItemEnum::Choice choice;\n";
|
||||
stream() << " choice.name = QStringLiteral(\"" << choice.name << "\");\n";
|
||||
if (cfg().setUserTexts) {
|
||||
if (!choice.label.isEmpty()) {
|
||||
stream() << " choice.label = " << translatedString(cfg(), choice.label, choice.context) << ";\n";
|
||||
}
|
||||
if (!choice.toolTip.isEmpty()) {
|
||||
stream() << " choice.toolTip = " << translatedString(cfg(), choice.toolTip, choice.context) << ";\n";
|
||||
}
|
||||
if (!choice.whatsThis.isEmpty()) {
|
||||
stream() << " choice.whatsThis = " << translatedString(cfg(), choice.whatsThis, choice.context) << ";\n";
|
||||
}
|
||||
}
|
||||
stream() << " values" << entry->name << ".append( choice );\n";
|
||||
stream() << " }\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key)
|
||||
{
|
||||
const QString itemVarStr = itemPath(entry, cfg());
|
||||
const QString innerItemVarStr = innerItemVar(entry, cfg());
|
||||
if (!entry->signalList.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << " = " << newInnerItem(entry, key, entry->defaultValue, cfg()) << '\n';
|
||||
}
|
||||
|
||||
stream() << " " << itemVarStr << " = " << newItem(entry, key, entry->defaultValue, cfg()) << '\n';
|
||||
|
||||
if (!entry->min.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n";
|
||||
}
|
||||
|
||||
if (!entry->max.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n";
|
||||
}
|
||||
|
||||
if (cfg().setUserTexts) {
|
||||
stream() << userTextsFunctions(entry, cfg());
|
||||
}
|
||||
|
||||
if (cfg().allNotifiers || cfg().notifiers.contains(entry->name)) {
|
||||
stream() << " " << itemVarStr << "->setWriteFlags(KConfigBase::Notify);\n";
|
||||
}
|
||||
|
||||
for (const CfgEntry::Choice &choice : std::as_const(entry->choices.choices)) {
|
||||
if (!choice.val.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val
|
||||
<< "\" ));\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry->parentGroup.isEmpty()) {
|
||||
stream() << " " << itemVarStr << "->setGroup(cg" << QString(entry->group).remove(QRegularExpression(QStringLiteral("\\W"))) << ");\n";
|
||||
}
|
||||
|
||||
stream() << " addItem( " << itemVarStr;
|
||||
QString quotedName = entry->name;
|
||||
addQuotes(quotedName);
|
||||
if (quotedName != key) {
|
||||
stream() << ", QStringLiteral( \"" << entry->name << "\" )";
|
||||
}
|
||||
stream() << " );\n";
|
||||
}
|
||||
|
||||
// TODO : Some compiler option won't work or generate bogus settings file.
|
||||
// * Does not manage properly Notifiers=true kcfgc option for parameterized entries :
|
||||
// ** KConfigCompilerSignallingItem generated with wrong userData parameter (4th one).
|
||||
// ** setWriteFlags() is missing.
|
||||
// * Q_PROPERTY signal won't work
|
||||
void KConfigSourceGenerator::createIndexedEntry(const CfgEntry *entry, const QString &key)
|
||||
{
|
||||
for (int i = 0; i <= entry->paramMax; i++) {
|
||||
const QString argBracket = QStringLiteral("[%1]").arg(i);
|
||||
const QString innerItemVarStr = innerItemVar(entry, cfg()) + argBracket;
|
||||
|
||||
const QString defaultStr = !entry->paramDefaultValues[i].isEmpty() ? entry->paramDefaultValues[i]
|
||||
: !entry->defaultValue.isEmpty() ? paramString(entry->defaultValue, entry, i)
|
||||
: defaultValue(entry->type);
|
||||
|
||||
if (!entry->signalList.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << " = " << newInnerItem(entry, paramString(key, entry, i), defaultStr, cfg(), argBracket) << '\n';
|
||||
}
|
||||
|
||||
const QString itemVarStr = itemPath(entry, cfg()) + argBracket;
|
||||
|
||||
stream() << " " << itemVarStr << " = " << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), argBracket) << '\n';
|
||||
|
||||
if (!entry->min.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n";
|
||||
}
|
||||
if (!entry->max.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n";
|
||||
}
|
||||
|
||||
for (const CfgEntry::Choice &choice : std::as_const(entry->choices.choices)) {
|
||||
if (!choice.val.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val
|
||||
<< "\" ));\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg().setUserTexts) {
|
||||
stream() << userTextsFunctions(entry, cfg(), itemVarStr, entry->paramName);
|
||||
}
|
||||
|
||||
// Make mutators for enum parameters work by adding them with $(..) replaced by the
|
||||
// param name. The check for isImmutable in the set* functions doesn't have the param
|
||||
// name available, just the corresponding enum value (int), so we need to store the
|
||||
// param names in a separate static list!.
|
||||
const bool isEnum = entry->paramType == QLatin1String("Enum");
|
||||
const QString arg = isEnum ? entry->paramValues[i] : QString::number(i);
|
||||
|
||||
QString paramName = entry->paramName;
|
||||
|
||||
stream() << " addItem( " << itemVarStr << ", QStringLiteral( \"";
|
||||
stream() << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), QLatin1String("%1")).arg(arg);
|
||||
stream() << "\" ) );\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::handleCurrentGroupChange(const CfgEntry *entry)
|
||||
{
|
||||
if (entry->group == mCurrentGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: This fixes one spacing line in the diff. Remove this in the future and adapt the testcases.
|
||||
static bool first = true;
|
||||
if (!entry->group.isEmpty()) {
|
||||
if (!first) {
|
||||
stream() << '\n';
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
mCurrentGroup = entry->group;
|
||||
|
||||
if (!entry->parentGroup.isEmpty()) {
|
||||
QString parentGroup = QString(entry->parentGroup).remove(QRegularExpression(QStringLiteral("\\W")));
|
||||
if (!mConfigGroupList.contains(parentGroup)) {
|
||||
stream() << " KConfigGroup cg" << parentGroup << "(this->config(), " << paramString(entry->parentGroup, parseResult.parameters) << ");\n";
|
||||
mConfigGroupList << parentGroup;
|
||||
}
|
||||
QString currentGroup = QString(mCurrentGroup).remove(QRegularExpression(QStringLiteral("\\W")));
|
||||
if (!mConfigGroupList.contains(currentGroup)) {
|
||||
stream() << " KConfigGroup cg" << currentGroup << " = cg" << QString(entry->parentGroup).remove(QRegularExpression(QStringLiteral("\\W")))
|
||||
<< ".group(" << paramString(mCurrentGroup, parseResult.parameters) << ");\n";
|
||||
mConfigGroupList << currentGroup;
|
||||
}
|
||||
} else {
|
||||
stream() << " setCurrentGroup( " << paramString(mCurrentGroup, parseResult.parameters) << " );";
|
||||
stream() << "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::doConstructor()
|
||||
{
|
||||
// Constructor
|
||||
stream() << cfg().className << "::" << cfg().className << "(";
|
||||
createConstructorParameterList();
|
||||
stream() << " )\n";
|
||||
stream() << " : ";
|
||||
createParentConstructorCall();
|
||||
createInitializerList();
|
||||
|
||||
stream() << "{\n";
|
||||
|
||||
if (cfg().parentInConstructor) {
|
||||
stream() << " setParent(parent);\n";
|
||||
}
|
||||
|
||||
if (cfg().dpointer) {
|
||||
stream() << " d = new " << cfg().className << "Private;\n";
|
||||
}
|
||||
|
||||
// Needed in case the singleton class is used as baseclass for
|
||||
// another singleton.
|
||||
if (cfg().singleton) {
|
||||
stream() << " Q_ASSERT(!s_global" << cfg().className << "()->q);\n";
|
||||
stream() << " s_global" << cfg().className << "()->q = this;\n";
|
||||
}
|
||||
|
||||
if (!parseResult.signalList.isEmpty()) {
|
||||
// this cast to base-class pointer-to-member is valid C++
|
||||
// https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/
|
||||
stream() << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction ="
|
||||
<< " static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&" << cfg().className << "::itemChanged);\n";
|
||||
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
handleCurrentGroupChange(entry);
|
||||
|
||||
const QString key = paramString(entry->key, parseResult.parameters);
|
||||
if (!entry->code.isEmpty()) {
|
||||
stream() << entry->code << '\n';
|
||||
}
|
||||
createEnums(entry);
|
||||
|
||||
stream() << itemDeclaration(entry, cfg());
|
||||
|
||||
if (entry->param.isEmpty()) {
|
||||
createNormalEntry(entry, key);
|
||||
} else {
|
||||
createIndexedEntry(entry, key);
|
||||
}
|
||||
}
|
||||
|
||||
stream() << "}\n\n";
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createGetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
// Accessor
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << enumType(entry, cfg().globalEnums);
|
||||
} else {
|
||||
stream() << cppType(entry->type);
|
||||
}
|
||||
|
||||
stream() << " " << getFunction(entry->name, cfg().className) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
startScope();
|
||||
// HACK: Fix memberAccessorBody
|
||||
stream() << " " << memberAccessorBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createImmutableGetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
stream() << whitespace() << "";
|
||||
stream() << "bool "
|
||||
<< " " << immutableFunction(entry->name, cfg().className) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
startScope();
|
||||
memberImmutableBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createSetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
// Manipulator
|
||||
if (!(cfg().allMutators || cfg().mutators.contains(entry->name))) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << "void " << setFunction(entry->name, cfg().className) << "( ";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << cppType(entry->paramType) << " i, ";
|
||||
}
|
||||
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << enumType(entry, cfg().globalEnums);
|
||||
} else {
|
||||
stream() << param(entry->type);
|
||||
}
|
||||
stream() << " v )\n";
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
startScope();
|
||||
memberMutatorBody(entry);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createItemGetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
// Item accessor
|
||||
if (!cfg().itemAccessors) {
|
||||
return;
|
||||
}
|
||||
stream() << '\n';
|
||||
stream() << cfg().inherits << "::Item" << itemType(entry->type) << " *" << getFunction(entry->name, cfg().className) << "Item(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")\n";
|
||||
startScope();
|
||||
stream() << " " << itemAccessorBody(entry, cfg());
|
||||
endScope();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::doGetterSetterDPointerMode()
|
||||
{
|
||||
if (!cfg().dpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// setters and getters go in Cpp if in dpointer mode
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
createSetterDPointerMode(entry);
|
||||
createGetterDPointerMode(entry);
|
||||
createImmutableGetterDPointerMode(entry);
|
||||
createItemGetterDPointerMode(entry);
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createDefaultValueGetterSetter()
|
||||
{
|
||||
// default value getters always go in Cpp
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
QString n = entry->name;
|
||||
QString t = entry->type;
|
||||
|
||||
// Default value Accessor, as "helper" function
|
||||
if ((cfg().allDefaultGetters || cfg().defaultGetters.contains(n)) && !entry->defaultValue.isEmpty()) {
|
||||
stream() << cppType(t) << " " << getDefaultFunction(n, cfg().className) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
startScope();
|
||||
stream() << memberGetDefaultBody(entry) << '\n';
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createDestructor()
|
||||
{
|
||||
stream() << cfg().className << "::~" << cfg().className << "()\n";
|
||||
startScope();
|
||||
if (cfg().dpointer) {
|
||||
stream() << " delete d;\n";
|
||||
}
|
||||
if (cfg().singleton) {
|
||||
const QString qgs = QLatin1String("s_global") + cfg().className;
|
||||
stream() << " if (" << qgs << ".exists() && !" << qgs << ".isDestroyed()) {\n";
|
||||
stream() << " " << qgs << "()->q = nullptr;\n";
|
||||
stream() << " }\n";
|
||||
}
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createNonModifyingSignalsHelper()
|
||||
{
|
||||
if (!parseResult.hasNonModifySignals) {
|
||||
return;
|
||||
}
|
||||
stream() << "bool " << cfg().className << "::"
|
||||
<< "usrSave()\n";
|
||||
startScope();
|
||||
stream() << " const bool res = " << cfg().inherits << "::usrSave();\n";
|
||||
stream() << " if (!res) return false;\n\n";
|
||||
for (const Signal &signal : std::as_const(parseResult.signalList)) {
|
||||
if (signal.modify) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stream() << " if (" << varPath(QStringLiteral("settingsChanged"), cfg()) << ".contains(" << signalEnumName(signal.name) << "))\n";
|
||||
stream() << " Q_EMIT " << signal.name << "(";
|
||||
auto it = signal.arguments.cbegin();
|
||||
const auto itEnd = signal.arguments.cend();
|
||||
while (it != itEnd) {
|
||||
Param argument = *it;
|
||||
bool cast = false;
|
||||
if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) {
|
||||
for (int i = 0, end = parseResult.entries.count(); i < end; ++i) {
|
||||
if (parseResult.entries.at(i)->name == argument.name) {
|
||||
stream() << "static_cast<" << enumType(parseResult.entries.at(i), cfg().globalEnums) << ">(";
|
||||
cast = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stream() << varPath(argument.name, cfg());
|
||||
if (cast) {
|
||||
stream() << ")";
|
||||
}
|
||||
if (++it != itEnd) {
|
||||
stream() << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
stream() << ");\n";
|
||||
}
|
||||
|
||||
stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << ".clear();\n";
|
||||
stream() << " return true;\n";
|
||||
endScope();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createSignalFlagsHandler()
|
||||
{
|
||||
if (parseResult.signalList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << '\n';
|
||||
stream() << "void " << cfg().className << "::"
|
||||
<< "itemChanged(quint64 signalFlag) {\n";
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << ".insert(signalFlag);\n";
|
||||
}
|
||||
|
||||
if (!parseResult.signalList.isEmpty()) {
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
bool modifySignalsWritten = false;
|
||||
for (const Signal &signal : std::as_const(parseResult.signalList)) {
|
||||
if (signal.modify) {
|
||||
if (!modifySignalsWritten) {
|
||||
stream() << " switch (signalFlag) {\n";
|
||||
modifySignalsWritten = true;
|
||||
}
|
||||
stream() << " case " << signalEnumName(signal.name) << ":\n";
|
||||
stream() << " Q_EMIT " << signal.name << "();\n";
|
||||
stream() << " break;\n";
|
||||
}
|
||||
}
|
||||
if (modifySignalsWritten) {
|
||||
stream() << " }\n";
|
||||
}
|
||||
|
||||
stream() << "}\n";
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGSOURCEGENERATOR_H
|
||||
#define KCONFIGSOURCEGENERATOR_H
|
||||
|
||||
#include "KConfigCodeGeneratorBase.h"
|
||||
#include "KConfigCommonStructs.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
class KConfigParameters;
|
||||
class CfgEntry;
|
||||
class QTextStream;
|
||||
struct ParseResult;
|
||||
|
||||
class KConfigSourceGenerator : public KConfigCodeGeneratorBase
|
||||
{
|
||||
public:
|
||||
KConfigSourceGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters ¶meters, ParseResult &parseResult);
|
||||
|
||||
void start() override;
|
||||
|
||||
private:
|
||||
// Those are fairly self contained functions.
|
||||
void createHeaders();
|
||||
void createPrivateDPointerImplementation();
|
||||
void createSingletonImplementation();
|
||||
void createPreamble();
|
||||
void createDestructor();
|
||||
void createConstructorParameterList();
|
||||
void createParentConstructorCall();
|
||||
void createInitializerList();
|
||||
void createDefaultValueGetterSetter();
|
||||
void createNonModifyingSignalsHelper();
|
||||
void createSignalFlagsHandler();
|
||||
|
||||
// Constructor related methods
|
||||
// the `do` methods have related helper functions that are only related
|
||||
// to it. So we can break the function into many smaller ones and create
|
||||
// logic inside of the `do` function.
|
||||
void doConstructor();
|
||||
void createEnums(const CfgEntry *entry);
|
||||
void createNormalEntry(const CfgEntry *entry, const QString &key);
|
||||
void createIndexedEntry(const CfgEntry *entry, const QString &key);
|
||||
void handleCurrentGroupChange(const CfgEntry *entry);
|
||||
|
||||
void doGetterSetterDPointerMode();
|
||||
void createGetterDPointerMode(const CfgEntry *entry);
|
||||
void createImmutableGetterDPointerMode(const CfgEntry *entry);
|
||||
void createSetterDPointerMode(const CfgEntry *entry);
|
||||
void createItemGetterDPointerMode(const CfgEntry *entry);
|
||||
|
||||
private:
|
||||
QString mCurrentGroup;
|
||||
QStringList mConfigGroupList; // keeps track of generated KConfigGroup;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigXmlParser.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDomAttr>
|
||||
#include <QDomElement>
|
||||
#include <QDomNode>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <iostream>
|
||||
// TODO: Move preprocessDefault to Header / CPP implementation.
|
||||
// it makes no sense for a parser to process those values and generate code.
|
||||
|
||||
static void preProcessDefault(QString &defaultValue,
|
||||
const QString &name,
|
||||
const QString &type,
|
||||
const CfgEntry::Choices &cfgChoices,
|
||||
QString &code,
|
||||
const KConfigParameters &cfg)
|
||||
{
|
||||
if (type == QLatin1String("String") && !defaultValue.isEmpty()) {
|
||||
defaultValue = literalString(defaultValue);
|
||||
|
||||
} else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) {
|
||||
defaultValue = literalString(defaultValue);
|
||||
} else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) {
|
||||
// Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did.
|
||||
defaultValue = QLatin1String("QUrl::fromUserInput( %1)").arg(literalString(defaultValue));
|
||||
} else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) {
|
||||
QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append);
|
||||
if (!code.isEmpty()) {
|
||||
cpp << '\n';
|
||||
}
|
||||
|
||||
if (type == QLatin1String("UrlList")) {
|
||||
cpp << " QList<QUrl> default" << name << ";\n";
|
||||
} else {
|
||||
cpp << " QStringList default" << name << ";\n";
|
||||
}
|
||||
const QStringList defaults = defaultValue.split(QLatin1Char(','));
|
||||
for (const auto &val : defaults) {
|
||||
cpp << " default" << name << ".append( ";
|
||||
if (type == QLatin1String("UrlList")) {
|
||||
cpp << "QUrl::fromUserInput(";
|
||||
}
|
||||
cpp << "QString::fromUtf8( \"" << val << "\" ) ";
|
||||
if (type == QLatin1String("UrlList")) {
|
||||
cpp << ") ";
|
||||
}
|
||||
cpp << ");\n";
|
||||
}
|
||||
defaultValue = QLatin1String("default") + name;
|
||||
|
||||
} else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) {
|
||||
static const QRegularExpression colorRe(QRegularExpression::anchoredPattern(QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?")));
|
||||
|
||||
if (colorRe.match(defaultValue).hasMatch()) {
|
||||
defaultValue = QLatin1String("QColor( %1 )").arg(defaultValue);
|
||||
} else {
|
||||
defaultValue = QLatin1String("QColor( \"%1\" )").arg(defaultValue);
|
||||
}
|
||||
|
||||
} else if (type == QLatin1String("Enum")) {
|
||||
for (const auto &choice : cfgChoices.choices) {
|
||||
if (choice.name == defaultValue) {
|
||||
if (cfg.globalEnums && cfgChoices.name().isEmpty()) {
|
||||
defaultValue.prepend(cfgChoices.prefix);
|
||||
} else {
|
||||
defaultValue.prepend(enumTypeQualifier(name, cfgChoices) + cfgChoices.prefix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type == QLatin1String("IntList")) {
|
||||
QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append);
|
||||
if (!code.isEmpty()) {
|
||||
cpp << '\n';
|
||||
}
|
||||
|
||||
cpp << " QList<int> default" << name << ";\n";
|
||||
if (!defaultValue.isEmpty()) {
|
||||
const QStringList defaults = defaultValue.split(QLatin1Char(','));
|
||||
for (const auto &defaultVal : defaults) {
|
||||
cpp << " default" << name << ".append( " << defaultVal << " );\n";
|
||||
}
|
||||
}
|
||||
defaultValue = QLatin1String("default") + name;
|
||||
}
|
||||
}
|
||||
|
||||
static QString dumpNode(const QDomNode &node)
|
||||
{
|
||||
QString msg;
|
||||
QTextStream s(&msg, QIODevice::WriteOnly);
|
||||
node.save(s, 0);
|
||||
|
||||
msg = msg.simplified();
|
||||
if (msg.length() > 40) {
|
||||
return msg.left(37) + QLatin1String("...");
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readParameterFromEntry(CfgEntry &readEntry, const QDomElement &e)
|
||||
{
|
||||
readEntry.param = e.attribute(QStringLiteral("name"));
|
||||
readEntry.paramType = e.attribute(QStringLiteral("type"));
|
||||
|
||||
if (readEntry.param.isEmpty()) {
|
||||
std::cerr << "Parameter must have a name: " << qPrintable(dumpNode(e)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (readEntry.paramType.isEmpty()) {
|
||||
std::cerr << "Parameter must have a type: " << qPrintable(dumpNode(e)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((readEntry.paramType == QLatin1String("Int")) || (readEntry.paramType == QLatin1String("UInt"))) {
|
||||
bool ok;
|
||||
readEntry.paramMax = e.attribute(QStringLiteral("max")).toInt(&ok);
|
||||
if (!ok) {
|
||||
std::cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " << qPrintable(dumpNode(e)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
} else if (readEntry.paramType == QLatin1String("Enum")) {
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() == QLatin1String("values")) {
|
||||
for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) {
|
||||
if (e3.tagName() == QLatin1String("value")) {
|
||||
readEntry.paramValues.append(e3.text());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (readEntry.paramValues.isEmpty()) {
|
||||
std::cerr << "No values specified for parameter '" << qPrintable(readEntry.param) << "'." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
readEntry.paramMax = readEntry.paramValues.count() - 1;
|
||||
} else {
|
||||
std::cerr << "Parameter '" << qPrintable(readEntry.param) << "' has type " << qPrintable(readEntry.paramType)
|
||||
<< " but must be of type int, uint or Enum." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool KConfigXmlParser::hasDefaultCode(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
Q_UNUSED(readEntry)
|
||||
|
||||
for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
|
||||
if (e.attribute(QStringLiteral("param")).isEmpty()) {
|
||||
if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readChoicesFromEntry(CfgEntry &readEntry, const QDomElement &e)
|
||||
{
|
||||
QList<CfgEntry::Choice> chlist;
|
||||
const auto choiceNameRegex = QRegularExpression(QStringLiteral("\\w+"));
|
||||
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() != QLatin1String("choice")) {
|
||||
continue;
|
||||
}
|
||||
CfgEntry::Choice choice;
|
||||
choice.name = e2.attribute(QStringLiteral("name"));
|
||||
if (choice.name.isEmpty()) {
|
||||
std::cerr << "Tag <choice> requires attribute 'name'." << std::endl;
|
||||
} else if (!choiceNameRegex.match(choice.name).hasMatch()) {
|
||||
std::cerr << "Tag <choice> attribute 'name' must be compatible with Enum naming. name was '" << qPrintable(choice.name)
|
||||
<< "'. You can use attribute 'value' to pass any string as the choice value." << std::endl;
|
||||
}
|
||||
choice.val = e2.attribute(QStringLiteral("value"));
|
||||
for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) {
|
||||
if (e3.tagName() == QLatin1String("label")) {
|
||||
choice.label = e3.text();
|
||||
choice.context = e3.attribute(QStringLiteral("context"));
|
||||
}
|
||||
if (e3.tagName() == QLatin1String("tooltip")) {
|
||||
choice.toolTip = e3.text();
|
||||
choice.context = e3.attribute(QStringLiteral("context"));
|
||||
}
|
||||
if (e3.tagName() == QLatin1String("whatsthis")) {
|
||||
choice.whatsThis = e3.text();
|
||||
choice.context = e3.attribute(QStringLiteral("context"));
|
||||
}
|
||||
}
|
||||
chlist.append(choice);
|
||||
}
|
||||
|
||||
QString name = e.attribute(QStringLiteral("name"));
|
||||
QString prefix = e.attribute(QStringLiteral("prefix"));
|
||||
|
||||
readEntry.choices = CfgEntry::Choices(chlist, name, prefix);
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
|
||||
QString tag = e.tagName();
|
||||
if (tag == QLatin1String("label")) {
|
||||
readEntry.label = e.text();
|
||||
readEntry.labelContext = e.attribute(QStringLiteral("context"));
|
||||
} else if (tag == QLatin1String("tooltip")) {
|
||||
readEntry.toolTip = e.text();
|
||||
readEntry.toolTipContext = e.attribute(QStringLiteral("context"));
|
||||
} else if (tag == QLatin1String("whatsthis")) {
|
||||
readEntry.whatsThis = e.text();
|
||||
readEntry.whatsThisContext = e.attribute(QStringLiteral("context"));
|
||||
} else if (tag == QLatin1String("min")) {
|
||||
readEntry.min = e.text();
|
||||
} else if (tag == QLatin1String("max")) {
|
||||
readEntry.max = e.text();
|
||||
} else if (tag == QLatin1String("code")) {
|
||||
readEntry.code = e.text();
|
||||
} else if (tag == QLatin1String("parameter")) {
|
||||
readParameterFromEntry(readEntry, e);
|
||||
} else if (tag == QLatin1String("default")) {
|
||||
if (e.attribute(QStringLiteral("param")).isEmpty()) {
|
||||
readEntry.defaultValue = e.text();
|
||||
}
|
||||
} else if (tag == QLatin1String("choices")) {
|
||||
readChoicesFromEntry(readEntry, e);
|
||||
} else if (tag == QLatin1String("emit")) {
|
||||
Signal signal;
|
||||
signal.name = e.attribute(QStringLiteral("signal"));
|
||||
readEntry.signalList.append(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::createChangedSignal(CfgEntry &readEntry)
|
||||
{
|
||||
if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(readEntry.name))) {
|
||||
Signal s;
|
||||
s.name = changeSignalName(readEntry.name);
|
||||
s.modify = true;
|
||||
readEntry.signalList.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::validateNameAndKey(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
bool nameIsEmpty = readEntry.name.isEmpty();
|
||||
if (nameIsEmpty && readEntry.key.isEmpty()) {
|
||||
std::cerr << "Entry must have a name or a key: " << qPrintable(dumpNode(element)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (readEntry.key.isEmpty()) {
|
||||
readEntry.key = readEntry.name;
|
||||
}
|
||||
|
||||
if (nameIsEmpty) {
|
||||
readEntry.name = readEntry.key;
|
||||
readEntry.name.remove(QLatin1Char(' '));
|
||||
} else if (readEntry.name.contains(QLatin1Char(' '))) {
|
||||
std::cout << "Entry '" << qPrintable(readEntry.name) << "' contains spaces! <name> elements can not contain spaces!" << std::endl;
|
||||
readEntry.name.remove(QLatin1Char(' '));
|
||||
}
|
||||
|
||||
if (readEntry.name.contains(QStringLiteral("$("))) {
|
||||
if (readEntry.param.isEmpty()) {
|
||||
std::cerr << "Name may not be parameterized: " << qPrintable(readEntry.name) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (!readEntry.param.isEmpty()) {
|
||||
std::cerr << "Name must contain '$(" << qPrintable(readEntry.param) << ")': " << qPrintable(readEntry.name) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readParamDefaultValues(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
if (readEntry.param.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Adjust name
|
||||
readEntry.paramName = readEntry.name;
|
||||
|
||||
readEntry.name.remove(QStringLiteral("$(") + readEntry.param + QLatin1Char(')'));
|
||||
// Lookup defaults for indexed entries
|
||||
for (int i = 0; i <= readEntry.paramMax; i++) {
|
||||
readEntry.paramDefaultValues.append(QString());
|
||||
}
|
||||
|
||||
for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
|
||||
QString tag = e.tagName();
|
||||
if (tag != QLatin1String("default")) {
|
||||
continue;
|
||||
}
|
||||
QString index = e.attribute(QStringLiteral("param"));
|
||||
if (index.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int i = index.toInt(&ok);
|
||||
if (!ok) {
|
||||
i = readEntry.paramValues.indexOf(index);
|
||||
if (i == -1) {
|
||||
std::cerr << "Index '" << qPrintable(index) << "' for default value is unknown." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((i < 0) || (i > readEntry.paramMax)) {
|
||||
std::cerr << "Index '" << i << "' for default value is out of range [0, " << readEntry.paramMax << "]." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QString tmpDefaultValue = e.text();
|
||||
|
||||
if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) {
|
||||
preProcessDefault(tmpDefaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg);
|
||||
}
|
||||
|
||||
readEntry.paramDefaultValues[i] = tmpDefaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
CfgEntry *KConfigXmlParser::parseEntry(const QString &group, const QString &parentGroup, const QDomElement &element)
|
||||
{
|
||||
CfgEntry readEntry;
|
||||
readEntry.type = element.attribute(QStringLiteral("type"));
|
||||
readEntry.name = element.attribute(QStringLiteral("name"));
|
||||
readEntry.key = element.attribute(QStringLiteral("key"));
|
||||
readEntry.hidden = element.attribute(QStringLiteral("hidden")) == QLatin1String("true");
|
||||
;
|
||||
readEntry.group = group;
|
||||
readEntry.parentGroup = parentGroup;
|
||||
|
||||
const bool nameIsEmpty = readEntry.name.isEmpty();
|
||||
|
||||
readGroupElements(readEntry, element);
|
||||
|
||||
validateNameAndKey(readEntry, element);
|
||||
|
||||
if (readEntry.label.isEmpty()) {
|
||||
readEntry.label = readEntry.key;
|
||||
}
|
||||
|
||||
if (readEntry.type.isEmpty()) {
|
||||
readEntry.type = QStringLiteral("String"); // XXX : implicit type might be bad
|
||||
}
|
||||
|
||||
readParamDefaultValues(readEntry, element);
|
||||
|
||||
if (!mValidNameRegexp.match(readEntry.name).hasMatch()) {
|
||||
if (nameIsEmpty) {
|
||||
std::cerr << "The key '" << qPrintable(readEntry.key)
|
||||
<< "' can not be used as name for the entry because "
|
||||
"it is not a valid name. You need to specify a valid name for this entry."
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not a valid name for an entry." << std::endl;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (mAllNames.contains(readEntry.name)) {
|
||||
if (nameIsEmpty) {
|
||||
std::cerr << "The key '" << qPrintable(readEntry.key)
|
||||
<< "' can not be used as name for the entry because "
|
||||
"it does not result in a unique name. You need to specify a unique name for this entry."
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not unique." << std::endl;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mAllNames.append(readEntry.name);
|
||||
|
||||
if (!hasDefaultCode(readEntry, element)) {
|
||||
// TODO: Move all the options to CfgEntry.
|
||||
preProcessDefault(readEntry.defaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg);
|
||||
}
|
||||
|
||||
// TODO: Try to Just return the CfgEntry we populated instead of
|
||||
// creating another one to fill the code.
|
||||
CfgEntry *result = new CfgEntry();
|
||||
result->group = readEntry.group;
|
||||
result->parentGroup = readEntry.parentGroup;
|
||||
result->type = readEntry.type;
|
||||
result->key = readEntry.key;
|
||||
result->name = readEntry.name;
|
||||
result->labelContext = readEntry.labelContext;
|
||||
result->label = readEntry.label;
|
||||
result->toolTipContext = readEntry.toolTipContext;
|
||||
result->toolTip = readEntry.toolTip;
|
||||
result->whatsThisContext = readEntry.whatsThisContext;
|
||||
result->whatsThis = readEntry.whatsThis;
|
||||
result->code = readEntry.code;
|
||||
result->defaultValue = readEntry.defaultValue;
|
||||
result->choices = readEntry.choices;
|
||||
result->signalList = readEntry.signalList;
|
||||
result->hidden = readEntry.hidden;
|
||||
|
||||
if (!readEntry.param.isEmpty()) {
|
||||
result->param = readEntry.param;
|
||||
result->paramName = readEntry.paramName;
|
||||
result->paramType = readEntry.paramType;
|
||||
result->paramValues = readEntry.paramValues;
|
||||
result->paramDefaultValues = readEntry.paramDefaultValues;
|
||||
result->paramMax = readEntry.paramMax;
|
||||
}
|
||||
result->min = readEntry.min;
|
||||
result->max = readEntry.max;
|
||||
createChangedSignal(*result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Change the name of the config variable.
|
||||
KConfigXmlParser::KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName)
|
||||
: cfg(cfg)
|
||||
, mInputFileName(inputFileName)
|
||||
{
|
||||
mValidNameRegexp.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*")));
|
||||
}
|
||||
|
||||
void KConfigXmlParser::start()
|
||||
{
|
||||
QFile input(mInputFileName);
|
||||
if (!input.open(QIODevice::ReadOnly)) {
|
||||
qFatal("Could not open input file: %s", qUtf8Printable(mInputFileName));
|
||||
}
|
||||
QDomDocument doc;
|
||||
const QDomDocument::ParseResult parseResult = doc.setContent(&input);
|
||||
if (!parseResult) {
|
||||
std::cerr << "Unable to load document." << std::endl;
|
||||
std::cerr << "Parse error in " << qPrintable(mInputFileName) << ", line " << parseResult.errorLine << ", col " << parseResult.errorColumn << ": "
|
||||
<< qPrintable(parseResult.errorMessage) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QDomElement cfgElement = doc.documentElement();
|
||||
if (cfgElement.isNull()) {
|
||||
std::cerr << "No document in kcfg file" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (QDomElement element = cfgElement.firstChildElement(); !element.isNull(); element = element.nextSiblingElement()) {
|
||||
QString tag = element.tagName();
|
||||
|
||||
if (tag == QLatin1String("include")) {
|
||||
readIncludeTag(element);
|
||||
} else if (tag == QLatin1String("kcfgfile")) {
|
||||
readKcfgfileTag(element);
|
||||
} else if (tag == QLatin1String("group")) {
|
||||
readGroupTag(element);
|
||||
} else if (tag == QLatin1String("signal")) {
|
||||
readSignalTag(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParseResult KConfigXmlParser::getParseResult() const
|
||||
{
|
||||
return mParseResult;
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readIncludeTag(const QDomElement &e)
|
||||
{
|
||||
QString includeFile = e.text();
|
||||
if (!includeFile.isEmpty()) {
|
||||
mParseResult.includes.append(includeFile);
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readGroupTag(const QDomElement &e)
|
||||
{
|
||||
QString group = e.attribute(QStringLiteral("name"));
|
||||
if (group.isEmpty()) {
|
||||
std::cerr << "Group without name" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const QString parentGroup = e.attribute(QStringLiteral("parentGroupName"));
|
||||
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() != QLatin1String("entry")) {
|
||||
continue;
|
||||
}
|
||||
CfgEntry *entry = parseEntry(group, parentGroup, e2);
|
||||
if (entry) {
|
||||
mParseResult.entries.append(entry);
|
||||
} else {
|
||||
std::cerr << "Can not parse entry." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readKcfgfileTag(const QDomElement &e)
|
||||
{
|
||||
mParseResult.cfgFileName = e.attribute(QStringLiteral("name"));
|
||||
mParseResult.cfgStateConfig = e.attribute(QStringLiteral("stateConfig")).toLower() == QLatin1String("true");
|
||||
mParseResult.cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true");
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() == QLatin1String("parameter")) {
|
||||
Param p;
|
||||
p.name = e2.attribute(QStringLiteral("name"));
|
||||
p.type = e2.attribute(QStringLiteral("type"));
|
||||
if (p.type.isEmpty()) {
|
||||
p.type = QStringLiteral("String");
|
||||
}
|
||||
mParseResult.parameters.append(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readSignalTag(const QDomElement &e)
|
||||
{
|
||||
QString signalName = e.attribute(QStringLiteral("name"));
|
||||
if (signalName.isEmpty()) {
|
||||
std::cerr << "Signal without name." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
Signal theSignal;
|
||||
theSignal.name = signalName;
|
||||
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() == QLatin1String("argument")) {
|
||||
Param argument;
|
||||
argument.type = e2.attribute(QStringLiteral("type"));
|
||||
if (argument.type.isEmpty()) {
|
||||
std::cerr << "Signal argument without type." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
argument.name = e2.text();
|
||||
theSignal.arguments.append(argument);
|
||||
} else if (e2.tagName() == QLatin1String("label")) {
|
||||
theSignal.label = e2.text();
|
||||
}
|
||||
}
|
||||
|
||||
mParseResult.signalList.append(theSignal);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGXMLPARSER_H
|
||||
#define KCONFIGXMLPARSER_H
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
|
||||
#include "KConfigCommonStructs.h"
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
/* This parses the contents of a Xml file into a ParseResult Structure,
|
||||
* It also fails hard:
|
||||
* If start() succeeds, you can use the result,
|
||||
* if start() fails, the program aborts with an error message so there's
|
||||
* no possibility of generating incorrect code information.
|
||||
*/
|
||||
class KConfigXmlParser
|
||||
{
|
||||
public:
|
||||
KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName);
|
||||
|
||||
// Start the parser and reads the contents of the inputFileName into the ParseResult Structure
|
||||
void start();
|
||||
|
||||
// Get the result of the parse
|
||||
ParseResult getParseResult() const;
|
||||
|
||||
private:
|
||||
// creates a `somethingChanged` signal for every property
|
||||
void createChangedSignal(CfgEntry &readEntry);
|
||||
|
||||
void validateNameAndKey(CfgEntry &readEntry, const QDomElement &element);
|
||||
|
||||
// TODO: Use std::optional and CfgEntry (without heap allocation) for this function
|
||||
// *or* fail hard if the parse fails.
|
||||
CfgEntry *parseEntry(const QString &group, const QString &parentGroup, const QDomElement &element);
|
||||
|
||||
// Steps
|
||||
void readIncludeTag(const QDomElement &element);
|
||||
void readGroupTag(const QDomElement &element);
|
||||
void readKcfgfileTag(const QDomElement &element);
|
||||
void readSignalTag(const QDomElement &element);
|
||||
|
||||
// Those are the Entries in the Xml, that represent a parameter within the <group> </group> tag.
|
||||
void readParameterFromEntry(CfgEntry &entry, const QDomElement &element);
|
||||
bool hasDefaultCode(CfgEntry &entry, const QDomElement &element);
|
||||
void readChoicesFromEntry(CfgEntry &entry, const QDomElement &element);
|
||||
void readGroupElements(CfgEntry &entry, const QDomElement &element);
|
||||
void readParamDefaultValues(CfgEntry &entry, const QDomElement &element);
|
||||
|
||||
private:
|
||||
ParseResult mParseResult;
|
||||
KConfigParameters cfg;
|
||||
QString mInputFileName;
|
||||
QStringList mAllNames;
|
||||
QRegularExpression mValidNameRegexp;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,560 @@
|
||||
/**
|
||||
\page kconfig_compiler The KDE Configuration Compiler
|
||||
|
||||
kconfig_compiler generates C++ source code from an XML file containing
|
||||
information about configuration options (.kcfg) and a file that provides
|
||||
the code generation options (.kcfgc) The generated class is based on
|
||||
KConfigSkeleton and provides an API for the application to access its
|
||||
configuration data.
|
||||
|
||||
The generated C++ source code is output to a .h and a .cpp file, whose base
|
||||
name is the same as that of the .kcfgc file.
|
||||
|
||||
\section kcfg_format The .kcfg XML file format: description of the configuration skeleton
|
||||
|
||||
The structure of the .kcfg file is described by its DTD kcfg.xsd.
|
||||
|
||||
The \<kcfgfile\> tag may contain either the "name" attribute, which should be the name
|
||||
of the configuration file described, or the "arg" attribute, which, if set to
|
||||
"true", will allow you to pass the KSharedConfig::Ptr object to use.
|
||||
|
||||
If neither "name" nor "arg" is set, the default configuration file
|
||||
("\<appname\>rc") will be used.
|
||||
|
||||
The \<include\> tags are optional and may contain C++ header files that
|
||||
are needed to compile the code needed to compute default values. To generate
|
||||
a \#include "..." statement instead of \#include \<...\>, enclose the header
|
||||
file name in double quotes (e.g. \<include\>"header.h"\</include\>).
|
||||
|
||||
The remaining entries in the XML file are grouped by the tag \<group\>
|
||||
which describes the corresponding group in the configuration file.
|
||||
|
||||
The individual entries must have at least a name or a key. The key is used
|
||||
as the key in the config file, while the name is used to create accessor and
|
||||
modifier functions. If \<key\> is given, but not \<name\>, the name is
|
||||
constructed by removing all spaces from \<key\>. If \<name\> is given, but
|
||||
not \<key\>, the key is the same as \<name\>.
|
||||
|
||||
An entry must also have a type. The list of allowable types is
|
||||
specified in the DTD and loosely follows the list of types supported
|
||||
by the QVariant with exception of the clearly binary types
|
||||
(e.g. Pixmap, Image...) which are not supported. Besides those basic
|
||||
types the following special types are supported:
|
||||
|
||||
<dl>
|
||||
<dt>Path</dt>
|
||||
<dd>
|
||||
This is a string that is specially treated as a file-path.
|
||||
In particular paths in the home directory are prefixed with $HOME in
|
||||
when being stored in the configuration file.
|
||||
</dd>
|
||||
|
||||
<dt>Enum</dt>
|
||||
<dd>
|
||||
This indicates an enumeration. The possible enum values and optional
|
||||
enum name should be provided via the \<choices\> tag. Enum values are
|
||||
accessed as integers by the application but stored as strings in the
|
||||
configuration file. This makes it possible to add more values at a later
|
||||
date without breaking compatibility.
|
||||
</dd>
|
||||
|
||||
<dt>IntList</dt>
|
||||
<dd>
|
||||
This indicates a list of integers. This information is provided
|
||||
to the application as QList<int>. Useful for storing QSplitter
|
||||
geometries.
|
||||
</dd>
|
||||
|
||||
<dt>Color</dt>
|
||||
<dd>
|
||||
Isn't a special type but has special input. It is generated as QColor.
|
||||
Any valid input to QColor(QString) can be used (hex or SVG keyword notation)
|
||||
as well as a special format r,g,b,a where the a denotes the alpha channel and
|
||||
may be omitted.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
An entry can optionally have a default value which is used as default when
|
||||
the value isn't specified in any config file. Default values are interpreted
|
||||
as literal constant values. If a default value needs to be computed
|
||||
or if it needs to be obtained from a function call, the \<default\> tag
|
||||
should contain the code="true" attribute. The contents of the \<default\>
|
||||
tag is then considered to be a C++ expression. Note that in this case you
|
||||
might have to add an \<include\> tag as described above, or a
|
||||
SourceIncludeFiles entry in the .kcfgc file as described below, so that the
|
||||
code which computes the default value can be compiled.
|
||||
|
||||
Additional code for computing default values can be provided outside any
|
||||
entry definition via the \<code\> tag. The contents of the \<code\> tag is
|
||||
inserted as-is. A typical use for this is to compute a common default value
|
||||
which can then be referenced by multiple entries that follow.
|
||||
|
||||
\section kcfgc_format The .kcfgc file format: code generation options
|
||||
|
||||
The options for generating the C++ sources are read from the file with the
|
||||
extension .kcfgc. To generate a class add the corresponding kcfgc file to the
|
||||
SOURCES line in the Makefile.am.
|
||||
|
||||
The following options are read from the kcfgc file:
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>File=\<string\></dt>
|
||||
<dd>
|
||||
Default: \<programname\>.kcfg \n
|
||||
Name of kcfg file containing the options the class is generated for
|
||||
<!-- what? -->
|
||||
</dd>
|
||||
|
||||
<dt>HeaderExtension=\<string\></dt>
|
||||
<dd>
|
||||
Default: h \n
|
||||
Since KF 5.57 \n
|
||||
Extension to use for the name of the generated C++ header files.
|
||||
</dd>
|
||||
|
||||
<dt>NameSpace=\<string\></dt>
|
||||
<dd>
|
||||
Optional namespace for generated class
|
||||
</dd>
|
||||
|
||||
<dt>ClassName=\<string\></dt>
|
||||
<dd>
|
||||
Name of generated class (required)
|
||||
</dd>
|
||||
|
||||
<dt>Inherits=\<string\></dt>
|
||||
<dd>
|
||||
Default: KConfigSkeleton \n
|
||||
Class the generated class inherits from.
|
||||
|
||||
This class must inherit KConfigSkeleton and must provide a default
|
||||
constructor (kcfgfile not specified), a constructor taking a
|
||||
QString argument (kcfgfile with "name" attribute) and a constructor
|
||||
taking a KSharedConfig::Ptr as argument (kcfgfile with "arg" attribute).
|
||||
Please refer to the documentation of KConfigSkeleton.
|
||||
</dd>
|
||||
|
||||
<dt>Visibility=\<string\></dt>
|
||||
<dd>
|
||||
Inserts visibility directive (for example KDE_EXPORT) between
|
||||
"class" keyword and class name in header file
|
||||
</dd>
|
||||
|
||||
<dt>Singleton=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
Generated class is a singleton.
|
||||
</dd>
|
||||
|
||||
<dt>CustomAdditions=\<bool\></dt>
|
||||
<dd>
|
||||
<!-- what? -->
|
||||
</dd>
|
||||
|
||||
<dt>MemberVariables=\<string\></dt>
|
||||
<dd>
|
||||
Values: public, protected, private, dpointer \n
|
||||
Default: private \n
|
||||
C++ access modifier used for member variables holding the configuration
|
||||
values
|
||||
</dd>
|
||||
|
||||
<dt>IncludeFiles=\<string\>, \<string\> ...</dt>
|
||||
<dd>
|
||||
Names of files to be included in the header of the generated class.
|
||||
|
||||
Enclose a file name in (escaped) double quotes to generate
|
||||
\#include "..." instead of \#include \<...\>.
|
||||
</dd>
|
||||
|
||||
<dt>SourceIncludeFiles=\<string\>, \<string\> ...</dt>
|
||||
<dd>
|
||||
Names of files to be included in the source file of the generated class.
|
||||
|
||||
Enclose a file name in (escaped) double quotes to generate
|
||||
\#include "..." instead of \#include \<...\>.
|
||||
</dd>
|
||||
|
||||
<dt>Mutators=\<value\></dt>
|
||||
<dd>
|
||||
Values: true, false or a comma separated list of options \n
|
||||
Default: false \n
|
||||
If true, mutator functions for all configuration options are generated.
|
||||
If false, no mutator functions are generated. If a list is provided,
|
||||
mutator functions are generated for the options that are listed.
|
||||
</dd>
|
||||
|
||||
<dt>Notifiers=\<value\></dt>
|
||||
<dd>
|
||||
Values: true, false or a comma separated list of options \n
|
||||
Default: false \n
|
||||
If true, entries will be written with the <b>Notify</b> flag enabled,
|
||||
so changes can be detected using <b>KConfigWatcher</b>.
|
||||
If a list is provided, the options that are listed are written with
|
||||
said flag.
|
||||
</dd>
|
||||
|
||||
<dt>DefaultValueGetters=\<value\></dt>
|
||||
<dd>
|
||||
Values: true, false or a comma separated list of options \n
|
||||
Default: false \n
|
||||
If true, functions to return the default value of all configuration options
|
||||
are generated. If false, no default value functions are generated. If a list
|
||||
is provided, default value functions are generated for the options that are listed.
|
||||
</dd>
|
||||
|
||||
<dt>ItemAccessors=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
Generate accessor functions for the KConfigSkeletonItem objects
|
||||
corresponding to the configuration options. If <b>SetUserTexts</b> is set,
|
||||
<b>ItemAccessors</b> also has to be set.
|
||||
</dd>
|
||||
|
||||
<dt>SetUserTexts=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
Set the label and whatthis texts of the items from the kcfg file.
|
||||
If <b>SetUserTexts</b> is set, <b>ItemAccessors</b> also has to be set.
|
||||
</dd>
|
||||
|
||||
<dt>GlobalEnums=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true all choices of Enum items will be created in the global
|
||||
scope of the generated class. If set to false, each Enum item whose enum is not
|
||||
explicitly named will get its own namespace for its choices.
|
||||
</dd>
|
||||
|
||||
<dt>UseEnumTypes=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, all Enum items whose enums are named will use enum types for
|
||||
the return value of accessor functions and for the parameter of mutator
|
||||
functions. This eliminates the need to cast accessor return values to the enum
|
||||
type if you want to use the enum type in your own code. If set to false,
|
||||
accessor return values and mutator parameters will be of type int.
|
||||
</dd>
|
||||
|
||||
<dt>ForceStringFilename=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, forces the first parameter of the generated class
|
||||
to be a QString when using an argument for the filename.
|
||||
This is useful to specify at runtime the filename of the configuration class.
|
||||
</dd>
|
||||
|
||||
<dt>GenerateProperties=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, a Q_PROPERTY will be generated for each member variable holding a
|
||||
configuration value and the Q_OBJECT macro will be added to the generated class.
|
||||
Note that you will also need to pass the GENERATE_MOC option to the
|
||||
kconfig_add_kcfg_files macro.
|
||||
</dd>
|
||||
|
||||
|
||||
<dt>ParentInConstructor=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, the generated constructor will take an additional QObject*
|
||||
parameter that will be used as the object's parent.
|
||||
This is useful when working with KQuickAddons::ManagedConfigModule
|
||||
to set it as the parent of the generated class to allow automatic
|
||||
settings discovery and handle the deallocation.
|
||||
Note this parameter is incompatible with <b>Singleton</b> set to true.
|
||||
</dd>
|
||||
|
||||
<dt>TranslationSystem=\<string\></dt>
|
||||
<dd>
|
||||
Default: qt \n
|
||||
Set the translation system for label, tooltip and whatsthis text in generated KConfigSkeleton.
|
||||
Set the value to <b>kde</b> to use the KDE Framework translation system, see KI18n.
|
||||
</dd>
|
||||
|
||||
<dt>TranslationDomain=\<value\></dt>
|
||||
<dd>
|
||||
When <b>TranslationSystem=kde</b> is set, allow to specify the domain in which to look for translations.
|
||||
</dd>
|
||||
|
||||
<dt>QmlRegistration=\<bool></dt>
|
||||
<dd>
|
||||
Default: false\n
|
||||
If set to true the generated code will use the QML_ELEMENT macro to register the type to QML.
|
||||
If Singleton is also true it will be registered as a QML singleton using QML_SINGLETON
|
||||
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
\section entry_options Advanced entry options
|
||||
|
||||
There are several possibilities to parameterize entries.
|
||||
|
||||
\subsection parametrized_entries Parameterized entries
|
||||
|
||||
An entry can be parameterized using a fixed range parameter specified with
|
||||
the \<parameter\> tag. Such parameter can either be an Enum or an int. An Enum
|
||||
parameter should specify the possible enumeration values with the \<choices\>
|
||||
tag. An int parameter should specify its maximum value. Its minimum value
|
||||
is always 0.
|
||||
|
||||
A parameterized entry is expanded to a number of entries, one for each
|
||||
value in the parameter range. The name and key should contain a reference
|
||||
to the parameter in the form of $(parameter-name). When expanding the entries
|
||||
the $(parameter-name) part is replaced with the value of the parameter.
|
||||
In the case of an Enum parameter it is replaced with the name of the
|
||||
enumuration value. In the case of an int parameter it is replaced with
|
||||
the numeric value of the parameter.
|
||||
|
||||
Parameterized entries all share the same default value unless different
|
||||
default values have been specified for specific parameter values.
|
||||
This can be done with the param= attribute of the \<default\>. When a
|
||||
param attribute is specified the default value only applies to that
|
||||
particular parameter value.
|
||||
|
||||
Example 1:
|
||||
\code{.xml}
|
||||
<entry name="Color$(ColorIndex)" type="Color" key="color_$(ColorIndex)">
|
||||
<parameter name="ColorIndex" type="Int" max="3"/>
|
||||
<default param="0">#ff0000</default>
|
||||
<default param="1">#00ff00</default>
|
||||
<default param="2">#0000ff</default>
|
||||
<default param="3">#ffff00</default>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
The above describes 4 color configuration entries with the following defaults:
|
||||
|
||||
\verbatim
|
||||
color_0=#ff0000
|
||||
color_1=#00ff00
|
||||
color_2=#0000ff
|
||||
color_3=#ffff00
|
||||
\endverbatim
|
||||
|
||||
The configuration options will be accessible to the application via
|
||||
a QColor color(int ColorIndex) and a
|
||||
void setColor(int ColorIndex, const QColor &v) function.
|
||||
|
||||
Example 2:
|
||||
\code{.xml}
|
||||
<entry name="Sound$(SoundEvent)" type="String" key="sound_$(SoundEvent)">
|
||||
<parameter name="SoundEvent" type="Enum">
|
||||
<values>
|
||||
<value>Explosion</value>
|
||||
<value>Crash</value>
|
||||
<value>Missile</value>
|
||||
</values>
|
||||
</parameter>
|
||||
<default param="Explosion">boom.wav</default>
|
||||
<default param="Crash">crash.wav</default>
|
||||
<default param="Missile">missile.wav</default>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
The above describes 3 string configuration entries with the following defaults:
|
||||
|
||||
\verbatim
|
||||
sound_Explosion=boom.wav
|
||||
sound_Crash=crash.wav
|
||||
sound_Missile=missile.wav
|
||||
\endverbatim
|
||||
|
||||
The configuration options will be accessible to the application via
|
||||
a QString sound(int SoundEvent) and a
|
||||
void setSound(int SoundEvent, const QString &v) function.
|
||||
|
||||
\subsection parametrized_groups Parameterized groups
|
||||
|
||||
A group name can be parametrized using a parameter given to the KConfigSkeleton
|
||||
instance (which means this feature cannot be used with singleton classes).
|
||||
|
||||
Example 1:
|
||||
\code{.xml}
|
||||
<kcfgfile name="testrc">
|
||||
<parameter name="groupname"/>
|
||||
</kcfgfile>
|
||||
<group name="$(groupname)">
|
||||
<entry key="Text" type="string">
|
||||
</entry>
|
||||
</group>
|
||||
\endcode
|
||||
|
||||
In this case passing "Group2" as the 'groupname' parameter to the generated class
|
||||
will make it use group "Group2" for the entry "Text".
|
||||
|
||||
By setting the stateConfig attribute of kcfgfile to "true", KSharedConfig::openStateConfig is used.
|
||||
This should be used when one stores volatile data, like window sizes or autocompletion texts.
|
||||
It is recommended to have at least two separate kcfg files for the different kinds of data.
|
||||
NOTE: This option is ignored when ForceStringFilename is set.
|
||||
|
||||
\subsection enums Enums
|
||||
|
||||
By default, if <b>GlobalEnums</b> is set to false, a separate named enum will be generated
|
||||
for each Enum entry. Since each enum is defined in a little enclosing class of its own,
|
||||
this allows the same Enum value names to be used in different enums. For example, the
|
||||
.kcfg entry
|
||||
|
||||
\code{.xml}
|
||||
<entry name="KeepData" type="Enum">
|
||||
<choices>
|
||||
<choice name="Do"/>
|
||||
<choice name="Dont" value="Don't"/>
|
||||
</choices>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
will generate this public class containing the enum definition, inside the generated class:
|
||||
|
||||
\code{.cpp}
|
||||
class EnumKeepData
|
||||
{
|
||||
public:
|
||||
enum type { Do, Dont, COUNT };
|
||||
};
|
||||
\endcode
|
||||
|
||||
Since 5.68, if present the <b>value</b> attribute will be used as the choice value written to the backend
|
||||
instead of the <b>name</b>, allowing to write text incompatible with enum naming.
|
||||
|
||||
Alternatively, if <b>GlobalEnums</b> is set to true, all Enum items are defined as
|
||||
unnamed enums in the global scope of the generated class. In this case, all Enum values
|
||||
must have different names to avoid clashes. However, you can use a 'prefix' argument
|
||||
in \<choices\> to prevent duplicate enum member names clashing. Using this, the Enum value
|
||||
names are prefixed in code with the string you specify. For example, if <b>GlobalEnums</b>
|
||||
is set to true, the .kcfg entry
|
||||
|
||||
\code{.xml}
|
||||
<entry name="KeepData" type="Enum">
|
||||
<choices prefix="Keep_">
|
||||
<choice name="Do"/>
|
||||
<choice name="Dont" value="Don't"/>
|
||||
</choices>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
will generate config file entries of "KeepData=Do" and "KeepData=Dont", but the enum
|
||||
will be declared
|
||||
|
||||
\code{.cpp}
|
||||
enum { Keep_Do, Keep_Dont };
|
||||
\endcode
|
||||
|
||||
It is possible to specify your own name for a generated enum, by including a
|
||||
'name' parameter in \<choices\>. Just like unnamed enums, this enum will be defined in
|
||||
the global scope of the generated class (without any enclosing class of its own).
|
||||
Therefore the names of Enum values must be unique across both unnamed enums (if
|
||||
<b>GlobalEnums</b> is set to true) and all specifically named enums.
|
||||
|
||||
An example of a specifically named enum:
|
||||
|
||||
\code{.xml}
|
||||
<entry name="KeepData" type="Enum">
|
||||
<choices name="Types">
|
||||
<choice name="Do"/>
|
||||
<choice name="Dont"/>
|
||||
</choices>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
which results in the following enum declaration, inside the generated class:
|
||||
|
||||
\code{.cpp}
|
||||
enum Types { Do, Dont };
|
||||
\endcode
|
||||
|
||||
It is also possible to specify the use of enums external to the generated class, by
|
||||
including the string "::" in the enum name - just ensure that it is sufficiently
|
||||
qualified to be unambiguous in use. To specify use of an unnamed enum, append a
|
||||
trailing "::". For example, to use the enum 'myEnum' defined in class ClassA, use
|
||||
either of
|
||||
|
||||
\code{.xml}
|
||||
<choices name="ClassA::myEnum">
|
||||
<choices name="::ClassA::myEnum">
|
||||
\endcode
|
||||
|
||||
To specify an unnamed enum in namespace ProgSpace, use
|
||||
|
||||
\code{.xml}
|
||||
<choices name="ProgSpace::">
|
||||
\endcode
|
||||
|
||||
To specify a top-level unnamed enum, use
|
||||
|
||||
\code{.xml}
|
||||
<choices name="::">
|
||||
\endcode
|
||||
|
||||
To specify the top-level enum 'anotherEnum', use
|
||||
|
||||
\code{.xml}
|
||||
|
||||
<choices name="::anotherEnum">
|
||||
\endcode
|
||||
|
||||
|
||||
\subsection notify_signals Notify signals
|
||||
|
||||
An entry can emit a signal when it gets changed. First of all, you must
|
||||
define a list of signals for the configuration class. The signal's name may be
|
||||
any legal identifier you wish. The \<argument\> tag allows you to specify arguments
|
||||
for the emitted signal. It supports all types as defined in
|
||||
the KConfigXT DTD. The argument value must specify the name, without spaces, of one
|
||||
of the entries defined in the .kcfg file.
|
||||
A signal definition can also contain a \<label\> tag which will be
|
||||
the documentation line in the generated file.
|
||||
|
||||
\code{.xml}
|
||||
<signal name="emoticonSettingsChanged" />
|
||||
|
||||
<signal name="styleChanged">
|
||||
<label>Tell when a complete style change.</label>
|
||||
<argument type="String">stylePath</argument>
|
||||
<argument type="String">StyleCSSVariant</argument>
|
||||
</signal>
|
||||
\endcode
|
||||
|
||||
After defining the signals, you must tell which signal to emit for the entry.
|
||||
A signal can be emitted by multiple entries. Also, you don't need to specify the arguments
|
||||
for a signal, the signal name will suffice.
|
||||
|
||||
\code{.xml}
|
||||
<entry key="stylePath" type="String">
|
||||
<label>Absolute path to a directory containing a Adium/Kopete chat window style.</label>
|
||||
<emit signal="styleChanged" />
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
The signal will be emitted when save() is called.
|
||||
|
||||
@warning
|
||||
You must not call save() in direct response to any notify signal.
|
||||
If you need to call save(),
|
||||
use a QueuedConnection to listen to the notify signal.
|
||||
|
||||
You can also listen to the generic configChanged() signal from KConfigSkeleton to
|
||||
notify your application about configuration changes.
|
||||
In that case you may also call save() in direct response.
|
||||
|
||||
Note that you will also need to pass the GENERATE_MOC option to the kconfig_add_kcfg_files macro.
|
||||
|
||||
\subsection translation_context Translation context
|
||||
|
||||
In the kcfg file you can specify the translation's context for \<tooltip\>, \<whatsthis\> and \<label\> element for an entry.
|
||||
|
||||
\code{.xml}
|
||||
<entry type="Bool" key="Auto Save">
|
||||
<label>Enable automatic saving of calendar</label>
|
||||
<whatsthis context="@info:whatsthis">Enable automatic saving of calendars to have calendars saved automatically.</whatsthis>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
For more information on translation context and how to write good translatable text,
|
||||
please refer to https://api.kde.org/frameworks/ki18n/html/prg_guide.html
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
if ( @ARGV < 1 ) {
|
||||
print STDERR "Missing arg: filename\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$file = $ARGV[0];
|
||||
|
||||
$file =~ /^(.*)\.[^\.]*$/;
|
||||
$filebase = $1;
|
||||
|
||||
$header_extension = @ARGV > 1 ? $ARGV[1] : "h";
|
||||
$source_extension = @ARGV > 2 ? $ARGV[2] : "cpp";
|
||||
$file_h = "$filebase.$header_extension";
|
||||
$file_cpp = "$filebase.$source_extension";
|
||||
|
||||
$kcfgc = $file . "c";
|
||||
|
||||
$cmd = "./kconfig_compiler_kf6 $file $kcfgc";
|
||||
|
||||
#print "CMD $cmd\n";
|
||||
|
||||
if ( system( $cmd ) != 0 ) {
|
||||
print STDERR "Unable to run kconfig_compiler_kf6\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
checkfile( $file_h );
|
||||
checkfile( $file_cpp );
|
||||
|
||||
exit 0;
|
||||
|
||||
sub checkfile()
|
||||
{
|
||||
my $file = shift;
|
||||
|
||||
$file =~ /\/([^\/]*)$/;
|
||||
my $filename = $1;
|
||||
|
||||
print "Checking '$filename':\n";
|
||||
|
||||
my @ref;
|
||||
if ( !open( REF, "$file.ref" ) ) {
|
||||
print STDERR "Unable to open $file.ref\n";
|
||||
exit 1;
|
||||
}
|
||||
while( <REF> ) {
|
||||
push @ref, $_;
|
||||
}
|
||||
close REF;
|
||||
|
||||
if ( !open( READ, $filename ) ) {
|
||||
print STDERR "Unable to open $filename\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$error = 0;
|
||||
$i = 0;
|
||||
$line = 1;
|
||||
while( <READ> ) {
|
||||
$out = $_;
|
||||
$ref = @ref[$i++];
|
||||
|
||||
if ( $out ne $ref ) {
|
||||
$error++;
|
||||
print " Line $line: Expected : $ref";
|
||||
print " Line $line: Compiler output : $out";
|
||||
}
|
||||
|
||||
$line++;
|
||||
}
|
||||
|
||||
close READ;
|
||||
|
||||
if ( $error > 0 ) {
|
||||
print "\n FAILED: $error errors found.\n";
|
||||
if ( $error > 5 ) {
|
||||
system( "diff -u $file.ref $filename" );
|
||||
}
|
||||
exit 1;
|
||||
} else {
|
||||
print " OK\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- kcfg XSD v1.0 -->
|
||||
<xsd:schema
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:kcfg="http://www.kde.org/standards/kcfg/1.0"
|
||||
targetNamespace="http://www.kde.org/standards/kcfg/1.0"
|
||||
version="1.0"
|
||||
elementFormDefault="qualified" >
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
|
||||
Permission to use, copy, modify and distribute this DTD
|
||||
and its accompanying documentation for any purpose and without fee
|
||||
is hereby granted in perpetuity, provided that the above copyright
|
||||
notice and this paragraph appear in all copies. The copyright
|
||||
holders make no representation about the suitability of the DTD for
|
||||
any purpose. It is provided "as is" without expressed or implied
|
||||
warranty.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
A Schema for KDE's KConfigXT XML format. It is similar to the DTD
|
||||
found at:
|
||||
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.dtd
|
||||
|
||||
Documents valid against the Schema version are backwards compatible
|
||||
to the DTD. Validating against the Schema instead of the DTD is
|
||||
recommended, since the former provides better validation.
|
||||
|
||||
A document instance of this Schema should have a declaration
|
||||
looking like this:
|
||||
|
||||
<![CDATA[
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<!-- the content -->
|
||||
</kcfg>
|
||||
|
||||
]]>
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="kcfg">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="include" minOccurs="0" maxOccurs="unbounded" type="xsd:string"/>
|
||||
<xsd:element name="kcfgfile" minOccurs="0" maxOccurs="1" >
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="parameter" type="kcfg:parameter" minOccurs="0" maxOccurs="unbounded" />
|
||||
<!-- FIXME: Are really unbounded occurrences of parameter allowed? -->
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="optional"/>
|
||||
<xsd:attribute name="stateConfig" type="xsd:boolean" use="optional"/>
|
||||
<xsd:attribute name="arg" type="xsd:boolean" use="optional"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="signal" type="kcfg:signal" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="group" maxOccurs="unbounded" >
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="entry" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="parameter" minOccurs="0" type="kcfg:parameter"/>
|
||||
<xsd:element name="label" minOccurs="0" type="kcfg:translatableString"/>
|
||||
<xsd:element name="whatsthis" minOccurs="0" type="kcfg:translatableString"/>
|
||||
<xsd:element name="tooltip" minOccurs="0" type="kcfg:translatableString"/>
|
||||
<xsd:element name="choices" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="choice" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:all>
|
||||
<xsd:element minOccurs="0" name="label" type="kcfg:translatableString"/>
|
||||
<xsd:element minOccurs="0" name="whatsthis" type="kcfg:translatableString"/>
|
||||
<xsd:element minOccurs="0" name="tooltip" type="kcfg:translatableString"/>
|
||||
</xsd:all>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="value" use="optional" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="optional" type="xsd:string"/>
|
||||
<xsd:attribute name="prefix" use="optional" type="xsd:string"/>
|
||||
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="code" minOccurs="0" type="kcfg:code"/>
|
||||
|
||||
<xsd:element name="default" maxOccurs="unbounded" minOccurs="0" >
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute use="optional" name="code" type="xsd:boolean"/>
|
||||
<xsd:attribute use="optional" name="param" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="min" minOccurs="0" >
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="code" type="xsd:boolean"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="max" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="code" type="xsd:boolean"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="emit" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="signal" use="required" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" use="optional" type="xsd:string"/>
|
||||
<xsd:attribute name="key" use="optional" type="xsd:string"/>
|
||||
<xsd:attribute name="hidden" use="optional" type="xsd:boolean"/>
|
||||
<xsd:attribute name="type" type="kcfg:datatype"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="parentGroupName" use="optional" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:simpleType name="datatype">
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="String"/>
|
||||
<xsd:enumeration value="StringList"/>
|
||||
<xsd:enumeration value="Font"/>
|
||||
<xsd:enumeration value="Rect"/>
|
||||
<xsd:enumeration value="RectF"/>
|
||||
<xsd:enumeration value="Size"/>
|
||||
<xsd:enumeration value="SizeF"/>
|
||||
<xsd:enumeration value="Color"/>
|
||||
<xsd:enumeration value="Point"/>
|
||||
<xsd:enumeration value="PointF"/>
|
||||
<xsd:enumeration value="Int"/>
|
||||
<xsd:enumeration value="UInt"/>
|
||||
<xsd:enumeration value="Bool"/>
|
||||
<xsd:enumeration value="Double"/>
|
||||
<xsd:enumeration value="DateTime"/>
|
||||
<xsd:enumeration value="LongLong"/>
|
||||
<xsd:enumeration value="ULongLong"/>
|
||||
<xsd:enumeration value="IntList"/>
|
||||
<xsd:enumeration value="Enum"/>
|
||||
<xsd:enumeration value="Path"/>
|
||||
<xsd:enumeration value="PathList"/>
|
||||
<xsd:enumeration value="Password"/>
|
||||
<xsd:enumeration value="Url"/>
|
||||
<xsd:enumeration value="UrlList"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:complexType name="parameter">
|
||||
<xsd:sequence>
|
||||
<xsd:element minOccurs="0" name="values">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" maxOccurs="unbounded" type="xsd:string"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" use="optional" type="kcfg:datatype" />
|
||||
<xsd:attribute name="max" use="optional" type="xsd:positiveInteger"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="code">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string"/>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="signal">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="label" minOccurs="0" type="xsd:string"/>
|
||||
<xsd:element name="argument" maxOccurs="unbounded" minOccurs="0" >
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute use="required" name="type" type="kcfg:datatype"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="translatableString">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute use="optional" name="context" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <QCommandLineOption>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
#include <QDomAttr>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../core/kconfig_version.h"
|
||||
#include "KConfigCommonStructs.h"
|
||||
#include "KConfigHeaderGenerator.h"
|
||||
#include "KConfigParameters.h"
|
||||
#include "KConfigSourceGenerator.h"
|
||||
#include "KConfigXmlParser.h"
|
||||
|
||||
QString varName(const QString &n, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
if (!cfg.dpointer) {
|
||||
result = QChar::fromLatin1('m') + n;
|
||||
result[1] = result.at(1).toUpper();
|
||||
} else {
|
||||
result = n;
|
||||
result[0] = result.at(0).toLower();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString varPath(const QString &n, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
if (cfg.dpointer) {
|
||||
result = QLatin1String{"d->"} + varName(n, cfg);
|
||||
} else {
|
||||
result = varName(n, cfg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumName(const QString &n)
|
||||
{
|
||||
QString result = QLatin1String("Enum") + n;
|
||||
result[4] = result.at(4).toUpper();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumName(const QString &n, const CfgEntry::Choices &c)
|
||||
{
|
||||
QString result = c.name();
|
||||
if (result.isEmpty()) {
|
||||
result = QLatin1String("Enum") + n;
|
||||
result[4] = result.at(4).toUpper();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumType(const CfgEntry *e, bool globalEnums)
|
||||
{
|
||||
QString result = e->choices.name();
|
||||
if (result.isEmpty()) {
|
||||
result = QLatin1String("Enum") + e->name;
|
||||
if (!globalEnums) {
|
||||
result += QLatin1String("::type");
|
||||
}
|
||||
result[4] = result.at(4).toUpper();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
|
||||
{
|
||||
QString result = c.name();
|
||||
if (result.isEmpty()) {
|
||||
result = QLatin1String("Enum") + n + QLatin1String("::");
|
||||
result[4] = result.at(4).toUpper();
|
||||
} else if (c.external()) {
|
||||
result = c.externalQualifier();
|
||||
} else {
|
||||
result.clear();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString setFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = QLatin1String("set") + n;
|
||||
result[3] = result.at(3).toUpper();
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result = className + QLatin1String("::") + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString changeSignalName(const QString &n)
|
||||
{
|
||||
return n + QLatin1String{"Changed"};
|
||||
}
|
||||
|
||||
QString getDefaultFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = QLatin1String("default%1Value").arg(n);
|
||||
result[7] = result.at(7).toUpper();
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result.prepend(className + QLatin1String("::"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString getFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = n;
|
||||
result[0] = result.at(0).toLower();
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result.prepend(className + QLatin1String("::"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString immutableFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = QLatin1String("is") + n;
|
||||
result[2] = result.at(2).toUpper();
|
||||
result += QLatin1String{"Immutable"};
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result.prepend(className + QLatin1String("::"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void addQuotes(QString &s)
|
||||
{
|
||||
if (!s.startsWith(QLatin1Char('"'))) {
|
||||
s.prepend(QLatin1Char('"'));
|
||||
}
|
||||
if (!s.endsWith(QLatin1Char('"'))) {
|
||||
s.append(QLatin1Char('"'));
|
||||
}
|
||||
}
|
||||
|
||||
static QString quoteString(const QString &s)
|
||||
{
|
||||
QString r = s;
|
||||
r.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
|
||||
r.replace(QLatin1Char('\"'), QLatin1String("\\\""));
|
||||
r.remove(QLatin1Char('\r'));
|
||||
r.replace(QLatin1Char('\n'), QLatin1String("\\n\"\n\""));
|
||||
return QLatin1Char('\"') + r + QLatin1Char('\"');
|
||||
}
|
||||
|
||||
QString literalString(const QString &str)
|
||||
{
|
||||
const bool isAscii = std::none_of(str.cbegin(), str.cend(), [](const QChar ch) {
|
||||
return ch.unicode() > 127;
|
||||
});
|
||||
|
||||
if (isAscii) {
|
||||
return QLatin1String("QStringLiteral( %1 )").arg(quoteString(str));
|
||||
} else {
|
||||
return QLatin1String("QString::fromUtf8( %1 )").arg(quoteString(str));
|
||||
}
|
||||
}
|
||||
|
||||
QString signalEnumName(const QString &signalName)
|
||||
{
|
||||
QString result;
|
||||
result = QLatin1String("signal") + signalName;
|
||||
result[6] = result.at(6).toUpper();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isUnsigned(const QString &type)
|
||||
{
|
||||
return type == QLatin1String("UInt") || type == QLatin1String("ULongLong");
|
||||
}
|
||||
|
||||
/**
|
||||
Return parameter declaration for given type.
|
||||
*/
|
||||
QString param(const QString &t)
|
||||
{
|
||||
const QString type = t.toLower();
|
||||
if (type == QLatin1String("string")) {
|
||||
return QStringLiteral("const QString &");
|
||||
} else if (type == QLatin1String("stringlist")) {
|
||||
return QStringLiteral("const QStringList &");
|
||||
} else if (type == QLatin1String("font")) {
|
||||
return QStringLiteral("const QFont &");
|
||||
} else if (type == QLatin1String("rect")) {
|
||||
return QStringLiteral("const QRect &");
|
||||
} else if (type == QLatin1String("rectf")) {
|
||||
return QStringLiteral("const QRectF &");
|
||||
} else if (type == QLatin1String("size")) {
|
||||
return QStringLiteral("const QSize &");
|
||||
} else if (type == QLatin1String("sizef")) {
|
||||
return QStringLiteral("const QSizeF &");
|
||||
} else if (type == QLatin1String("color")) {
|
||||
return QStringLiteral("const QColor &");
|
||||
} else if (type == QLatin1String("point")) {
|
||||
return QStringLiteral("const QPoint &");
|
||||
} else if (type == QLatin1String("pointf")) {
|
||||
return QStringLiteral("const QPointF &");
|
||||
} else if (type == QLatin1String("int")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("uint")) {
|
||||
return QStringLiteral("uint");
|
||||
} else if (type == QLatin1String("bool")) {
|
||||
return QStringLiteral("bool");
|
||||
} else if (type == QLatin1String("double")) {
|
||||
return QStringLiteral("double");
|
||||
} else if (type == QLatin1String("datetime")) {
|
||||
return QStringLiteral("const QDateTime &");
|
||||
} else if (type == QLatin1String("longlong")) {
|
||||
return QStringLiteral("qint64");
|
||||
} else if (type == QLatin1String("ulonglong")) {
|
||||
return QStringLiteral("quint64");
|
||||
} else if (type == QLatin1String("intlist")) {
|
||||
return QStringLiteral("const QList<int> &");
|
||||
} else if (type == QLatin1String("enum")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("path")) {
|
||||
return QStringLiteral("const QString &");
|
||||
} else if (type == QLatin1String("pathlist")) {
|
||||
return QStringLiteral("const QStringList &");
|
||||
} else if (type == QLatin1String("password")) {
|
||||
return QStringLiteral("const QString &");
|
||||
} else if (type == QLatin1String("url")) {
|
||||
return QStringLiteral("const QUrl &");
|
||||
} else if (type == QLatin1String("urllist")) {
|
||||
return QStringLiteral("const QList<QUrl> &");
|
||||
} else {
|
||||
std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
|
||||
return QStringLiteral("QString"); // For now, but an assert would be better
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Actual C++ storage type for given type.
|
||||
*/
|
||||
QString cppType(const QString &t)
|
||||
{
|
||||
const QString type = t.toLower();
|
||||
if (type == QLatin1String("string")) {
|
||||
return QStringLiteral("QString");
|
||||
} else if (type == QLatin1String("stringlist")) {
|
||||
return QStringLiteral("QStringList");
|
||||
} else if (type == QLatin1String("font")) {
|
||||
return QStringLiteral("QFont");
|
||||
} else if (type == QLatin1String("rect")) {
|
||||
return QStringLiteral("QRect");
|
||||
} else if (type == QLatin1String("rectf")) {
|
||||
return QStringLiteral("QRectF");
|
||||
} else if (type == QLatin1String("size")) {
|
||||
return QStringLiteral("QSize");
|
||||
} else if (type == QLatin1String("sizef")) {
|
||||
return QStringLiteral("QSizeF");
|
||||
} else if (type == QLatin1String("color")) {
|
||||
return QStringLiteral("QColor");
|
||||
} else if (type == QLatin1String("point")) {
|
||||
return QStringLiteral("QPoint");
|
||||
} else if (type == QLatin1String("pointf")) {
|
||||
return QStringLiteral("QPointF");
|
||||
} else if (type == QLatin1String("int")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("uint")) {
|
||||
return QStringLiteral("uint");
|
||||
} else if (type == QLatin1String("bool")) {
|
||||
return QStringLiteral("bool");
|
||||
} else if (type == QLatin1String("double")) {
|
||||
return QStringLiteral("double");
|
||||
} else if (type == QLatin1String("datetime")) {
|
||||
return QStringLiteral("QDateTime");
|
||||
} else if (type == QLatin1String("longlong")) {
|
||||
return QStringLiteral("qint64");
|
||||
} else if (type == QLatin1String("ulonglong")) {
|
||||
return QStringLiteral("quint64");
|
||||
} else if (type == QLatin1String("intlist")) {
|
||||
return QStringLiteral("QList<int>");
|
||||
} else if (type == QLatin1String("enum")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("path")) {
|
||||
return QStringLiteral("QString");
|
||||
} else if (type == QLatin1String("pathlist")) {
|
||||
return QStringLiteral("QStringList");
|
||||
} else if (type == QLatin1String("password")) {
|
||||
return QStringLiteral("QString");
|
||||
} else if (type == QLatin1String("url")) {
|
||||
return QStringLiteral("QUrl");
|
||||
} else if (type == QLatin1String("urllist")) {
|
||||
return QStringLiteral("QList<QUrl>");
|
||||
} else {
|
||||
std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
|
||||
return QStringLiteral("QString"); // For now, but an assert would be better
|
||||
}
|
||||
}
|
||||
|
||||
QString defaultValue(const QString &t)
|
||||
{
|
||||
const QString type = t.toLower();
|
||||
if (type == QLatin1String("string")) {
|
||||
return QStringLiteral("\"\""); // Use empty string, not null string!
|
||||
} else if (type == QLatin1String("stringlist")) {
|
||||
return QStringLiteral("QStringList()");
|
||||
} else if (type == QLatin1String("font")) {
|
||||
return QStringLiteral("QFont()");
|
||||
} else if (type == QLatin1String("rect")) {
|
||||
return QStringLiteral("QRect()");
|
||||
} else if (type == QLatin1String("rectf")) {
|
||||
return QStringLiteral("QRectF()");
|
||||
} else if (type == QLatin1String("size")) {
|
||||
return QStringLiteral("QSize()");
|
||||
} else if (type == QLatin1String("sizef")) {
|
||||
return QStringLiteral("QSizeF()");
|
||||
} else if (type == QLatin1String("color")) {
|
||||
return QStringLiteral("QColor(128, 128, 128)");
|
||||
} else if (type == QLatin1String("point")) {
|
||||
return QStringLiteral("QPoint()");
|
||||
} else if (type == QLatin1String("pointf")) {
|
||||
return QStringLiteral("QPointF()");
|
||||
} else if (type == QLatin1String("int")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("uint")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("bool")) {
|
||||
return QStringLiteral("false");
|
||||
} else if (type == QLatin1String("double")) {
|
||||
return QStringLiteral("0.0");
|
||||
} else if (type == QLatin1String("datetime")) {
|
||||
return QStringLiteral("QDateTime()");
|
||||
} else if (type == QLatin1String("longlong")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("ulonglong")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("intlist")) {
|
||||
return QStringLiteral("QList<int>()");
|
||||
} else if (type == QLatin1String("enum")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("path")) {
|
||||
return QStringLiteral("\"\""); // Use empty string, not null string!
|
||||
} else if (type == QLatin1String("pathlist")) {
|
||||
return QStringLiteral("QStringList()");
|
||||
} else if (type == QLatin1String("password")) {
|
||||
return QStringLiteral("\"\""); // Use empty string, not null string!
|
||||
} else if (type == QLatin1String("url")) {
|
||||
return QStringLiteral("QUrl()");
|
||||
} else if (type == QLatin1String("urllist")) {
|
||||
return QStringLiteral("QList<QUrl>()");
|
||||
} else {
|
||||
std::cerr << "Error, kconfig_compiler_kf6 does not support the \"" << qPrintable(type) << "\" type!" << std::endl;
|
||||
return QStringLiteral("QString"); // For now, but an assert would be better
|
||||
}
|
||||
}
|
||||
|
||||
QString itemType(const QString &type)
|
||||
{
|
||||
if (type.isEmpty()) {
|
||||
return QString{};
|
||||
}
|
||||
|
||||
QString str = type;
|
||||
str[0] = str.at(0).toUpper();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
if (e->name.isEmpty()) {
|
||||
return QString{};
|
||||
}
|
||||
|
||||
const QString type = cfg.inherits + QLatin1String{"::Item"} + itemType(e->type);
|
||||
|
||||
QString fCap = e->name;
|
||||
fCap[0] = fCap.at(0).toUpper();
|
||||
const QString argSuffix = (!e->param.isEmpty()) ? (QStringLiteral("[%1]").arg(e->paramMax + 1)) : QString();
|
||||
QString result;
|
||||
|
||||
if (!cfg.itemAccessors && !cfg.dpointer) {
|
||||
result += QLatin1String{" "} + (!e->signalList.isEmpty() ? QStringLiteral("KConfigCompilerSignallingItem") : type);
|
||||
result += QLatin1String(" *item%1;\n").arg(fCap + argSuffix);
|
||||
}
|
||||
|
||||
if (!e->signalList.isEmpty()) {
|
||||
result += QLatin1String(" %1 *%2;\n").arg(type, innerItemVar(e, cfg) + argSuffix);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns the name of an item variable
|
||||
// use itemPath to know the full path
|
||||
// like using d-> in case of dpointer
|
||||
QString itemVar(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
if (cfg.itemAccessors) {
|
||||
if (!cfg.dpointer) {
|
||||
result = QLatin1String("m%1Item").arg(e->name);
|
||||
result[1] = result.at(1).toUpper();
|
||||
} else {
|
||||
result = e->name + QLatin1String{"Item"};
|
||||
result[0] = result.at(0).toLower();
|
||||
}
|
||||
} else {
|
||||
result = QLatin1String{"item"} + e->name;
|
||||
result[4] = result.at(4).toUpper();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns the name of the local inner item if there is one
|
||||
// (before wrapping with KConfigCompilerSignallingItem)
|
||||
// Otherwise return itemVar()
|
||||
QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
if (e->signalList.isEmpty()) {
|
||||
return itemPath(e, cfg);
|
||||
}
|
||||
|
||||
QString result = QLatin1String{"innerItem"} + e->name;
|
||||
result[9] = result.at(9).toUpper();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString itemPath(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
return cfg.dpointer ? QLatin1String{"d->"} + itemVar(e, cfg) : itemVar(e, cfg);
|
||||
}
|
||||
|
||||
QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m)
|
||||
{
|
||||
QString str = QLatin1String("new %1::Item%2").arg(cfg.inherits, itemType(entry->type));
|
||||
str += QLatin1String("( currentGroup(), %1, %2").arg(key, varPath(entry->name, cfg) + param);
|
||||
|
||||
if (entry->type == QLatin1String("Enum")) {
|
||||
str += QLatin1String{", values"} + entry->name;
|
||||
}
|
||||
if (!defaultValue.isEmpty()) {
|
||||
str += QLatin1String(", ") + defaultValue;
|
||||
}
|
||||
str += QLatin1String(" );");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m)
|
||||
{
|
||||
const QList<Signal> sigs = entry->signalList;
|
||||
if (sigs.isEmpty()) {
|
||||
return newInnerItem(entry, key, defaultValue, cfg, param);
|
||||
}
|
||||
|
||||
QString str;
|
||||
str += QLatin1String("new KConfigCompilerSignallingItem(%1, this, notifyFunction, ").arg(innerItemVar(entry, cfg) + param);
|
||||
// Append the signal flags
|
||||
const int listSize = sigs.size();
|
||||
for (int i = 0; i < listSize; ++i) {
|
||||
if (i != 0) {
|
||||
str += QLatin1String(" | ");
|
||||
}
|
||||
str += signalEnumName(sigs[i].name);
|
||||
}
|
||||
str += QLatin1String(");");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
QString paramString(const QString &s, const CfgEntry *e, int i)
|
||||
{
|
||||
QString result = s;
|
||||
const QString needle = QLatin1String("$(%1)").arg(e->param);
|
||||
if (result.contains(needle)) {
|
||||
const QString tmp = e->paramType == QLatin1String{"Enum"} ? e->paramValues.at(i) : QString::number(i);
|
||||
|
||||
result.replace(needle, tmp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString paramString(const QString &group, const QList<Param> ¶meters)
|
||||
{
|
||||
QString paramString = group;
|
||||
QString arguments;
|
||||
int i = 1;
|
||||
bool firstArg = true;
|
||||
for (const auto ¶m : parameters) {
|
||||
const QString paramName = param.name;
|
||||
const QString str = QLatin1String("$(%1)").arg(paramName);
|
||||
if (paramString.contains(str)) {
|
||||
const QString tmp = QStringLiteral("%%1").arg(i++);
|
||||
paramString.replace(str, tmp);
|
||||
|
||||
if (firstArg) {
|
||||
arguments += QLatin1String{".arg( "};
|
||||
firstArg = false;
|
||||
}
|
||||
|
||||
arguments += QLatin1String("mParam%1, ").arg(paramName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arguments.isEmpty()) {
|
||||
// Remove the last ", "
|
||||
arguments.chop(2);
|
||||
|
||||
// Close the ".arg( "
|
||||
arguments += QLatin1String{" )"};
|
||||
} else {
|
||||
return QLatin1String("QStringLiteral( \"%1\" )").arg(group);
|
||||
}
|
||||
|
||||
return QLatin1String("QStringLiteral( \"%1\" )%2").arg(paramString, arguments);
|
||||
}
|
||||
|
||||
QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString ¶m, const QString ¶mValue)
|
||||
{
|
||||
QString result;
|
||||
|
||||
switch (cfg.translationSystem) {
|
||||
case KConfigParameters::QtTranslation:
|
||||
if (!context.isEmpty()) {
|
||||
result += QLatin1String("/*: %1 */ QCoreApplication::translate(\"").arg(context);
|
||||
} else {
|
||||
result += QLatin1String{"QCoreApplication::translate(\""};
|
||||
}
|
||||
result += QLatin1String("%1\", ").arg(cfg.className);
|
||||
break;
|
||||
|
||||
case KConfigParameters::KdeTranslation:
|
||||
if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {
|
||||
result += QLatin1String("i18ndc(%1, %2, ").arg(quoteString(cfg.translationDomain), quoteString(context));
|
||||
} else if (!cfg.translationDomain.isEmpty()) {
|
||||
result += QLatin1String("i18nd(%1, ").arg(quoteString(cfg.translationDomain));
|
||||
} else if (!context.isEmpty()) {
|
||||
result += QLatin1String("i18nc(%1, ").arg(quoteString(context));
|
||||
} else {
|
||||
result += QLatin1String{"i18n("};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!param.isEmpty()) {
|
||||
QString resolvedString = string;
|
||||
resolvedString.replace(QLatin1String("$(%1)").arg(param), paramValue);
|
||||
result += quoteString(resolvedString);
|
||||
} else {
|
||||
result += quoteString(string);
|
||||
}
|
||||
|
||||
result += QLatin1Char{')'};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* int i is the value of the parameter */
|
||||
QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i)
|
||||
{
|
||||
QString txt;
|
||||
if (itemVarStr.isNull()) {
|
||||
itemVarStr = itemPath(e, cfg);
|
||||
}
|
||||
if (!e->label.isEmpty()) {
|
||||
txt += QLatin1String(" %1->setLabel( %2 );\n").arg(itemVarStr, translatedString(cfg, e->label, e->labelContext, e->param, i));
|
||||
}
|
||||
if (!e->toolTip.isEmpty()) {
|
||||
txt += QLatin1String(" %1->setToolTip( %2 );\n").arg(itemVarStr, translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i));
|
||||
}
|
||||
if (!e->whatsThis.isEmpty()) {
|
||||
txt += QLatin1String(" %1->setWhatsThis( %2 );\n").arg(itemVarStr, translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i));
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
// returns the member mutator implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
// TODO: Fix add Debug Method, it should also take the debug string.
|
||||
void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n)
|
||||
{
|
||||
if (cfg.qCategoryLoggingName.isEmpty()) {
|
||||
out << " qDebug() << \"" << setFunction(n);
|
||||
} else {
|
||||
out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n);
|
||||
}
|
||||
}
|
||||
|
||||
// returns the member get default implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
QString memberGetDefaultBody(const CfgEntry *e)
|
||||
{
|
||||
QString result = e->code;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
out << '\n';
|
||||
|
||||
if (!e->param.isEmpty()) {
|
||||
out << " switch (i) {\n";
|
||||
for (int i = 0; i <= e->paramMax; ++i) {
|
||||
if (!e->paramDefaultValues[i].isEmpty()) {
|
||||
out << " case " << i << ": return " << e->paramDefaultValues[i] << ";\n";
|
||||
}
|
||||
}
|
||||
QString defaultValue = e->defaultValue;
|
||||
|
||||
out << " default:\n";
|
||||
out << " return " << defaultValue.replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("i")) << ";\n";
|
||||
out << " }\n";
|
||||
} else {
|
||||
out << " return " << e->defaultValue << ';';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns the item accessor implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
|
||||
out << "return " << itemPath(e, cfg);
|
||||
if (!e->param.isEmpty()) {
|
||||
out << "[i]";
|
||||
}
|
||||
out << ";\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// indents text adding X spaces per line
|
||||
QString indent(QString text, int spaces)
|
||||
{
|
||||
QString result;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
QTextStream in(&text, QIODevice::ReadOnly);
|
||||
QString currLine;
|
||||
while (!in.atEnd()) {
|
||||
currLine = in.readLine();
|
||||
if (!currLine.isEmpty()) {
|
||||
for (int i = 0; i < spaces; ++i) {
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
out << currLine << '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool hasErrors(KConfigXmlParser &parser, const ParseResult &parseResult, const KConfigParameters &cfg)
|
||||
{
|
||||
Q_UNUSED(parser)
|
||||
|
||||
if (cfg.className.isEmpty()) {
|
||||
std::cerr << "Class name missing" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg.singleton && !parseResult.parameters.isEmpty()) {
|
||||
std::cerr << "Singleton class can not have parameters" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) {
|
||||
std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: For some reason some configuration files prefer to have *no* entries
|
||||
* at all in it, and the generated code is mostly bogus as KConfigXT will not
|
||||
* handle save / load / properties, etc, nothing.
|
||||
*
|
||||
* The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop
|
||||
* project.
|
||||
* I think we should remove the possibility of creating configuration classes from configuration
|
||||
* files that don't really have configuration in it. but I'm changing this right now to allow
|
||||
* kdevelop files to pass.
|
||||
*
|
||||
* Remove for KDE 6
|
||||
* (to make things more interesting, it failed in a code that's never used within KDevelop... )
|
||||
*/
|
||||
if (parseResult.entries.isEmpty()) {
|
||||
std::cerr << "No entries." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationName(QStringLiteral("kconfig_compiler"));
|
||||
app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
|
||||
|
||||
QString inputFilename;
|
||||
QString codegenFilename;
|
||||
|
||||
QCommandLineOption targetDirectoryOption(QStringList{QStringLiteral("d"), QStringLiteral("directory")},
|
||||
QCoreApplication::translate("main", "Directory to generate files in [.]"),
|
||||
QCoreApplication::translate("main", "directory"),
|
||||
QStringLiteral("."));
|
||||
|
||||
QCommandLineOption licenseOption(QStringList{QStringLiteral("l"), QStringLiteral("license")},
|
||||
QCoreApplication::translate("main", "Display software license."));
|
||||
|
||||
QCommandLineParser parser;
|
||||
|
||||
parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));
|
||||
parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file"));
|
||||
|
||||
parser.addOption(targetDirectoryOption);
|
||||
parser.addOption(licenseOption);
|
||||
|
||||
parser.addVersionOption();
|
||||
parser.addHelpOption();
|
||||
parser.process(app);
|
||||
|
||||
if (parser.isSet(licenseOption)) {
|
||||
std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl;
|
||||
std::cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl;
|
||||
std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl;
|
||||
std::cout << "You may redistribute copies of this program" << std::endl;
|
||||
std::cout << "under the terms of the GNU Library Public License." << std::endl;
|
||||
std::cout << "For more information about these matters, see the file named COPYING." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (args.count() < 2) {
|
||||
std::cerr << "Too few arguments." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (args.count() > 2) {
|
||||
std::cerr << "Too many arguments." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
inputFilename = args.at(0);
|
||||
codegenFilename = args.at(1);
|
||||
|
||||
// TODO: Transform baseDir into a helper.
|
||||
QString baseDir = parser.value(targetDirectoryOption);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (!baseDir.endsWith(QLatin1Char{'/'}) && !baseDir.endsWith(QLatin1Char{'\\'})) {
|
||||
#else
|
||||
if (!baseDir.endsWith(QLatin1Char{'/'})) {
|
||||
#endif
|
||||
baseDir.append(QLatin1Char{'/'});
|
||||
}
|
||||
|
||||
KConfigParameters cfg(codegenFilename);
|
||||
|
||||
KConfigXmlParser xmlParser(cfg, inputFilename);
|
||||
|
||||
// The Xml Parser aborts in the case of an error, so if we get
|
||||
// to parseResult, we have a working Xml file.
|
||||
xmlParser.start();
|
||||
|
||||
ParseResult parseResult = xmlParser.getParseResult();
|
||||
|
||||
if (hasErrors(xmlParser, parseResult, cfg)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: Move this to somewhere saner.
|
||||
for (const auto &signal : std::as_const(parseResult.signalList)) {
|
||||
parseResult.hasNonModifySignals |= !signal.modify;
|
||||
}
|
||||
|
||||
// remove '.kcfg' from the name.
|
||||
const QString baseName = inputFilename.mid(0, inputFilename.size() - 5);
|
||||
KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult);
|
||||
headerGenerator.start();
|
||||
headerGenerator.save();
|
||||
|
||||
KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult);
|
||||
sourceGenerator.start();
|
||||
sourceGenerator.save();
|
||||
|
||||
qDeleteAll(parseResult.entries);
|
||||
}
|
||||
Reference in New Issue
Block a user