diff --git a/autotests/kconfig_compiler/test4.cpp.ref b/autotests/kconfig_compiler/test4.cpp.ref --- a/autotests/kconfig_compiler/test4.cpp.ref +++ b/autotests/kconfig_compiler/test4.cpp.ref @@ -68,10 +68,13 @@ } KConfigSkeleton::ItemEnum *itemMouseAction[3]; itemMouseAction[0] = new KConfigSkeleton::ItemEnum( currentGroup(), QStringLiteral( "right_mouse_action" ), mMouseAction[0], valuesMouseAction, EnumMouseAction::Decrypt ); + itemMouseAction[0]->addValue(QStringLiteral( "CrashNBurn" ), QStringLiteral( "Crash and Burn" )); addItem( itemMouseAction[0], QStringLiteral( "MouseActionright" ) ); itemMouseAction[1] = new KConfigSkeleton::ItemEnum( currentGroup(), QStringLiteral( "mid_mouse_action" ), mMouseAction[1], valuesMouseAction, EnumMouseAction::Encrypt ); + itemMouseAction[1]->addValue(QStringLiteral( "CrashNBurn" ), QStringLiteral( "Crash and Burn" )); addItem( itemMouseAction[1], QStringLiteral( "MouseActionmid" ) ); itemMouseAction[2] = new KConfigSkeleton::ItemEnum( currentGroup(), QStringLiteral( "left_mouse_action" ), mMouseAction[2], valuesMouseAction, EnumMouseAction::PumpNDump ); + itemMouseAction[2]->addValue(QStringLiteral( "CrashNBurn" ), QStringLiteral( "Crash and Burn" )); addItem( itemMouseAction[2], QStringLiteral( "MouseActionleft" ) ); KConfigSkeleton::ItemColor *itemGrayColor[11]; itemGrayColor[0] = new KConfigSkeleton::ItemColor( currentGroup(), QStringLiteral( "gray color #0" ), mGrayColor[0], diff --git a/autotests/kconfig_compiler/test4.kcfg b/autotests/kconfig_compiler/test4.kcfg --- a/autotests/kconfig_compiler/test4.kcfg +++ b/autotests/kconfig_compiler/test4.kcfg @@ -24,7 +24,7 @@ - + Decrypt diff --git a/src/core/kcoreconfigskeleton.h b/src/core/kcoreconfigskeleton.h --- a/src/core/kcoreconfigskeleton.h +++ b/src/core/kcoreconfigskeleton.h @@ -778,11 +778,23 @@ void writeConfig(KConfig *config) override; // Source compatibility with 4.x + // TODO KF6 remove typedef Choice Choice2; QList choices2() const; + /** + * Returns the value for for the choice with the given name + */ + QString valueForChoice(QString name) const; + + /** + * Stores a choice value for name + */ + void addValue(QString name, QString valueForChoice); + private: QList mChoices; + QHash mValues; }; /** diff --git a/src/core/kcoreconfigskeleton.cpp b/src/core/kcoreconfigskeleton.cpp --- a/src/core/kcoreconfigskeleton.cpp +++ b/src/core/kcoreconfigskeleton.cpp @@ -575,6 +575,21 @@ mMax = v; } +QString KCoreConfigSkeleton::ItemEnum::valueForChoice(QString name) const +{ + // TODO KF6 move value to ItemEnum::Choice and remove KCoreConfigSkeleton::ItemEnum::mValues + const auto inHash = mValues.value(name); + return !inHash.isEmpty() ? inHash : name; +} + +/** + * Stores a choice value for name + */ +void KCoreConfigSkeleton::ItemEnum::addValue(QString name, QString value) +{ + mValues.insert(name, value); +} + KCoreConfigSkeleton::ItemEnum::ItemEnum(const QString &_group, const QString &_key, qint32 &reference, const QList &choices, @@ -594,7 +609,8 @@ QString tmp = cg.readEntry(mKey, QString()).toLower(); for (QList::ConstIterator it = mChoices.constBegin(); it != mChoices.constEnd(); ++it, ++i) { - if ((*it).name.toLower() == tmp) { + QString choiceName = (*it).name; + if (valueForChoice(choiceName).toLower() == tmp) { mReference = i; break; } @@ -615,7 +631,7 @@ if ((mDefault == mReference) && !cg.hasDefault(mKey)) { cg.revertToDefault(mKey, writeFlags()); } else if ((mReference >= 0) && (mReference < mChoices.count())) { - cg.writeEntry(mKey, mChoices[mReference].name, writeFlags()); + cg.writeEntry(mKey, valueForChoice(mChoices.at(mReference).name), writeFlags()); } else { cg.writeEntry(mKey, mReference, writeFlags()); } diff --git a/src/kconfig_compiler/KConfigCommonStructs.h b/src/kconfig_compiler/KConfigCommonStructs.h --- a/src/kconfig_compiler/KConfigCommonStructs.h +++ b/src/kconfig_compiler/KConfigCommonStructs.h @@ -55,6 +55,12 @@ QString label; QString toolTip; QString whatsThis; + QString val; + + QString value() const + { + return val.isEmpty() ? name : val; + } }; class Choices { @@ -106,6 +112,7 @@ Choices choices; QList signalList; QStringList paramValues; + QHash paramEnumValues; QStringList paramDefaultValues; int paramMax; bool hidden; diff --git a/src/kconfig_compiler/KConfigSourceGenerator.cpp b/src/kconfig_compiler/KConfigSourceGenerator.cpp --- a/src/kconfig_compiler/KConfigSourceGenerator.cpp +++ b/src/kconfig_compiler/KConfigSourceGenerator.cpp @@ -352,6 +352,10 @@ stream() << " " << itemVarStr << " = " << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), QStringLiteral("[%1]").arg(i)) << '\n'; + for (QHash::const_iterator it = entry->paramEnumValues.cbegin(), end = entry->paramEnumValues.cend(); it != end; ++it) { + stream() << " " << itemVarStr << "->addValue(QStringLiteral( \"" << it.key() << "\" ), QStringLiteral( \"" << it.value() << "\" ));\n"; + } + if (cfg().setUserTexts) { stream() << userTextsFunctions(entry, cfg(), itemVarStr, entry->paramName); } diff --git a/src/kconfig_compiler/KConfigXmlParser.cpp b/src/kconfig_compiler/KConfigXmlParser.cpp --- a/src/kconfig_compiler/KConfigXmlParser.cpp +++ b/src/kconfig_compiler/KConfigXmlParser.cpp @@ -176,6 +176,7 @@ bool KConfigXmlParser::hasDefaultCode(CfgEntry &readEntry, const QDomElement &element) { + Q_UNUSED(readEntry) for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { if (e.attribute(QStringLiteral("param")).isEmpty()) { if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) { @@ -186,19 +187,23 @@ return false; } - void KConfigXmlParser::readChoicesFromEntry(CfgEntry &readEntry, const QDomElement &e) { QList chlist; + const QRegularExpression choiceNameRegex = QRegularExpression(QStringLiteral("\\w+")); + for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() != QLatin1String("choice")) { continue; } CfgEntry::Choice choice; choice.name = e2.attribute(QStringLiteral("name")); if (choice.name.isEmpty()) { std::cerr << "Tag requires attribute 'name'." << std::endl; + } else if (!choiceNameRegex.match(choice.name).hasMatch()) { + std::cerr << "Tag attribute 'name' must be compatible with Enum naming. name was '" << qPrintable(choice.name) << "'. You can use attribute 'value' to pass any string as the choice value." << std::endl; } + choice.val = e2.attribute(QStringLiteral("value")); for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { if (e3.tagName() == QLatin1String("label")) { choice.label = e3.text(); @@ -220,6 +225,13 @@ QString prefix = e.attribute(QStringLiteral("prefix")); readEntry.choices = CfgEntry::Choices(chlist, name, prefix); + + // handle set custom "value" field + for (const CfgEntry::Choice &choice : chlist) { + if (!choice.val.isEmpty()) { + readEntry.paramEnumValues.insert(choice.name, choice.val); + } + } } void KConfigXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element) @@ -427,6 +439,7 @@ result->paramType = readEntry.paramType; result->paramValues = readEntry.paramValues; result->paramDefaultValues = readEntry.paramDefaultValues; + result->paramEnumValues = readEntry.paramEnumValues; result->paramMax = readEntry.paramMax; } result->min = readEntry.min; diff --git a/src/kconfig_compiler/README.dox b/src/kconfig_compiler/README.dox --- a/src/kconfig_compiler/README.dox +++ b/src/kconfig_compiler/README.dox @@ -354,7 +354,7 @@ - + \endverbatim @@ -369,6 +369,9 @@ }; \endverbatim +Since 5.68, if present the value attribute will be used used as the choice value written to the backend +instead of the name, allowing to write text incompatible with enum naming. + Alternatively, if GlobalEnums is set to true, all Enum items are defined as unnamed enums in the global scope of the generated class. In this case, all Enum values must have different names to avoid clashes. However, you can use a 'prefix' argument @@ -380,7 +383,7 @@ - + \endverbatim diff --git a/src/kconfig_compiler/kcfg.xsd b/src/kconfig_compiler/kcfg.xsd --- a/src/kconfig_compiler/kcfg.xsd +++ b/src/kconfig_compiler/kcfg.xsd @@ -93,6 +93,7 @@ +