/* This file is part of KDE. Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> Copyright (c) 2003 Waldo Bastian <bastian@kde.org> Copyright (c) 2003 Zack Rusin <zack@kde.org> Copyright (c) 2006 MichaĆ«l Larouche <michael.larouche@kdemail.net> Copyright (c) 2008 Allen Winter <winter@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Compiling this file with this flag is just crazy #undef QT_NO_CAST_FROM_ASCII #include <QCoreApplication> #include <QFile> #include <QFileInfo> #include <QSettings> #include <QTextStream> #include <QDomAttr> #include <QRegExp> #include <QStringList> #include <qcommandlineparser.h> #include <qcommandlineoption.h> #include <ostream> #include <iostream> #include <stdlib.h> #include "../../kconfig_version.h" namespace { QTextStream cout(stdout); QTextStream cerr(stderr); } QStringList allNames; QRegExp *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) { result = QChar::fromLatin1('m') + n; result[1] = result[1].toUpper(); } else { result = n; result[0] = result[0].toLower(); } return result; } static QString varPath(const QString &n, const CfgConfig &cfg) { QString result; if (cfg.dpointer) { result = "d->" + varName(n, cfg); } else { result = varName(n, cfg); } return result; } static QString enumName(const QString &n) { QString result = QLatin1String("Enum") + n; result[4] = result[4].toUpper(); return result; } static QString enumName(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + n; result[4] = result[4].toUpper(); } return result; } static 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[4].toUpper(); } return result; } static QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + n + QLatin1String("::"); result[4] = result[4].toUpper(); } else if (c.external()) { result = c.externalQualifier(); } else { result.clear(); } return result; } static QString setFunction(const QString &n, const QString &className = QString()) { QString result = QLatin1String("set") + n; result[3] = result[3].toUpper(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } static QString changeSignalName(const QString &n) { return n+QStringLiteral("Changed"); } static QString getDefaultFunction(const QString &n, const QString &className = QString()) { QString result = QLatin1String("default") + n + QLatin1String("Value"); result[7] = result[7].toUpper(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } static QString getFunction(const QString &n, const QString &className = QString()) { QString result = n; result[0] = result[0].toLower(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } static 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('\"'); } static QString literalString(const QString &s) { bool isAscii = true; for (int i = s.length(); i--;) if (s[i].unicode() > 127) { isAscii = false; } if (isAscii) { return QLatin1String("QStringLiteral( ") + quoteString(s) + QLatin1String(" )"); } else { return QLatin1String("QString::fromUtf8( ") + quoteString(s) + QLatin1String(" )"); } } 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; } static QString filenameOnly(const QString &path) { int i = path.lastIndexOf(QRegExp(QStringLiteral("[/\\]"))); if (i >= 0) { return path.mid(i + 1); } return path; } static QString signalEnumName(const QString &signalName) { QString result; result = QLatin1String("signal") + signalName; result[6] = result[6].toUpper(); 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; } 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()) { QRegExp colorRe(QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?")); if (colorRe.exactMatch(defaultValue)) { 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->exactMatch(name)) { 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; } if (type == QLatin1String("ULongLong")) { return true; } return false; } /** 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("size")) { return QStringLiteral("const QSize &"); } else if (type == QLatin1String("color")) { return QStringLiteral("const QColor &"); } else if (type == QLatin1String("point")) { return QStringLiteral("const QPoint &"); } 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 { cerr << "kconfig_compiler_kf5 does not support type \"" << type << "\"" << 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("size")) { return QStringLiteral("QSize"); } else if (type == QLatin1String("color")) { return QStringLiteral("QColor"); } else if (type == QLatin1String("point")) { return QStringLiteral("QPoint"); } 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 { cerr << "kconfig_compiler_kf5 does not support type \"" << type << "\"" << 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("size")) { return QStringLiteral("QSize()"); } else if (type == QLatin1String("color")) { return QStringLiteral("QColor(128, 128, 128)"); } else if (type == QLatin1String("point")) { return QStringLiteral("QPoint()"); } 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 { cerr << "Error, kconfig_compiler_kf5 does not support the \"" << type << "\" type!" << endl; return QStringLiteral("QString"); //For now, but an assert would be better } } QString itemType(const QString &type) { QString t; t = type; t.replace(0, 1, t.left(1).toUpper()); return t; } static QString itemDeclaration(const CfgEntry *e, const CfgConfig &cfg) { if (cfg.itemAccessors) { return QString(); } QString type; if (!e->signalList().isEmpty()) { type = QStringLiteral("KConfigCompilerSignallingItem"); } else { type = cfg.inherits + "::Item" + itemType(e->type()); } QString fCap = e->name(); fCap[0] = fCap[0].toUpper(); return " " + type + " *item" + fCap + ( (!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 static QString itemVar(const CfgEntry *e, const CfgConfig &cfg) { QString result; if (cfg.itemAccessors) { if (!cfg.dpointer) { result = 'm' + e->name() + "Item"; result[1] = result[1].toUpper(); } else { result = e->name() + "Item"; result[0] = result[0].toLower(); } } else { result = "item" + e->name(); result[4] = result[4].toUpper(); } return result; } static QString itemPath(const CfgEntry *e, const CfgConfig &cfg) { QString result; if (cfg.dpointer) { result = "d->" + itemVar(e, cfg); } else { result = itemVar(e, cfg); } return result; } QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue, const CfgConfig &cfg, const QString ¶m = QString()) { 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; if (entry->type() == QLatin1String("Enum")) { t += ", values" + entry->name(); } if (!defaultValue.isEmpty()) { t += QLatin1String(", ") + defaultValue; } t += QLatin1String(" )"); if (!sigs.isEmpty()) { t += QLatin1String(", this, notifyFunction, "); //append the signal flags for (int i = 0; i < sigs.size(); ++i) { if (i != 0) t += QLatin1String(" | "); t += signalEnumName(sigs[i].name); } t += QLatin1String(")"); } t += QLatin1String(";"); return t; } QString paramString(const QString &s, const CfgEntry *e, int i) { QString result = s; QString needle = "$(" + e->param() + ')'; if (result.contains(needle)) { QString tmp; if (e->paramType() == QLatin1String("Enum")) { tmp = e->paramValues().at(i); } else { tmp = 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; for (QList<Param>::ConstIterator it = parameters.constBegin(); it != parameters.constEnd(); ++it) { if (paramString.contains("$(" + (*it).name + ')')) { const QString tmp = QStringLiteral("%%1").arg(i++); paramString.replace("$(" + (*it).name + ')', tmp); arguments += ".arg( mParam" + (*it).name + " )"; } } if (arguments.isEmpty()) { return "QStringLiteral( \"" + group + "\" )"; } return "QStringLiteral( \"" + paramString + "\" )" + arguments; } QString translatedString(const CfgConfig &cfg, const QString &string, const QString &context = QString(), const QString ¶m = QString(), const QString ¶mValue = QString()) { QString result; switch (cfg.translationSystem) { case CfgConfig::QtTranslation: if (!context.isEmpty()) { result += "/*: " + context + " */ QCoreApplication::translate(\""; } else { result += QLatin1String("QCoreApplication::translate(\""); } result += cfg.className + "\", "; break; case CfgConfig::KdeTranslation: if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) { result += "i18ndc(" + quoteString(cfg.translationDomain) + ", " + quoteString(context) + ", "; } else if (!cfg.translationDomain.isEmpty()) { result += "i18nd(" + quoteString(cfg.translationDomain) + ", "; } else if (!context.isEmpty()) { result += "i18nc(" + quoteString(context) + ", "; } else { result += QLatin1String("i18n("); } break; } if (!param.isEmpty()) { QString resolvedString = string; resolvedString.replace("$(" + param + ')', paramValue); result += quoteString(resolvedString); } else { result += quoteString(string); } result += ')'; return result; } /* int i is the value of the parameter */ 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()) { txt += " " + itemVarStr + "->setLabel( "; txt += translatedString(cfg, e->label(), e->labelContext(), e->param(), i); txt += QLatin1String(" );\n"); } if (!e->toolTip().isEmpty()) { txt += " " + itemVarStr + "->setToolTip( "; txt += translatedString(cfg, e->toolTip(), e->toolTipContext(), e->param(), i); txt += QLatin1String(" );\n"); } if (!e->whatsThis().isEmpty()) { txt += " " + itemVarStr + "->setWhatsThis( "; 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 void addDebugMethod(QTextStream &out, const CfgConfig &cfg, const QString &n) { if (cfg.qCategoryLoggingName.isEmpty()) { out << " qDebug() << \"" << setFunction(n); } else { out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n); } } 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(CfgEntry *e) { QString result = e->code(); QTextStream out(&result, QIODevice::WriteOnly); out << endl; if (!e->param().isEmpty()) { out << " switch (i) {" << endl; for (int i = 0; i <= e->paramMax(); ++i) { if (!e->paramDefaultValue(i).isEmpty()) { out << " case " << i << ": return " << e->paramDefaultValue(i) << ';' << endl; } } out << " default:" << endl; out << " return " << e->defaultValue().replace("$(" + e->param() + ')', QLatin1String("i")) << ';' << endl; out << " }" << endl; } else { out << " return " << e->defaultValue() << ';'; } return result; } // returns the item accesor implementation // which should go in the h file if inline // or the cpp file if not inline QString itemAccessorBody(CfgEntry *e, const CfgConfig &cfg) { QString result; QTextStream out(&result, QIODevice::WriteOnly); out << "return " << itemPath(e, cfg); if (!e->param().isEmpty()) { out << "[i]"; } out << ";" << endl; 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 << endl; } return result; } // 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 (!p_ns.isEmpty()) { const QStringList nameSpaces = p_ns.split(QStringLiteral("::")); for (const QString &ns : nameSpaces) { p_out << "namespace " << ns << " {" << endl; } p_out << endl; } } // 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; } } int main(int argc, char **argv) { QCoreApplication app(argc, argv); app.setApplicationName(QStringLiteral("kconfig_compiler")); app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING)); validNameRegexp = new QRegExp(QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*")); 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(); parser.addHelpOption(); parser.process(app); if (parser.isSet(licenseOption)) { 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; cout << "under the terms of the GNU Library Public License." << endl; cout << "For more information about these matters, see the file named COPYING." << endl; return 0; } const QStringList args = parser.positionalArguments(); if (args.count() < 2) { cerr << "Too few arguments." << endl; return 1; } if (args.count() > 2) { cerr << "Too many arguments." << endl; return 1; } inputFilename = args.at(0); codegenFilename = args.at(1); QString baseDir = parser.value(targetDirectoryOption); #ifdef Q_OS_WIN if (!baseDir.endsWith('/') && !baseDir.endsWith('\\')) #else if (!baseDir.endsWith('/')) #endif baseDir.append("/"); 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); QFile input(inputFilename); 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; } QDomElement cfgElement = doc.documentElement(); if (cfgElement.isNull()) { cerr << "No document in kcfg file" << endl; return 1; } 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; } 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" << hex << val; if (++it != itEnd) { h << ","; } h << endl; } h << " };" << dec << 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 \"" << filenameOnly(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++ // http://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; } // clear entries list qDeleteAll(entries); implementation.close(); }