aboutsummaryrefslogtreecommitdiff
path: root/src/kconfig_compiler/kconfig_compiler.cpp
diff options
context:
space:
mode:
authorTomaz Canabrava <tcanabrava@kde.org>2020-01-17 10:03:43 +0000
committerTomaz Canabrava <tcanabrava@kde.org>2020-01-17 10:03:43 +0000
commit5f8c2ce63499d05dfb4753eb1acc21dccf21d434 (patch)
treefed718a3c8f4c4974411d612a79c4125ae3fffd8 /src/kconfig_compiler/kconfig_compiler.cpp
parent98c32e29f50465d4d4e16bafdf0491edbfb422b0 (diff)
downloadkconfig-5f8c2ce63499d05dfb4753eb1acc21dccf21d434.tar.gz
kconfig-5f8c2ce63499d05dfb4753eb1acc21dccf21d434.tar.bz2
Revert "WIP: Refactor KConfigXT"
This reverts commit 98c32e29f50465d4d4e16bafdf0491edbfb422b0. This broke kdevelop and kmymoney. I'm workin on the fixes and more unittests for them.
Diffstat (limited to 'src/kconfig_compiler/kconfig_compiler.cpp')
-rw-r--r--src/kconfig_compiler/kconfig_compiler.cpp2208
1 files changed, 2088 insertions, 120 deletions
diff --git a/src/kconfig_compiler/kconfig_compiler.cpp b/src/kconfig_compiler/kconfig_compiler.cpp
index 00699c36..848595f2 100644
--- a/src/kconfig_compiler/kconfig_compiler.cpp
+++ b/src/kconfig_compiler/kconfig_compiler.cpp
@@ -34,19 +34,15 @@
#include <QDomAttr>
#include <QRegularExpression>
#include <QStringList>
-#include <QCommandLineParser>
-#include <QCommandLineOption>
+
+#include <qcommandlineparser.h>
+#include <qcommandlineoption.h>
#include <ostream>
#include <iostream>
#include <stdlib.h>
#include "../../kconfig_version.h"
-#include "KConfigXTParameters.h"
-#include "KConfigCommonStructs.h"
-#include "KConfigHeaderGenerator.h"
-#include "KConfigSourceGenerator.h"
-#include "KCFGXmlParser.h"
namespace
{
@@ -54,7 +50,444 @@ QTextStream cout(stdout);
QTextStream cerr(stderr);
}
-QString varName(const QString &n, const KConfigXTParameters &cfg)
+QStringList allNames;
+QRegularExpression *validNameRegexp;
+QString This;
+QString Const;
+
+/**
+ Configuration Compiler Configuration
+*/
+class CfgConfig
+{
+public:
+ CfgConfig(const QString &codegenFilename)
+ {
+ // 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()) {
+ cerr << "Class name missing" << 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 += ' ';
+ }
+ 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")) {
+ cerr << "Unknown translation system, falling back to Qt tr()" << endl;
+ }
+ translationSystem = QtTranslation;
+ }
+ qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString();
+ headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString();
+ sourceExtension = codegenConfig.value(QStringLiteral("SourceExtension"), QStringLiteral("cpp")).toString();
+ }
+
+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;
+ QString sourceExtension;
+ bool allMutators;
+ bool setUserTexts;
+ bool allDefaultGetters;
+ bool dpointer;
+ bool globalEnums;
+ bool useEnumTypes;
+ bool itemAccessors;
+ bool allNotifiers;
+ TranslationSystem translationSystem;
+ QString translationDomain;
+ bool generateProperties;
+};
+
+struct SignalArguments {
+ QString type;
+ QString variableName;
+};
+
+class Signal
+{
+public:
+ Signal() : modify(false) {}
+
+ QString name;
+ QString label;
+ QList<SignalArguments> arguments;
+ bool modify;
+};
+
+class CfgEntry
+{
+public:
+ struct Choice {
+ QString name;
+ QString context;
+ QString label;
+ QString toolTip;
+ QString whatsThis;
+ };
+ 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;
+ };
+
+ CfgEntry(const QString &group, const QString &type, const QString &key,
+ const QString &name, const QString &labelContext, const QString &label,
+ const QString &toolTipContext, const QString &toolTip, const QString &whatsThisContext, const QString &whatsThis, const QString &code,
+ const QString &defaultValue, const Choices &choices, const QList<Signal> &signalList,
+ bool hidden)
+ : mGroup(group), mType(type), mKey(key), mName(name),
+ mLabelContext(labelContext), mLabel(label), mToolTipContext(toolTipContext), mToolTip(toolTip),
+ mWhatsThisContext(whatsThisContext), mWhatsThis(whatsThis),
+ mCode(code), mDefaultValue(defaultValue), mChoices(choices),
+ mSignalList(signalList), mParamMax(0), mHidden(hidden)
+ {
+ }
+
+ void setGroup(const QString &group)
+ {
+ mGroup = group;
+ }
+ QString group() const
+ {
+ return mGroup;
+ }
+
+ void setType(const QString &type)
+ {
+ mType = type;
+ }
+ QString type() const
+ {
+ return mType;
+ }
+
+ void setKey(const QString &key)
+ {
+ mKey = key;
+ }
+ QString key() const
+ {
+ return mKey;
+ }
+
+ void setName(const QString &name)
+ {
+ mName = name;
+ }
+ QString name() const
+ {
+ return mName;
+ }
+
+ void setLabelContext(const QString &labelContext)
+ {
+ mLabelContext = labelContext;
+ }
+ QString labelContext() const
+ {
+ return mLabelContext;
+ }
+
+ void setLabel(const QString &label)
+ {
+ mLabel = label;
+ }
+ QString label() const
+ {
+ return mLabel;
+ }
+
+ void setToolTipContext(const QString &toolTipContext)
+ {
+ mToolTipContext = toolTipContext;
+ }
+ QString toolTipContext() const
+ {
+ return mToolTipContext;
+ }
+
+ void setToolTip(const QString &toolTip)
+ {
+ mToolTip = toolTip;
+ }
+ QString toolTip() const
+ {
+ return mToolTip;
+ }
+
+ void setWhatsThisContext(const QString &whatsThisContext)
+ {
+ mWhatsThisContext = whatsThisContext;
+ }
+ QString whatsThisContext() const
+ {
+ return mWhatsThisContext;
+ }
+
+ void setWhatsThis(const QString &whatsThis)
+ {
+ mWhatsThis = whatsThis;
+ }
+ QString whatsThis() const
+ {
+ return mWhatsThis;
+ }
+
+ void setDefaultValue(const QString &d)
+ {
+ mDefaultValue = d;
+ }
+ QString defaultValue() const
+ {
+ return mDefaultValue;
+ }
+
+ void setCode(const QString &d)
+ {
+ mCode = d;
+ }
+ QString code() const
+ {
+ return mCode;
+ }
+
+ void setMinValue(const QString &d)
+ {
+ mMin = d;
+ }
+ QString minValue() const
+ {
+ return mMin;
+ }
+
+ void setMaxValue(const QString &d)
+ {
+ mMax = d;
+ }
+ QString maxValue() const
+ {
+ return mMax;
+ }
+
+ void setParam(const QString &d)
+ {
+ mParam = d;
+ }
+ QString param() const
+ {
+ return mParam;
+ }
+
+ void setParamName(const QString &d)
+ {
+ mParamName = d;
+ }
+ QString paramName() const
+ {
+ return mParamName;
+ }
+
+ void setParamType(const QString &d)
+ {
+ mParamType = d;
+ }
+ QString paramType() const
+ {
+ return mParamType;
+ }
+
+ void setChoices(const QList<Choice> &d, const QString &n, const QString &p)
+ {
+ mChoices = Choices(d, n, p);
+ }
+ Choices choices() const
+ {
+ return mChoices;
+ }
+
+ void setParamValues(const QStringList &d)
+ {
+ mParamValues = d;
+ }
+ QStringList paramValues() const
+ {
+ return mParamValues;
+ }
+
+ void setParamDefaultValues(const QStringList &d)
+ {
+ mParamDefaultValues = d;
+ }
+ QString paramDefaultValue(int i) const
+ {
+ return mParamDefaultValues[i];
+ }
+
+ void setParamMax(int d)
+ {
+ mParamMax = d;
+ }
+ int paramMax() const
+ {
+ return mParamMax;
+ }
+
+ void setSignalList(const QList<Signal> &value)
+ {
+ mSignalList = value;
+ }
+ QList<Signal> signalList() const
+ {
+ return mSignalList;
+ }
+
+ bool hidden() const
+ {
+ return mHidden;
+ }
+
+ void dump() const
+ {
+ cerr << "<entry>" << endl;
+ cerr << " group: " << mGroup << endl;
+ cerr << " type: " << mType << endl;
+ cerr << " key: " << mKey << endl;
+ cerr << " name: " << mName << endl;
+ cerr << " label context: " << mLabelContext << endl;
+ cerr << " label: " << mLabel << endl;
+// whatsthis
+ cerr << " code: " << mCode << endl;
+// cerr << " values: " << mValues.join(":") << endl;
+
+ if (!param().isEmpty()) {
+ cerr << " param name: " << mParamName << endl;
+ cerr << " param type: " << mParamType << endl;
+ cerr << " paramvalues: " << mParamValues.join(QChar::fromLatin1(':')) << endl;
+ }
+ cerr << " default: " << mDefaultValue << endl;
+ cerr << " hidden: " << mHidden << endl;
+ cerr << " min: " << mMin << endl;
+ cerr << " max: " << mMax << endl;
+ cerr << "</entry>" << endl;
+ }
+
+private:
+ QString mGroup;
+ QString mType;
+ QString mKey;
+ QString mName;
+ QString mLabelContext;
+ QString mLabel;
+ QString mToolTipContext;
+ QString mToolTip;
+ QString mWhatsThisContext;
+ QString mWhatsThis;
+ QString mCode;
+ QString mDefaultValue;
+ QString mParam;
+ QString mParamName;
+ QString mParamType;
+ Choices mChoices;
+ QList<Signal> mSignalList;
+ QStringList mParamValues;
+ QStringList mParamDefaultValues;
+ int mParamMax;
+ bool mHidden;
+ QString mMin;
+ QString mMax;
+};
+
+class Param
+{
+public:
+ QString name;
+ QString type;
+};
+
+// returns the name of an member variable
+// use itemPath to know the full path
+// like using d-> in case of dpointer
+static QString varName(const QString &n, const CfgConfig &cfg)
{
QString result;
if (!cfg.dpointer) {
@@ -67,7 +500,7 @@ QString varName(const QString &n, const KConfigXTParameters &cfg)
return result;
}
-QString varPath(const QString &n, const KConfigXTParameters &cfg)
+static QString varPath(const QString &n, const CfgConfig &cfg)
{
QString result;
if (cfg.dpointer) {
@@ -78,14 +511,14 @@ QString varPath(const QString &n, const KConfigXTParameters &cfg)
return result;
}
-QString enumName(const QString &n)
+static QString enumName(const QString &n)
{
QString result = QLatin1String("Enum") + n;
result[4] = result[4].toUpper();
return result;
}
-QString enumName(const QString &n, const CfgEntry::Choices &c)
+static QString enumName(const QString &n, const CfgEntry::Choices &c)
{
QString result = c.name();
if (result.isEmpty()) {
@@ -95,11 +528,11 @@ QString enumName(const QString &n, const CfgEntry::Choices &c)
return result;
}
-QString enumType(const CfgEntry *e, bool globalEnums)
+static QString enumType(const CfgEntry *e, bool globalEnums)
{
- QString result = e->choices.name();
+ QString result = e->choices().name();
if (result.isEmpty()) {
- result = QLatin1String("Enum") + e->name;
+ result = QLatin1String("Enum") + e->name();
if (!globalEnums) {
result += QLatin1String("::type");
}
@@ -108,7 +541,7 @@ QString enumType(const CfgEntry *e, bool globalEnums)
return result;
}
-QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
+static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
{
QString result = c.name();
if (result.isEmpty()) {
@@ -122,7 +555,7 @@ QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
return result;
}
-QString setFunction(const QString &n, const QString &className)
+static QString setFunction(const QString &n, const QString &className = QString())
{
QString result = QLatin1String("set") + n;
result[3] = result[3].toUpper();
@@ -133,12 +566,12 @@ QString setFunction(const QString &n, const QString &className)
return result;
}
-QString changeSignalName(const QString &n)
+static QString changeSignalName(const QString &n)
{
return n+QStringLiteral("Changed");
}
-QString getDefaultFunction(const QString &n, const QString &className)
+static QString getDefaultFunction(const QString &n, const QString &className = QString())
{
QString result = QLatin1String("default") + n + QLatin1String("Value");
result[7] = result[7].toUpper();
@@ -149,7 +582,7 @@ QString getDefaultFunction(const QString &n, const QString &className)
return result;
}
-QString getFunction(const QString &n, const QString &className)
+static QString getFunction(const QString &n, const QString &className = QString())
{
QString result = n;
result[0] = result[0].toLower();
@@ -160,7 +593,7 @@ QString getFunction(const QString &n, const QString &className)
return result;
}
-void addQuotes(QString &s)
+static void addQuotes(QString &s)
{
if (!s.startsWith(QLatin1Char('"'))) {
s.prepend(QLatin1Char('"'));
@@ -180,7 +613,7 @@ static QString quoteString(const QString &s)
return QLatin1Char('\"') + r + QLatin1Char('\"');
}
-QString literalString(const QString &s)
+static QString literalString(const QString &s)
{
bool isAscii = true;
for (int i = s.length(); i--;)
@@ -195,8 +628,20 @@ QString literalString(const QString &s)
}
}
+static QString dumpNode(const QDomNode &node)
+{
+ QString msg;
+ QTextStream s(&msg, QIODevice::WriteOnly);
+ node.save(s, 0);
-QString signalEnumName(const QString &signalName)
+ msg = msg.simplified();
+ if (msg.length() > 40) {
+ return msg.left(37) + QLatin1String("...");
+ }
+ return msg;
+}
+
+static QString signalEnumName(const QString &signalName)
{
QString result;
result = QLatin1String("signal") + signalName;
@@ -205,8 +650,345 @@ QString signalEnumName(const QString &signalName)
return result;
}
+static void preProcessDefault(QString &defaultValue, const QString &name,
+ const QString &type,
+ const CfgEntry::Choices &choices,
+ QString &code, const CfgConfig &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( ") + literalString(defaultValue) + QLatin1Char(')');
+ } else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) {
+ QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append);
+ if (!code.isEmpty()) {
+ cpp << endl;
+ }
-bool isUnsigned(const QString &type)
+ if (type == QLatin1String("UrlList")) {
+ cpp << " QList<QUrl> default" << name << ";" << endl;
+ } else {
+ cpp << " QStringList default" << name << ";" << endl;
+ }
+ const QStringList defaults = defaultValue.split(QLatin1Char(','));
+ QStringList::ConstIterator it;
+ for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) {
+ cpp << " default" << name << ".append( ";
+ if (type == QLatin1String("UrlList")) {
+ cpp << "QUrl::fromUserInput(";
+ }
+ cpp << "QString::fromUtf8( \"" << *it << "\" ) ";
+ if (type == QLatin1String("UrlList")) {
+ cpp << ") ";
+ }
+ cpp << ");" << endl;
+ }
+ defaultValue = QLatin1String("default") + name;
+
+ } else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) {
+ const QRegularExpression colorRe(QRegularExpression::anchoredPattern(
+ QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?")));
+
+ if (colorRe.match(defaultValue).hasMatch()) {
+ defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )");
+ } else {
+ defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )");
+ }
+
+ } else if (type == QLatin1String("Enum")) {
+ QList<CfgEntry::Choice>::ConstIterator it;
+ for (it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it) {
+ if ((*it).name == defaultValue) {
+ if (cfg.globalEnums && choices.name().isEmpty()) {
+ defaultValue.prepend(choices.prefix);
+ } else {
+ defaultValue.prepend(enumTypeQualifier(name, choices) + choices.prefix);
+ }
+ break;
+ }
+ }
+
+ } else if (type == QLatin1String("IntList")) {
+ QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append);
+ if (!code.isEmpty()) {
+ cpp << endl;
+ }
+
+ cpp << " QList<int> default" << name << ";" << endl;
+ if (!defaultValue.isEmpty()) {
+ const QStringList defaults = defaultValue.split(QLatin1Char(','));
+ QStringList::ConstIterator it;
+ for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) {
+ cpp << " default" << name << ".append( " << *it << " );"
+ << endl;
+ }
+ }
+ defaultValue = QLatin1String("default") + name;
+ }
+}
+
+CfgEntry *parseEntry(const QString &group, const QDomElement &element, const CfgConfig &cfg)
+{
+ bool defaultCode = false;
+ QString type = element.attribute(QStringLiteral("type"));
+ QString name = element.attribute(QStringLiteral("name"));
+ QString key = element.attribute(QStringLiteral("key"));
+ QString hidden = element.attribute(QStringLiteral("hidden"));
+ QString labelContext;
+ QString label;
+ QString toolTipContext;
+ QString toolTip;
+ QString whatsThisContext;
+ QString whatsThis;
+ QString defaultValue;
+ QString code;
+ QString param;
+ QString paramName;
+ QString paramType;
+ CfgEntry::Choices choices;
+ QList<Signal> signalList;
+ QStringList paramValues;
+ QStringList paramDefaultValues;
+ QString minValue;
+ QString maxValue;
+ int paramMax = 0;
+
+ for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
+ QString tag = e.tagName();
+ if (tag == QLatin1String("label")) {
+ label = e.text();
+ labelContext = e.attribute(QStringLiteral("context"));
+ } else if (tag == QLatin1String("tooltip")) {
+ toolTip = e.text();
+ toolTipContext = e.attribute(QStringLiteral("context"));
+ } else if (tag == QLatin1String("whatsthis")) {
+ whatsThis = e.text();
+ whatsThisContext = e.attribute(QStringLiteral("context"));
+ } else if (tag == QLatin1String("min")) {
+ minValue = e.text();
+ } else if (tag == QLatin1String("max")) {
+ maxValue = e.text();
+ } else if (tag == QLatin1String("code")) {
+ code = e.text();
+ } else if (tag == QLatin1String("parameter")) {
+ param = e.attribute(QStringLiteral("name"));
+ paramType = e.attribute(QStringLiteral("type"));
+ if (param.isEmpty()) {
+ cerr << "Parameter must have a name: " << dumpNode(e) << endl;
+ return nullptr;
+ }
+ if (paramType.isEmpty()) {
+ cerr << "Parameter must have a type: " << dumpNode(e) << endl;
+ return nullptr;
+ }
+ if ((paramType == QLatin1String("Int")) || (paramType == QLatin1String("UInt"))) {
+ bool ok;
+ paramMax = e.attribute(QStringLiteral("max")).toInt(&ok);
+ if (!ok) {
+ cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): "
+ << dumpNode(e) << endl;
+ return nullptr;
+ }
+ } else if (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")) {
+ paramValues.append(e3.text());
+ }
+ }
+ break;
+ }
+ }
+ if (paramValues.isEmpty()) {
+ cerr << "No values specified for parameter '" << param
+ << "'." << endl;
+ return nullptr;
+ }
+ paramMax = paramValues.count() - 1;
+ } else {
+ cerr << "Parameter '" << param << "' has type " << paramType
+ << " but must be of type int, uint or Enum." << endl;
+ return nullptr;
+ }
+ } else if (tag == QLatin1String("default")) {
+ if (e.attribute(QStringLiteral("param")).isEmpty()) {
+ defaultValue = e.text();
+ if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) {
+ defaultCode = true;
+ }
+ }
+ } else if (tag == QLatin1String("choices")) {
+ QString name = e.attribute(QStringLiteral("name"));
+ QString prefix = e.attribute(QStringLiteral("prefix"));
+ QList<CfgEntry::Choice> chlist;
+ for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
+ if (e2.tagName() == QLatin1String("choice")) {
+ CfgEntry::Choice choice;
+ choice.name = e2.attribute(QStringLiteral("name"));
+ if (choice.name.isEmpty()) {
+ cerr << "Tag <choice> requires attribute 'name'." << endl;
+ }
+ 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);
+ }
+ }
+ choices = CfgEntry::Choices(chlist, name, prefix);
+ } else if (tag == QLatin1String("emit")) {
+ Signal signal;
+ signal.name = e.attribute(QStringLiteral("signal"));
+ signalList.append(signal);
+ }
+ }
+
+ if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(name))) {
+ Signal s;
+ s.name = changeSignalName(name);
+ s.modify = true;
+ signalList.append(s);
+ }
+
+ bool nameIsEmpty = name.isEmpty();
+ if (nameIsEmpty && key.isEmpty()) {
+ cerr << "Entry must have a name or a key: " << dumpNode(element) << endl;
+ return nullptr;
+ }
+
+ if (key.isEmpty()) {
+ key = name;
+ }
+
+ if (nameIsEmpty) {
+ name = key;
+ name.remove(' ');
+ } else if (name.contains(' ')) {
+ cout << "Entry '" << name << "' contains spaces! <name> elements can not contain spaces!" << endl;
+ name.remove(' ');
+ }
+
+ if (name.contains(QStringLiteral("$("))) {
+ if (param.isEmpty()) {
+ cerr << "Name may not be parameterized: " << name << endl;
+ return nullptr;
+ }
+ } else {
+ if (!param.isEmpty()) {
+ cerr << "Name must contain '$(" << param << ")': " << name << endl;
+ return nullptr;
+ }
+ }
+
+ if (label.isEmpty()) {
+ label = key;
+ }
+
+ if (type.isEmpty()) {
+ type = QStringLiteral("String"); // XXX : implicit type might be bad
+ }
+
+ if (!param.isEmpty()) {
+ // Adjust name
+ paramName = name;
+ name.remove("$(" + param + ')');
+ // Lookup defaults for indexed entries
+ for (int i = 0; i <= paramMax; i++) {
+ paramDefaultValues.append(QString());
+ }
+
+ for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
+ QString tag = e.tagName();
+ if (tag == QLatin1String("default")) {
+ QString index = e.attribute(QStringLiteral("param"));
+ if (index.isEmpty()) {
+ continue;
+ }
+
+ bool ok;
+ int i = index.toInt(&ok);
+ if (!ok) {
+ i = paramValues.indexOf(index);
+ if (i == -1) {
+ cerr << "Index '" << index << "' for default value is unknown." << endl;
+ return nullptr;
+ }
+ }
+
+ if ((i < 0) || (i > paramMax)) {
+ cerr << "Index '" << i << "' for default value is out of range [0, " << paramMax << "]." << endl;
+ return nullptr;
+ }
+
+ QString tmpDefaultValue = e.text();
+
+ if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) {
+ preProcessDefault(tmpDefaultValue, name, type, choices, code, cfg);
+ }
+
+ paramDefaultValues[i] = tmpDefaultValue;
+ }
+ }
+ }
+
+ if (!validNameRegexp->match(name).hasMatch()) {
+ if (nameIsEmpty)
+ cerr << "The key '" << 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." << endl;
+ else {
+ cerr << "The name '" << name << "' is not a valid name for an entry." << endl;
+ }
+ return nullptr;
+ }
+
+ if (allNames.contains(name)) {
+ if (nameIsEmpty)
+ cerr << "The key '" << 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." << endl;
+ else {
+ cerr << "The name '" << name << "' is not unique." << endl;
+ }
+ return nullptr;
+ }
+ allNames.append(name);
+
+ if (!defaultCode) {
+ preProcessDefault(defaultValue, name, type, choices, code, cfg);
+ }
+
+ CfgEntry *result = new CfgEntry(group, type, key, name, labelContext, label, toolTipContext, toolTip, whatsThisContext, whatsThis,
+ code, defaultValue, choices, signalList,
+ hidden == QLatin1String("true"));
+ if (!param.isEmpty()) {
+ result->setParam(param);
+ result->setParamName(paramName);
+ result->setParamType(paramType);
+ result->setParamValues(paramValues);
+ result->setParamDefaultValues(paramDefaultValues);
+ result->setParamMax(paramMax);
+ }
+ result->setMinValue(minValue);
+ result->setMaxValue(maxValue);
+
+ return result;
+}
+
+static bool isUnsigned(const QString &type)
{
if (type == QLatin1String("UInt")) {
return true;
@@ -386,47 +1168,47 @@ QString itemType(const QString &type)
return t;
}
-QString itemDeclaration(const CfgEntry *e, const KConfigXTParameters &cfg)
+static QString itemDeclaration(const CfgEntry *e, const CfgConfig &cfg)
{
if (cfg.itemAccessors) {
return QString();
}
QString type;
- if (!e->signalList.isEmpty()) {
+ if (!e->signalList().isEmpty()) {
type = QStringLiteral("KConfigCompilerSignallingItem");
} else {
- type = cfg.inherits + "::Item" + itemType(e->type);
+ type = cfg.inherits + "::Item" + itemType(e->type());
}
- QString fCap = e->name;
+ QString fCap = e->name();
fCap[0] = fCap[0].toUpper();
return " " + type + " *item" + fCap +
- ( (!e->param.isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax+1)) : QString()) + ";\n";
+ ( (!e->param().isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax()+1)) : QString()) + ";\n";
}
// 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 KConfigXTParameters &cfg)
+static QString itemVar(const CfgEntry *e, const CfgConfig &cfg)
{
QString result;
if (cfg.itemAccessors) {
if (!cfg.dpointer) {
- result = 'm' + e->name + "Item";
+ result = 'm' + e->name() + "Item";
result[1] = result[1].toUpper();
} else {
- result = e->name + "Item";
+ result = e->name() + "Item";
result[0] = result[0].toLower();
}
} else {
- result = "item" + e->name;
+ result = "item" + e->name();
result[4] = result[4].toUpper();
}
return result;
}
-QString itemPath(const CfgEntry *e, const KConfigXTParameters &cfg)
+static QString itemPath(const CfgEntry *e, const CfgConfig &cfg)
{
QString result;
if (cfg.dpointer) {
@@ -438,18 +1220,18 @@ QString itemPath(const CfgEntry *e, const KConfigXTParameters &cfg)
}
QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue,
- const KConfigXTParameters &cfg, const QString &param) {
+ const CfgConfig &cfg, const QString &param = QString()) {
- QList<Signal> sigs = entry->signalList;
+ QList<Signal> sigs = entry->signalList();
QString t;
if (!sigs.isEmpty()) {
t += QLatin1String("new KConfigCompilerSignallingItem(");
}
- t += "new "+ cfg.inherits + "::Item" + itemType(entry->type) + "( currentGroup(), "
- + key + ", " + varPath( entry->name, cfg ) + param;
+ t += "new "+ cfg.inherits + "::Item" + itemType(entry->type()) + "( currentGroup(), "
+ + key + ", " + varPath( entry->name(), cfg ) + param;
- if (entry->type == QLatin1String("Enum")) {
- t += ", values" + entry->name;
+ if (entry->type() == QLatin1String("Enum")) {
+ t += ", values" + entry->name();
}
if (!defaultValue.isEmpty()) {
t += QLatin1String(", ") + defaultValue;
@@ -473,11 +1255,11 @@ QString newItem(const CfgEntry* entry, const QString &key, const QString& defaul
QString paramString(const QString &s, const CfgEntry *e, int i)
{
QString result = s;
- QString needle = "$(" + e->param + ')';
+ QString needle = "$(" + e->param() + ')';
if (result.contains(needle)) {
QString tmp;
- if (e->paramType == QLatin1String("Enum")) {
- tmp = e->paramValues.at(i);
+ if (e->paramType() == QLatin1String("Enum")) {
+ tmp = e->paramValues().at(i);
} else {
tmp = QString::number(i);
}
@@ -507,12 +1289,12 @@ QString paramString(const QString &group, const QList<Param> &parameters)
return "QStringLiteral( \"" + paramString + "\" )" + arguments;
}
-QString translatedString(const KConfigXTParameters &cfg, const QString &string, const QString &context, const QString &param, const QString &paramValue)
+QString translatedString(const CfgConfig &cfg, const QString &string, const QString &context = QString(), const QString &param = QString(), const QString &paramValue = QString())
{
QString result;
switch (cfg.translationSystem) {
- case KConfigXTParameters::QtTranslation:
+ case CfgConfig::QtTranslation:
if (!context.isEmpty()) {
result += "/*: " + context + " */ QCoreApplication::translate(\"";
} else {
@@ -521,7 +1303,7 @@ QString translatedString(const KConfigXTParameters &cfg, const QString &string,
result += cfg.className + "\", ";
break;
- case KConfigXTParameters::KdeTranslation:
+ case CfgConfig::KdeTranslation:
if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {
result += "i18ndc(" + quoteString(cfg.translationDomain) + ", " + quoteString(context) + ", ";
} else if (!cfg.translationDomain.isEmpty()) {
@@ -548,36 +1330,62 @@ QString translatedString(const KConfigXTParameters &cfg, const QString &string,
}
/* int i is the value of the parameter */
-QString userTextsFunctions(const CfgEntry *e, const KConfigXTParameters &cfg, QString itemVarStr, const QString &i)
+QString userTextsFunctions(CfgEntry *e, const CfgConfig &cfg, QString itemVarStr = QString(), const QString &i = QString())
{
QString txt;
if (itemVarStr.isNull()) {
itemVarStr = itemPath(e, cfg);
}
- if (!e->label.isEmpty()) {
+ if (!e->label().isEmpty()) {
txt += " " + itemVarStr + "->setLabel( ";
- txt += translatedString(cfg, e->label, e->labelContext, e->param, i);
+ txt += translatedString(cfg, e->label(), e->labelContext(), e->param(), i);
txt += QLatin1String(" );\n");
}
- if (!e->toolTip.isEmpty()) {
+ if (!e->toolTip().isEmpty()) {
txt += " " + itemVarStr + "->setToolTip( ";
- txt += translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i);
+ txt += translatedString(cfg, e->toolTip(), e->toolTipContext(), e->param(), i);
txt += QLatin1String(" );\n");
}
- if (!e->whatsThis.isEmpty()) {
+ if (!e->whatsThis().isEmpty()) {
txt += " " + itemVarStr + "->setWhatsThis( ";
- txt += translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i);
+ txt += translatedString(cfg, e->whatsThis(), e->whatsThisContext(), e->param(), i);
txt += QLatin1String(" );\n");
}
return txt;
}
+// returns the member accesor implementation
+// which should go in the h file if inline
+// or the cpp file if not inline
+QString memberAccessorBody(CfgEntry *e, bool globalEnums, const CfgConfig &cfg)
+{
+ QString result;
+ QTextStream out(&result, QIODevice::WriteOnly);
+ QString n = e->name();
+ QString t = e->type();
+ bool useEnumType = cfg.useEnumTypes && t == QLatin1String("Enum");
+
+ out << "return ";
+ if (useEnumType) {
+ out << "static_cast<" << enumType(e, globalEnums) << ">(";
+ }
+ out << This << varPath(n, cfg);
+ if (!e->param().isEmpty()) {
+ out << "[i]";
+ }
+ if (useEnumType) {
+ out << ")";
+ }
+ out << ";" << endl;
+
+ return result;
+}
// 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 KConfigXTParameters &cfg, const QString &n)
+
+void addDebugMethod(QTextStream &out, const CfgConfig &cfg, const QString &n)
{
if (cfg.qCategoryLoggingName.isEmpty()) {
out << " qDebug() << \"" << setFunction(n);
@@ -586,30 +1394,101 @@ void addDebugMethod(QTextStream &out, const KConfigXTParameters &cfg, const QStr
}
}
+QString memberMutatorBody(CfgEntry *e, const CfgConfig &cfg)
+{
+ QString result;
+ QTextStream out(&result, QIODevice::WriteOnly);
+ QString n = e->name();
+ QString t = e->type();
+
+ if (!e->minValue().isEmpty()) {
+ if (e->minValue() != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579)
+ out << "if (v < " << e->minValue() << ")" << endl;
+ out << "{" << endl;
+ addDebugMethod(out, cfg, n);
+ out << ": value \" << v << \" is less than the minimum value of ";
+ out << e->minValue() << "\";" << endl;
+ out << " v = " << e->minValue() << ";" << endl;
+ out << "}" << endl;
+ }
+ }
+
+ if (!e->maxValue().isEmpty()) {
+ out << endl << "if (v > " << e->maxValue() << ")" << endl;
+ out << "{" << endl;
+ addDebugMethod(out, cfg, n);
+ out << ": value \" << v << \" is greater than the maximum value of ";
+ out << e->maxValue() << "\";" << endl;
+ out << " v = " << e->maxValue() << ";" << endl;
+ out << "}" << endl << endl;
+ }
+
+ const QString varExpression = This + varPath(n, cfg) + (e->param().isEmpty() ? QString() : QStringLiteral("[i]"));
+
+ const bool hasBody = !e->signalList().empty() || cfg.generateProperties;
+ out << "if (";
+ if (hasBody) {
+ out << "v != " << varExpression << " && ";
+ }
+ out << "!" << This << "isImmutable( QStringLiteral( \"";
+ if (!e->param().isEmpty()) {
+ out << e->paramName().replace("$(" + e->param() + ")", QLatin1String("%1")) << "\" ).arg( ";
+ if (e->paramType() == QLatin1String("Enum")) {
+ out << "QLatin1String( ";
+
+ if (cfg.globalEnums) {
+ out << enumName(e->param()) << "ToString[i]";
+ } else {
+ out << enumName(e->param()) << "::enumToString[i]";
+ }
+
+ out << " )";
+ } else {
+ out << "i";
+ }
+ out << " )";
+ } else {
+ out << n << "\" )";
+ }
+ out << " ))" << (hasBody ? " {" : "") << endl;
+ out << " " << varExpression << " = v;" << endl;
+
+ const auto listSignal = e->signalList();
+ for (const Signal &signal : listSignal) {
+ if (signal.modify) {
+ out << " Q_EMIT " << This << signal.name << "();" << endl;
+ } else {
+ out << " " << This << varPath(QStringLiteral("settingsChanged"), cfg) << " |= " << signalEnumName(signal.name) << ";" << endl;
+ }
+ }
+ if (hasBody) {
+ out << "}" << endl;
+ }
+
+ return result;
+}
// 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 memberGetDefaultBody(CfgEntry *e)
{
- QString result = e->code;
+ QString result = e->code();
QTextStream out(&result, QIODevice::WriteOnly);
out << endl;
- if (!e->param.isEmpty()) {
+ if (!e->param().isEmpty()) {
out << " switch (i) {" << endl;
- for (int i = 0; i <= e->paramMax; ++i) {
- if (!e->paramDefaultValues[i].isEmpty()) {
- out << " case " << i << ": return " << e->paramDefaultValues[i] << ';' << endl;
+ for (int i = 0; i <= e->paramMax(); ++i) {
+ if (!e->paramDefaultValue(i).isEmpty()) {
+ out << " case " << i << ": return " << e->paramDefaultValue(i) << ';' << endl;
}
}
- QString defaultValue = e->defaultValue;
-
out << " default:" << endl;
- out << " return " << defaultValue.replace("$(" + e->param + ')', QLatin1String("i")) << ';' << endl;
+ out << " return " << e->defaultValue().replace("$(" + e->param() + ')', QLatin1String("i")) << ';' << endl;
out << " }" << endl;
} else {
- out << " return " << e->defaultValue << ';';
+ out << " return " << e->defaultValue() << ';';
}
return result;
@@ -618,13 +1497,13 @@ QString memberGetDefaultBody(const CfgEntry *e)
// returns the item accesor implementation
// which should go in the h file if inline
// or the cpp file if not inline
-QString itemAccessorBody(const CfgEntry *e, const KConfigXTParameters &cfg)
+QString itemAccessorBody(CfgEntry *e, const CfgConfig &cfg)
{
QString result;
QTextStream out(&result, QIODevice::WriteOnly);
out << "return " << itemPath(e, cfg);
- if (!e->param.isEmpty()) {
+ if (!e->param().isEmpty()) {
out << "[i]";
}
out << ";" << endl;
@@ -650,29 +1529,30 @@ QString indent(QString text, int spaces)
return result;
}
-bool hasErrors(KCFGXmlParser &parser, const ParseResult& parseResult, const KConfigXTParameters &cfg)
+// adds as many 'namespace foo {' lines to p_out as
+// there are namespaces in p_ns
+void beginNamespaces(const QString &p_ns, QTextStream &p_out)
{
- if (cfg.className.isEmpty()) {
- cerr << "Class name missing" << endl;
- return true;
- }
-
- if (cfg.singleton && !parseResult.parameters.isEmpty()) {
- cerr << "Singleton class can not have parameters" << endl;
- return true;
- }
-
- if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) {
- cerr << "Having both a fixed filename and a filename as argument is not possible." << endl;
- return true;
+ if (!p_ns.isEmpty()) {
+ const QStringList nameSpaces = p_ns.split(QStringLiteral("::"));
+ for (const QString &ns : nameSpaces) {
+ p_out << "namespace " << ns << " {" << endl;
+ }
+ p_out << endl;
}
+}
- if (parseResult.entries.isEmpty()) {
- cerr << "No entries." << endl;
- return true;
+// adds as many '}' lines to p_out as
+// there are namespaces in p_ns
+void endNamespaces(const QString &p_ns, QTextStream &p_out)
+{
+ if (!p_ns.isEmpty()) {
+ const int namespaceCount = p_ns.count(QStringLiteral("::")) + 1;
+ for (int i = 0; i < namespaceCount; ++i) {
+ p_out << "}" << endl;
+ }
+ p_out << endl;
}
-
- return false;
}
int main(int argc, char **argv)
@@ -681,22 +1561,22 @@ int main(int argc, char **argv)
app.setApplicationName(QStringLiteral("kconfig_compiler"));
app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
- QString inputFilename, codegenFilename;
+ validNameRegexp = new QRegularExpression(QRegularExpression::anchoredPattern(
+ QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*")));
- 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."));
+ QString inputFilename, codegenFilename;
QCommandLineParser parser;
parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));
parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file"));
+ QCommandLineOption targetDirectoryOption(QStringList { QStringLiteral("d"), QStringLiteral("directory") },
+ QCoreApplication::translate("main", "Directory to generate files in [.]"),
+ QCoreApplication::translate("main", "directory"), QStringLiteral("."));
parser.addOption(targetDirectoryOption);
+
+ QCommandLineOption licenseOption (QStringList { QStringLiteral("l"), QStringLiteral("license") }, QCoreApplication::translate("main", "Display software license."));
parser.addOption (licenseOption);
parser.addVersionOption();
@@ -704,7 +1584,7 @@ int main(int argc, char **argv)
parser.process(app);
if (parser.isSet(licenseOption)) {
- cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl;
+ cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl;
cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << endl;
cout << "This program comes with ABSOLUTELY NO WARRANTY." << endl;
cout << "You may redistribute copies of this program" << endl;
@@ -715,18 +1595,16 @@ int main(int argc, char **argv)
const QStringList args = parser.positionalArguments();
if (args.count() < 2) {
- cerr << "Too few arguments." << endl;
- return 1;
+ cerr << "Too few arguments." << endl;
+ return 1;
}
-
if (args.count() > 2) {
- cerr << "Too many arguments." << endl;
- return 1;
+ cerr << "Too many arguments." << 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
@@ -736,34 +1614,1124 @@ int main(int argc, char **argv)
#endif
baseDir.append("/");
- KConfigXTParameters cfg(codegenFilename);
+ if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) {
+ cerr << "Codegen options file must have extension .kcfgc" << endl;
+ return 1;
+ }
+ QString baseName = QFileInfo(codegenFilename).fileName();
+ baseName = baseName.left(baseName.length() - 6);
+
+ CfgConfig cfg = CfgConfig(codegenFilename);
- KCFGXmlParser 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();
+ QFile input(inputFilename);
- ParseResult parseResult = xmlParser.getParseResult();
+ QDomDocument doc;
+ QString errorMsg;
+ int errorRow;
+ int errorCol;
+ if (!doc.setContent(&input, &errorMsg, &errorRow, &errorCol)) {
+ cerr << "Unable to load document." << endl;
+ cerr << "Parse error in " << inputFilename << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl;
+ return 1;
+ }
- if (hasErrors(xmlParser, parseResult, cfg)) {
+ QDomElement cfgElement = doc.documentElement();
+
+ if (cfgElement.isNull()) {
+ cerr << "No document in kcfg file" << endl;
return 1;
}
- // TODO: Move this to somewhere saner.
- for (const auto &signal : qAsConst(parseResult.signalList)) {
- parseResult.hasNonModifySignals |= !signal.modify;
+ QString cfgFileName;
+ bool cfgFileNameArg = false;
+ QList<Param> parameters;
+ QList<Signal> signalList;
+ QStringList includes;
+
+ QList<CfgEntry *> entries;
+
+ for (QDomElement e = cfgElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
+ QString tag = e.tagName();
+
+ if (tag == QLatin1String("include")) {
+ QString includeFile = e.text();
+ if (!includeFile.isEmpty()) {
+ includes.append(includeFile);
+ }
+
+ } else if (tag == QLatin1String("kcfgfile")) {
+ cfgFileName = e.attribute(QStringLiteral("name"));
+ 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");
+ }
+ parameters.append(p);
+ }
+ }
+
+ } else if (tag == QLatin1String("group")) {
+ QString group = e.attribute(QStringLiteral("name"));
+ if (group.isEmpty()) {
+ cerr << "Group without name" << endl;
+ return 1;
+ }
+ for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
+ if (e2.tagName() != QLatin1String("entry")) {
+ continue;
+ }
+ CfgEntry *entry = parseEntry(group, e2, cfg);
+ if (entry) {
+ entries.append(entry);
+ } else {
+ cerr << "Can not parse entry." << endl;
+ return 1;
+ }
+ }
+ } else if (tag == QLatin1String("signal")) {
+ QString signalName = e.attribute(QStringLiteral("name"));
+ if (signalName.isEmpty()) {
+ cerr << "Signal without name." << endl;
+ return 1;
+ }
+ Signal theSignal;
+ theSignal.name = signalName;
+
+ for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
+ if (e2.tagName() == QLatin1String("argument")) {
+ SignalArguments argument;
+ argument.type = e2.attribute(QStringLiteral("type"));
+ if (argument.type.isEmpty()) {
+ cerr << "Signal argument without type." << endl;
+ return 1;
+ }
+ argument.variableName = e2.text();
+ theSignal.arguments.append(argument);
+ } else if (e2.tagName() == QLatin1String("label")) {
+ theSignal.label = e2.text();
+ }
+ }
+ signalList.append(theSignal);
+ }
+ }
+
+ if (cfg.className.isEmpty()) {
+ cerr << "Class name missing" << endl;
+ return 1;
}
- // remove '.kcfg' from the name.
- const QString baseName = inputFilename.mid(0, inputFilename.size()-5);
- KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult);
- headerGenerator.start();
- headerGenerator.save();
+ if (cfg.singleton && !parameters.isEmpty()) {
+ cerr << "Singleton class can not have parameters" << endl;
+ return 1;
+ }
+
+ if (!cfgFileName.isEmpty() && cfgFileNameArg) {
+ cerr << "Having both a fixed filename and a filename as argument is not possible." << endl;
+ return 1;
+ }
+
+ if (entries.isEmpty()) {
+ cerr << "No entries." << endl;
+ }
+
+#if 0
+ CfgEntry *cfg;
+ for (cfg = entries.first(); cfg; cfg = entries.next()) {
+ cfg->dump();
+ }
+#endif
+
+ QString headerFileName = baseName + '.' + cfg.headerExtension;
+ QString implementationFileName = baseName + '.' + cfg.sourceExtension;
+ QString mocFileName = baseName + ".moc";
+ QString cppPreamble; // code to be inserted at the beginnin of the cpp file, e.g. initialization of static values
+
+ QFile header(baseDir + headerFileName);
+ if (!header.open(QIODevice::WriteOnly)) {
+ cerr << "Can not open '" << baseDir << headerFileName << "for writing." << endl;
+ return 1;
+ }
+
+ QTextStream h(&header);
+
+ h.setCodec("utf-8");
+
+ h << "// This file is generated by kconfig_compiler_kf5 from " << QFileInfo(inputFilename).fileName() << "." << endl;
+ h << "// All changes you do to this file will be lost." << endl;
+
+ h << "#ifndef " << (!cfg.nameSpace.isEmpty() ? QString(QString(cfg.nameSpace).replace(QLatin1String("::"), QLatin1String("_")).toUpper() + '_') : QLatin1String(""))
+ << cfg.className.toUpper() << "_H" << endl;
+ h << "#define " << (!cfg.nameSpace.isEmpty() ? QString(QString(cfg.nameSpace).replace(QLatin1String("::"), QLatin1String("_")).toUpper() + '_') : QLatin1String(""))
+ << cfg.className.toUpper() << "_H" << endl << endl;
+
+ // Includes
+ QStringList::ConstIterator it;
+ for (it = cfg.headerIncludes.constBegin(); it != cfg.headerIncludes.constEnd(); ++it) {
+ if ((*it).startsWith('"')) {
+ h << "#include " << *it << endl;
+ } else {
+ h << "#include <" << *it << ">" << endl;
+ }
+ }
+
+ if (!cfg.headerIncludes.isEmpty()) {
+ h << endl;
+ }
+
+ if (!cfg.singleton && parameters.isEmpty()) {
+ h << "#include <qglobal.h>" << endl;
+ }
+
+ if (cfg.inherits == QLatin1String("KCoreConfigSkeleton")) {
+ h << "#include <kcoreconfigskeleton.h>" << endl;
+ } else {
+ h << "#include <kconfigskeleton.h>" << endl;
+ }
+
+ h << "#include <QCoreApplication>" << endl;
+ h << "#include <QDebug>" << endl << endl;
+
+ // Includes
+ for (it = includes.constBegin(); it != includes.constEnd(); ++it) {
+ if ((*it).startsWith('"')) {
+ h << "#include " << *it << endl;
+ } else {
+ h << "#include <" << *it << ">" << endl;
+ }
+ }
+
+ beginNamespaces(cfg.nameSpace, h);
+
+ // Private class declaration
+ if (cfg.dpointer) {
+ h << "class " << cfg.className << "Private;" << endl << endl;
+ }
+
+ // Class declaration header
+ h << "class " << cfg.visibility << cfg.className << " : public " << cfg.inherits << endl;
+
+ h << "{" << endl;
+ // Add Q_OBJECT macro if the config need signals.
+ if (!signalList.isEmpty() || cfg.generateProperties) {
+ h << " Q_OBJECT" << endl;
+ }
+ h << " public:" << endl;
+
+ // enums
+ QList<CfgEntry *>::ConstIterator itEntry;
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ const CfgEntry::Choices &choices = (*itEntry)->choices();
+ const QList<CfgEntry::Choice> chlist = choices.choices;
+ if (!chlist.isEmpty()) {
+ QStringList values;
+ QList<CfgEntry::Choice>::ConstIterator itChoice;
+ for (itChoice = chlist.constBegin(); itChoice != chlist.constEnd(); ++itChoice) {
+ values.append(choices.prefix + (*itChoice).name);
+ }
+ if (choices.name().isEmpty()) {
+ if (cfg.globalEnums) {
+ h << " enum " << enumName((*itEntry)->name(), (*itEntry)->choices()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl;
+ } else {
+ // Create an automatically named enum
+ h << " class " << enumName((*itEntry)->name(), (*itEntry)->choices()) << endl;
+ h << " {" << endl;
+ h << " public:" << endl;
+ h << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl;
+ h << " };" << endl;
+ }
+ } else if (!choices.external()) {
+ // Create a named enum
+ h << " enum " << enumName((*itEntry)->name(), (*itEntry)->choices()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl;
+ }
+ }
+ const QStringList values = (*itEntry)->paramValues();
+ if (!values.isEmpty()) {
+ if (cfg.globalEnums) {
+ // ### FIXME!!
+ // make the following string table an index-based string search!
+ // ###
+ h << " enum " << enumName((*itEntry)->param()) << " { " << values.join(QStringLiteral(", ")) << " };" << endl;
+ h << " static const char* const " << enumName((*itEntry)->param()) << "ToString[];" << endl;
+ cppPreamble += "const char* const " + cfg.className + "::" + enumName((*itEntry)->param()) +
+ "ToString[] = { \"" + values.join(QStringLiteral("\", \"")) + "\" };\n";
+ } else {
+ h << " class " << enumName((*itEntry)->param()) << endl;
+ h << " {" << endl;
+ h << " public:" << endl;
+ h << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl;
+ h << " static const char* const enumToString[];" << endl;
+ h << " };" << endl;
+ cppPreamble += "const char* const " + cfg.className + "::" + enumName((*itEntry)->param()) +
+ "::enumToString[] = { \"" + values.join(QStringLiteral("\", \"")) + "\" };\n";
+ }
+ }
+ }
+ h << endl;
+
+
+ // Constructor or singleton accessor
+ if (!cfg.singleton) {
+ h << " " << cfg.className << "(";
+ if (cfgFileNameArg) {
+ if (cfg.forceStringFilename)
+ h << " const QString &cfgfilename"
+ << (parameters.isEmpty() ? " = QString()" : ", ");
+ else
+ h << " KSharedConfig::Ptr config"
+ << (parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", ");
+ }
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it) {
+ if (it != parameters.constBegin()) {
+ h << ",";
+ }
+ h << " " << param((*it).type) << " " << (*it).name;
+ }
+ if (cfg.parentInConstructor) {
+ if (cfgFileNameArg || !parameters.isEmpty()) {
+ h << ",";
+ }
+ h << " QObject *parent = nullptr";
+ }
+ h << " );" << endl;
+ } else {
+ h << " static " << cfg.className << " *self();" << endl;
+ if (cfgFileNameArg) {
+ h << " static void instance(const QString& cfgfilename);" << endl;
+ h << " static void instance(KSharedConfig::Ptr config);" << endl;
+ }
+ }
+
+ // Destructor
+ h << " ~" << cfg.className << "();" << endl << endl;
+
+ // global variables
+ if (cfg.staticAccessors) {
+ This = QStringLiteral("self()->");
+ } else {
+ Const = QStringLiteral(" const");
+ }
+
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ QString n = (*itEntry)->name();
+ QString t = (*itEntry)->type();
+
+ // Manipulator
+ if (cfg.allMutators || cfg.mutators.contains(n)) {
+ h << " /**" << endl;
+ h << " Set " << (*itEntry)->label() << endl;
+ h << " */" << endl;
+ if (cfg.staticAccessors) {
+ h << " static" << endl;
+ }
+ h << " void " << setFunction(n) << "( ";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << cppType((*itEntry)->paramType()) << " i, ";
+ }
+ if (cfg.useEnumTypes && t == QLatin1String("Enum")) {
+ h << enumType(*itEntry, cfg.globalEnums);
+ } else {
+ h << param(t);
+ }
+ h << " v )";
+ // function body inline only if not using dpointer
+ // for BC mode
+ if (!cfg.dpointer) {
+ h << endl << " {" << endl;
+ h << indent(memberMutatorBody(*itEntry, cfg), 6);
+ h << " }" << endl;
+ } else {
+ h << ";" << endl;
+ }
+ }
+ h << endl;
+
+ QString returnType;
+ if (cfg.useEnumTypes && t == QLatin1String("Enum")) {
+ returnType = enumType(*itEntry, cfg.globalEnums);
+ } else {
+ returnType = cppType(t);
+ }
+
+ if (cfg.generateProperties) {
+ h << " Q_PROPERTY(" << returnType << ' ' << getFunction(n);
+ h << " READ " << getFunction(n);
+ if (cfg.allMutators || cfg.mutators.contains(n)) {
+ const QString signal = changeSignalName(n);
+ h << " WRITE " << setFunction(n);
+ h << " 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;
+ signalList.append(s);
+ } else {
+ h << " CONSTANT";
+ }
+ h << ")" << endl;
+ }
+ // Accessor
+ h << " /**" << endl;
+ h << " Get " << (*itEntry)->label() << endl;
+ h << " */" << endl;
+ if (cfg.staticAccessors) {
+ h << " static" << endl;
+ }
+ h << " ";
+ h << returnType;
+ h << " " << getFunction(n) << "(";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ h << ")" << Const;
+ // function body inline only if not using dpointer
+ // for BC mode
+ if (!cfg.dpointer) {
+ h << endl << " {" << endl;
+ h << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 6);
+ h << " }" << endl;
+ } else {
+ h << ";" << endl;
+ }
+
+ // Default value Accessor
+ if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !(*itEntry)->defaultValue().isEmpty()) {
+ h << endl;
+ h << " /**" << endl;
+ h << " Get " << (*itEntry)->label() << " default value" << endl;
+ h << " */" << endl;
+ if (cfg.staticAccessors) {
+ h << " static" << endl;
+ }
+ h << " ";
+ if (cfg.useEnumTypes && t == QLatin1String("Enum")) {
+ h << enumType(*itEntry, cfg.globalEnums);
+ } else {
+ h << cppType(t);
+ }
+ h << " " << getDefaultFunction(n) << "(";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ h << ")" << Const << endl;
+ h << " {" << endl;
+ h << " return ";
+ if (cfg.useEnumTypes && t == QLatin1String("Enum")) {
+ h << "static_cast<" << enumType(*itEntry, cfg.globalEnums) << ">(";
+ }
+ h << getDefaultFunction(n) << "_helper(";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << " i ";
+ }
+ h << ")";
+ if (cfg.useEnumTypes && t == QLatin1String("Enum")) {
+ h << ")";
+ }
+ h << ";" << endl;
+ h << " }" << endl;
+ }
+
+ // Item accessor
+ if (cfg.itemAccessors) {
+ h << endl;
+ h << " /**" << endl;
+ h << " Get Item object corresponding to " << n << "()"
+ << endl;
+ h << " */" << endl;
+ h << " Item" << itemType((*itEntry)->type()) << " *"
+ << getFunction(n) << "Item(";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ h << ")";
+ if (!cfg.dpointer) {
+ h << endl << " {" << endl;
+ h << indent(itemAccessorBody((*itEntry), cfg), 6);
+ h << " }" << endl;
+ } else {
+ h << ";" << endl;
+ }
+ }
+
+ h << endl;
+ }
+
+ // Signal definition.
+ const bool hasSignals = !signalList.isEmpty();
+ bool hasNonModifySignals = false;
+ if (hasSignals) {
+ h << "\n enum {" << endl;
+ unsigned val = 1;
+ QList<Signal>::ConstIterator it, itEnd = signalList.constEnd();
+ for (it = signalList.constBegin(); it != itEnd; val <<= 1) {
+ hasNonModifySignals |= !it->modify;
+ if (!val) {
+ cerr << "Too many signals to create unique bit masks" << endl;
+ exit(1);
+ }
+ Signal signal = *it;
+ h << " " << signalEnumName(signal.name) << " = 0x" <<
+ #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
+ hex
+ #else
+ Qt::hex
+ #endif
+ << val;
+ if (++it != itEnd) {
+ h << ",";
+ }
+ h << endl;
+ }
+ h << " };" <<
+ #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
+ dec
+ #else
+ Qt::dec
+ #endif
+ << endl << endl;
+
+ h << " Q_SIGNALS:";
+ for (const Signal &signal : qAsConst(signalList)) {
+ h << endl;
+ if (!signal.label.isEmpty()) {
+ h << " /**" << endl;
+ h << " " << signal.label << endl;
+ h << " */" << endl;
+ }
+ h << " void " << signal.name << "(";
+ QList<SignalArguments>::ConstIterator it, itEnd = signal.arguments.constEnd();
+ for (it = signal.arguments.constBegin(); it != itEnd;) {
+ SignalArguments argument = *it;
+ QString type = param(argument.type);
+ if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) {
+ for (int i = 0, end = entries.count(); i < end; ++i) {
+ if (entries[i]->name() == argument.variableName) {
+ type = enumType(entries[i], cfg.globalEnums);
+ break;
+ }
+ }
+ }
+ h << type << " " << argument.variableName;
+ if (++it != itEnd) {
+ h << ", ";
+ }
+ }
+ h << ");" << endl;
+ }
+ h << endl;
+
+ h << " private:" << endl;
+ h << " void itemChanged(quint64 flags);" << endl;
+ h << endl;
+ }
+
+ h << " protected:" << endl;
+
+ // Private constructor for singleton
+ if (cfg.singleton) {
+ h << " " << cfg.className << "(";
+ if (cfgFileNameArg) {
+ h << "KSharedConfig::Ptr config";
+ }
+ if (cfg.parentInConstructor) {
+ if (cfgFileNameArg) {
+ h << ", ";
+ }
+ h << "QObject *parent = nullptr";
+ }
+ h << ");" << endl;
+ h << " friend class " << cfg.className << "Helper;" << endl << endl;
+ }
+
+ if (hasNonModifySignals) {
+ h << " bool usrSave() override;" << endl;
+ }
+
+ // Member variables
+ if (!cfg.memberVariables.isEmpty() && cfg.memberVariables != QLatin1String("private") && cfg.memberVariables != QLatin1String("dpointer")) {
+ h << " " << cfg.memberVariables << ":" << endl;
+ }
+
+ // Class Parameters
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it) {
+ h << " " << cppType((*it).type) << " mParam" << (*it).name << ";" << endl;
+ }
+
+ if (cfg.memberVariables != QLatin1String("dpointer")) {
+ QString group;
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ if ((*itEntry)->group() != group) {
+ group = (*itEntry)->group();
+ h << endl;
+ h << " // " << group << endl;
+ }
+ h << " " << cppType((*itEntry)->type()) << " " << varName((*itEntry)->name(), cfg);
+ if (!(*itEntry)->param().isEmpty()) {
+ h << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1);
+ }
+ h << ";" << endl;
+
+ if (cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name())) {
+ h << " ";
+ if (cfg.staticAccessors) {
+ h << "static ";
+ }
+ h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper(";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ h << ")" << Const << ";" << endl;
+ }
+ }
+
+ h << endl << " private:" << endl;
+ if (cfg.itemAccessors) {
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ h << " Item" << itemType((*itEntry)->type()) << " *" << itemVar(*itEntry, cfg);
+ if (!(*itEntry)->param().isEmpty()) {
+ h << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1);
+ }
+ h << ";" << endl;
+ }
+ }
+ if (hasNonModifySignals) {
+ h << " uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl;
+ }
+
+ } else {
+ // use a private class for both member variables and items
+ h << " private:" << endl;
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ if (cfg.allDefaultGetters || cfg.defaultGetters.contains((*itEntry)->name())) {
+ h << " ";
+ if (cfg.staticAccessors) {
+ h << "static ";
+ }
+ h << cppType((*itEntry)->type()) << " " << getDefaultFunction((*itEntry)->name()) << "_helper(";
+ if (!(*itEntry)->param().isEmpty()) {
+ h << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ h << ")" << Const << ";" << endl;
+ }
+ }
+ h << " " + cfg.className + "Private *d;" << endl;
+ }
+
+ if (cfg.customAddons) {
+ h << " // Include custom additions" << endl;
+ h << " #include \"" << baseName << "_addons." << cfg.headerExtension << '"' << endl;
+ }
+
+ h << "};" << endl << endl;
+
+ endNamespaces(cfg.nameSpace, h);
+
+ h << "#endif" << endl << endl;
+
+ header.close();
+
+ QFile implementation(baseDir + implementationFileName);
+ if (!implementation.open(QIODevice::WriteOnly)) {
+ cerr << "Can not open '" << implementationFileName << "for writing."
+ << endl;
+ return 1;
+ }
+
+ QTextStream cpp(&implementation);
+
+ cpp.setCodec("utf-8");
+
+ cpp << "// This file is generated by kconfig_compiler_kf5 from " << QFileInfo(inputFilename).fileName() << "." << endl;
+ cpp << "// All changes you do to this file will be lost." << endl << endl;
+
+ cpp << "#include \"" << headerFileName << "\"" << endl << endl;
+
+ for (it = cfg.sourceIncludes.constBegin(); it != cfg.sourceIncludes.constEnd(); ++it) {
+ if ((*it).startsWith('"')) {
+ cpp << "#include " << *it << endl;
+ } else {
+ cpp << "#include <" << *it << ">" << endl;
+ }
+ }
+
+ if (!cfg.sourceIncludes.isEmpty()) {
+ cpp << endl;
+ }
+
+ if (cfg.setUserTexts && cfg.translationSystem == CfgConfig::KdeTranslation) {
+ cpp << "#include <klocalizedstring.h>" << endl << endl;
+ }
+
+ // Header required by singleton implementation
+ if (cfg.singleton) {
+ cpp << "#include <qglobal.h>" << endl << "#include <QFile>" << endl << endl;
+ }
+ if (cfg.singleton && cfgFileNameArg) {
+ cpp << "#include <QDebug>" << endl << endl;
+ }
+
+ if (!cfg.nameSpace.isEmpty()) {
+ cpp << "using namespace " << cfg.nameSpace << ";" << endl << endl;
+ }
+
+ QString group;
+
+ // private class implementation
+ if (cfg.dpointer) {
+ beginNamespaces(cfg.nameSpace, cpp);
+ cpp << "class " << cfg.className << "Private" << endl;
+ cpp << "{" << endl;
+ cpp << " public:" << endl;
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ if ((*itEntry)->group() != group) {
+ group = (*itEntry)->group();
+ cpp << endl;
+ cpp << " // " << group << endl;
+ }
+ cpp << " " << cppType((*itEntry)->type()) << " " << varName((*itEntry)->name(), cfg);
+ if (!(*itEntry)->param().isEmpty()) {
+ cpp << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1);
+ }
+ cpp << ";" << endl;
+ }
+ cpp << endl << " // items" << endl;
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ const QString declType = (*itEntry)->signalList().isEmpty()
+ ? QString(cfg.inherits + "::Item" + itemType((*itEntry)->type()))
+ : QStringLiteral("KConfigCompilerSignallingItem");
+ cpp << " " << declType << " *" << itemVar( *itEntry, cfg );
+ if (!(*itEntry)->param().isEmpty()) {
+ cpp << QStringLiteral("[%1]").arg((*itEntry)->paramMax() + 1);
+ }
+ cpp << ";" << endl;
+ }
+ if (hasNonModifySignals) {
+ cpp << " uint " << varName(QStringLiteral("settingsChanged"), cfg) << ";" << endl;
+ }
+
+ cpp << "};" << endl << endl;
+ endNamespaces(cfg.nameSpace, cpp);
+ }
+
+ // Singleton implementation
+ if (cfg.singleton) {
+ beginNamespaces(cfg.nameSpace, cpp);
+ cpp << "class " << cfg.className << "Helper" << endl;
+ cpp << '{' << endl;
+ cpp << " public:" << endl;
+ cpp << " " << cfg.className << "Helper() : q(nullptr) {}" << endl;
+ cpp << " ~" << cfg.className << "Helper() { delete q; }" << endl;
+ cpp << " " << cfg.className << "Helper(const " << cfg.className << "Helper&) = delete;" << endl;
+ cpp << " " << cfg.className << "Helper& operator=(const " << cfg.className << "Helper&) = delete;" << endl;
+ cpp << " " << cfg.className << " *q;" << endl;
+ cpp << "};" << endl;
+ endNamespaces(cfg.nameSpace, cpp);
+ cpp << "Q_GLOBAL_STATIC(" << cfg.className << "Helper, s_global" << cfg.className << ")" << endl;
+
+ cpp << cfg.className << " *" << cfg.className << "::self()" << endl;
+ cpp << "{" << endl;
+ if (cfgFileNameArg) {
+ cpp << " if (!s_global" << cfg.className << "()->q)" << endl;
+ cpp << " qFatal(\"you need to call " << cfg.className << "::instance before using\");" << endl;
+ } else {
+ cpp << " if (!s_global" << cfg.className << "()->q) {" << endl;
+ cpp << " new " << cfg.className << ';' << endl;
+ cpp << " s_global" << cfg.className << "()->q->read();" << endl;
+ cpp << " }" << endl << endl;
+ }
+ cpp << " return s_global" << cfg.className << "()->q;" << endl;
+ cpp << "}" << endl << endl;
+
+ if (cfgFileNameArg) {
+ auto instance = [&cfg, &cpp] (const QString &type, const QString &arg, bool isString) {
+ cpp << "void " << cfg.className << "::instance(" << type << " " << arg << ")" << endl;
+ cpp << "{" << endl;
+ cpp << " if (s_global" << cfg.className << "()->q) {" << endl;
+ cpp << " qDebug() << \"" << cfg.className << "::instance called after the first use - ignoring\";" << endl;
+ cpp << " return;" << endl;
+ cpp << " }" << endl;
+ cpp << " new " << cfg.className << "(";
+ if (isString) {
+ cpp << "KSharedConfig::openConfig(" << arg << ")";
+ } else {
+ cpp << "std::move(" << arg << ")";
+ }
+ cpp << ");" << endl;
+ cpp << " s_global" << cfg.className << "()->q->read();" << endl;
+ cpp << "}" << endl << endl;
+ };
+ instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true);
+ instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false);
+ }
+ }
+
+ if (!cppPreamble.isEmpty()) {
+ cpp << cppPreamble << endl;
+ }
+
+ // Constructor
+ cpp << cfg.className << "::" << cfg.className << "(";
+ if (cfgFileNameArg) {
+ if (! cfg.forceStringFilename) {
+ cpp << " KSharedConfig::Ptr config";
+ } else {
+ cpp << " const QString& config";
+ }
+ cpp << (parameters.isEmpty() ? "" : ",");
+ }
+
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it) {
+ if (it != parameters.constBegin()) {
+ cpp << ",";
+ }
+ cpp << " " << param((*it).type) << " " << (*it).name;
+ }
+
+ if (cfg.parentInConstructor) {
+ if (cfgFileNameArg || !parameters.isEmpty()) {
+ cpp << ",";
+ }
+ cpp << " QObject *parent";
+ }
+ cpp << " )" << endl;
+
+ cpp << " : " << cfg.inherits << "(";
+ if (!cfgFileName.isEmpty()) {
+ cpp << " QStringLiteral( \"" << cfgFileName << "\" ";
+ }
+ if (cfgFileNameArg) {
+ if (! cfg.forceStringFilename) {
+ cpp << " std::move( config ) ";
+ } else {
+ cpp << " config ";
+ }
+ }
+ if (!cfgFileName.isEmpty()) {
+ cpp << ") ";
+ }
+ cpp << ")" << endl;
+
+ // Store parameters
+ for (QList<Param>::ConstIterator it = parameters.constBegin();
+ it != parameters.constEnd(); ++it) {
+ cpp << " , mParam" << (*it).name << "(" << (*it).name << ")" << endl;
+ }
+
+ if (hasNonModifySignals && !cfg.dpointer) {
+ cpp << " , " << varName(QStringLiteral("settingsChanged"), cfg) << "(0)" << endl;
+ }
+
+ cpp << "{" << endl;
+
+ if (cfg.parentInConstructor) {
+ cpp << " setParent(parent);" << endl;
+ }
+
+ if (cfg.dpointer) {
+ cpp << " d = new " + cfg.className + "Private;" << endl;
+ if (hasNonModifySignals) {
+ cpp << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl;
+ }
+ }
+ // Needed in case the singleton class is used as baseclass for
+ // another singleton.
+ if (cfg.singleton) {
+ cpp << " Q_ASSERT(!s_global" << cfg.className << "()->q);" << endl;
+ cpp << " s_global" << cfg.className << "()->q = this;" << endl;
+ }
+
+ group.clear();
+
+ if (hasSignals) {
+ // 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/
+ cpp << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction ="
+ << " static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&"
+ << cfg.className << "::itemChanged);" << endl << endl;
+ }
+
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ if ((*itEntry)->group() != group) {
+ if (!group.isEmpty()) {
+ cpp << endl;
+ }
+ group = (*itEntry)->group();
+ cpp << " setCurrentGroup( " << paramString(group, parameters) << " );" << endl << endl;
+ }
+
+ QString key = paramString((*itEntry)->key(), parameters);
+ if (!(*itEntry)->code().isEmpty()) {
+ cpp << (*itEntry)->code() << endl;
+ }
+ if ((*itEntry)->type() == QLatin1String("Enum")) {
+ cpp << " QList<" + cfg.inherits + "::ItemEnum::Choice> values"
+ << (*itEntry)->name() << ";" << endl;
+ const QList<CfgEntry::Choice> choices = (*itEntry)->choices().choices;
+ QList<CfgEntry::Choice>::ConstIterator it;
+ for (it = choices.constBegin(); it != choices.constEnd(); ++it) {
+ cpp << " {" << endl;
+ cpp << " " + cfg.inherits + "::ItemEnum::Choice choice;" << endl;
+ cpp << " choice.name = QStringLiteral(\"" << (*it).name << "\");" << endl;
+ if (cfg.setUserTexts) {
+ if (!(*it).label.isEmpty()) {
+ cpp << " choice.label = "
+ << translatedString(cfg, (*it).label, (*it).context)
+ << ";" << endl;
+ }
+ if (!(*it).toolTip.isEmpty()) {
+ cpp << " choice.toolTip = "
+ << translatedString(cfg, (*it).toolTip, (*it).context)
+ << ";" << endl;
+ }
+ if (!(*it).whatsThis.isEmpty()) {
+ cpp << " choice.whatsThis = "
+ << translatedString(cfg, (*it).whatsThis, (*it).context)
+ << ";" << endl;
+ }
+ }
+ cpp << " values" << (*itEntry)->name() << ".append( choice );" << endl;
+ cpp << " }" << endl;
+ }
+ }
+
+ if (!cfg.dpointer) {
+ cpp << itemDeclaration(*itEntry, cfg);
+ }
+
+ if ((*itEntry)->param().isEmpty()) {
+ // Normal case
+ cpp << " " << itemPath(*itEntry, cfg) << " = "
+ << newItem((*itEntry), key, (*itEntry)->defaultValue(), cfg) << endl;
+
+ if (!(*itEntry)->minValue().isEmpty()) {
+ cpp << " " << itemPath(*itEntry, cfg) << "->setMinValue(" << (*itEntry)->minValue() << ");" << endl;
+ }
+ if (!(*itEntry)->maxValue().isEmpty()) {
+ cpp << " " << itemPath(*itEntry, cfg) << "->setMaxValue(" << (*itEntry)->maxValue() << ");" << endl;
+ }
+
+ if (cfg.setUserTexts) {
+ cpp << userTextsFunctions((*itEntry), cfg);
+ }
+
+ if (cfg.allNotifiers || cfg.notifiers.contains((*itEntry)->name())) {
+ cpp << " " << itemPath(*itEntry, cfg) << "->setWriteFlags(KConfigBase::Notify);" << endl;
+ }
+
+ cpp << " addItem( " << itemPath(*itEntry, cfg);
+ QString quotedName = (*itEntry)->name();
+ addQuotes(quotedName);
+ if (quotedName != key) {
+ cpp << ", QStringLiteral( \"" << (*itEntry)->name() << "\" )";
+ }
+ cpp << " );" << endl;
+ } else {
+ // Indexed
+ for (int i = 0; i <= (*itEntry)->paramMax(); i++) {
+ QString defaultStr;
+ QString itemVarStr(itemPath(*itEntry, cfg) + QStringLiteral("[%1]").arg(i));
+
+ if (!(*itEntry)->paramDefaultValue(i).isEmpty()) {
+ defaultStr = (*itEntry)->paramDefaultValue(i);
+ } else if (!(*itEntry)->defaultValue().isEmpty()) {
+ defaultStr = paramString((*itEntry)->defaultValue(), (*itEntry), i);
+ } else {
+ defaultStr = defaultValue((*itEntry)->type());
+ }
+
+ cpp << " " << itemVarStr << " = "
+ << newItem((*itEntry), paramString(key, *itEntry, i), defaultStr, cfg, QStringLiteral("[%1]").arg(i)) << endl;
+
+ if (cfg.setUserTexts) {
+ cpp << userTextsFunctions(*itEntry, cfg, itemVarStr, (*itEntry)->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!.
+ cpp << " addItem( " << itemVarStr << ", QStringLiteral( \"";
+ if ((*itEntry)->paramType() == QLatin1String("Enum")) {
+ cpp << (*itEntry)->paramName().replace("$(" + (*itEntry)->param() + ')', QLatin1String("%1")).arg((*itEntry)->paramValues()[i]);
+ } else {
+ cpp << (*itEntry)->paramName().replace("$(" + (*itEntry)->param() + ')', QLatin1String("%1")).arg(i);
+ }
+ cpp << "\" ) );" << endl;
+ }
+ }
+ }
+
+ cpp << "}" << endl << endl;
+
+ if (cfg.dpointer) {
+ // setters and getters go in Cpp if in dpointer mode
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ QString n = (*itEntry)->name();
+ QString t = (*itEntry)->type();
+
+ // Manipulator
+ if (cfg.allMutators || cfg.mutators.contains(n)) {
+ cpp << "void " << setFunction(n, cfg.className) << "( ";
+ if (!(*itEntry)->param().isEmpty()) {
+ cpp << cppType((*itEntry)->paramType()) << " i, ";
+ }
+ if (cfg.useEnumTypes && t == QLatin1String("Enum")) {
+ cpp << enumType(*itEntry, cfg.globalEnums);
+ } else {
+ cpp << param(t);
+ }
+ cpp << " v )" << endl;
+ // function body inline only if not using dpointer
+ // for BC mode
+ cpp << "{" << endl;
+ cpp << indent(memberMutatorBody(*itEntry, cfg), 6);
+ cpp << "}" << endl << endl;
+ }
+
+ // Accessor
+ if (cfg.useEnumTypes && t == QLatin1String("Enum")) {
+ cpp << enumType(*itEntry, cfg.globalEnums);
+ } else {
+ cpp << cppType(t);
+ }
+ cpp << " " << getFunction(n, cfg.className) << "(";
+ if (!(*itEntry)->param().isEmpty()) {
+ cpp << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ cpp << ")" << Const << endl;
+ // function body inline only if not using dpointer
+ // for BC mode
+ cpp << "{" << endl;
+ cpp << indent(memberAccessorBody(*itEntry, cfg.globalEnums, cfg), 2);
+ cpp << "}" << endl << endl;
+
+ // Default value Accessor -- written by the loop below
+
+ // Item accessor
+ if (cfg.itemAccessors) {
+ cpp << endl;
+ cpp << cfg.inherits + "::Item" << itemType((*itEntry)->type()) << " *"
+ << getFunction(n, cfg.className) << "Item(";
+ if (!(*itEntry)->param().isEmpty()) {
+ cpp << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ cpp << ")" << endl;
+ cpp << "{" << endl;
+ cpp << indent(itemAccessorBody(*itEntry, cfg), 2);
+ cpp << "}" << endl;
+ }
+
+ cpp << endl;
+ }
+ }
+
+ // default value getters always go in Cpp
+ for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
+ QString n = (*itEntry)->name();
+ QString t = (*itEntry)->type();
+
+ // Default value Accessor, as "helper" function
+ if ((cfg.allDefaultGetters || cfg.defaultGetters.contains(n)) && !(*itEntry)->defaultValue().isEmpty()) {
+ cpp << cppType(t) << " " << getDefaultFunction(n, cfg.className) << "_helper(";
+ if (!(*itEntry)->param().isEmpty()) {
+ cpp << " " << cppType((*itEntry)->paramType()) << " i ";
+ }
+ cpp << ")" << Const << endl;
+ cpp << "{" << endl;
+ cpp << memberGetDefaultBody(*itEntry) << endl;
+ cpp << "}" << endl << endl;
+ }
+ }
+
+ // Destructor
+ cpp << cfg.className << "::~" << cfg.className << "()" << endl;
+ cpp << "{" << endl;
+ if (cfg.dpointer) {
+ cpp << " delete d;" << endl;
+ }
+ if (cfg.singleton) {
+ cpp << " s_global" << cfg.className << "()->q = nullptr;" << endl;
+ }
+ cpp << "}" << endl << endl;
+
+ if (hasNonModifySignals) {
+ cpp << "bool " << cfg.className << "::" << "usrSave()" << endl;
+ cpp << "{" << endl;
+ cpp << " const bool res = " << cfg.inherits << "::usrSave();" << endl;
+ cpp << " if (!res) return false;" << endl << endl;
+ for (const Signal &signal : qAsConst(signalList)) {
+ if (signal.modify) {
+ continue;
+ }
+
+ cpp << " if ( " << varPath(QStringLiteral("settingsChanged"), cfg) << " & " << signalEnumName(signal.name) << " )" << endl;
+ cpp << " Q_EMIT " << signal.name << "(";
+ QList<SignalArguments>::ConstIterator it, itEnd = signal.arguments.constEnd();
+ for (it = signal.arguments.constBegin(); it != itEnd;) {
+ SignalArguments argument = *it;
+ bool cast = false;
+ if (cfg.useEnumTypes && argument.type == QLatin1String("Enum")) {
+ for (int i = 0, end = entries.count(); i < end; ++i) {
+ if (entries[i]->name() == argument.variableName) {
+ cpp << "static_cast<" << enumType(entries[i], cfg.globalEnums) << ">(";
+ cast = true;
+ break;
+ }
+ }
+ }
+ cpp << varPath(argument.variableName, cfg);
+ if (cast) {
+ cpp << ")";
+ }
+ if (++it != itEnd) {
+ cpp << ", ";
+ }
+ }
+ cpp << ");" << endl;
+ }
+
+ cpp << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " = 0;" << endl;
+ cpp << " return true;" << endl;
+ cpp << "}" << endl;
+ }
+
+ if (hasSignals) {
+ cpp << endl;
+ cpp << "void " << cfg.className << "::" << "itemChanged(quint64 flags) {" << endl;
+ if (hasNonModifySignals)
+ cpp << " " << varPath(QStringLiteral("settingsChanged"), cfg) << " |= flags;" << endl;
+
+ if (!signalList.isEmpty())
+ cpp << endl;
+
+ for (const Signal &signal : qAsConst(signalList)) {
+ if (signal.modify) {
+ cpp << " if ( flags & " << signalEnumName(signal.name) << " ) {" << endl;
+ cpp << " Q_EMIT " << signal.name << "();" << endl;
+ cpp << " }" << endl;
+ }
+ }
+
+ cpp << "}" << endl;
+ }
+
+ if (hasSignals || cfg.generateProperties) {
+ // Add includemoc if they are signals defined.
+ cpp << endl;
+ cpp << "#include \"" << mocFileName << "\"" << endl;
+ cpp << endl;
+ }
- KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult);
- sourceGenerator.start();
- sourceGenerator.save();
+ // clear entries list
+ qDeleteAll(entries);
- qDeleteAll(parseResult.entries);
+ implementation.close();
}