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]->setValueForChoice(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]->setValueForChoice(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]->setValueForChoice(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/autotests/kconfig_compiler/test5.cpp.ref b/autotests/kconfig_compiler/test5.cpp.ref --- a/autotests/kconfig_compiler/test5.cpp.ref +++ b/autotests/kconfig_compiler/test5.cpp.ref @@ -69,10 +69,13 @@ } KConfigSkeleton::ItemEnum *itemMouseAction[3]; itemMouseAction[0] = new KConfigSkeleton::ItemEnum( currentGroup(), QStringLiteral( "right_mouse_action" ), mMouseAction[0], valuesMouseAction, Decrypt ); + itemMouseAction[0]->setValueForChoice(QStringLiteral( "CrashNBurn" ), QStringLiteral( "Crash'n Burn" )); addItem( itemMouseAction[0], QStringLiteral( "MouseActionright" ) ); itemMouseAction[1] = new KConfigSkeleton::ItemEnum( currentGroup(), QStringLiteral( "mid_mouse_action" ), mMouseAction[1], valuesMouseAction, Encrypt ); + itemMouseAction[1]->setValueForChoice(QStringLiteral( "CrashNBurn" ), QStringLiteral( "Crash'n Burn" )); addItem( itemMouseAction[1], QStringLiteral( "MouseActionmid" ) ); itemMouseAction[2] = new KConfigSkeleton::ItemEnum( currentGroup(), QStringLiteral( "left_mouse_action" ), mMouseAction[2], valuesMouseAction, PumpNDump ); + itemMouseAction[2]->setValueForChoice(QStringLiteral( "CrashNBurn" ), QStringLiteral( "Crash'n Burn" )); addItem( itemMouseAction[2], QStringLiteral( "MouseActionleft" ) ); KConfigSkeleton::ItemString *itemFooBar; itemFooBar = new KConfigSkeleton::ItemString( currentGroup(), QStringLiteral( "foo bar" ), mFooBar ); diff --git a/autotests/kconfig_compiler/test5.kcfg b/autotests/kconfig_compiler/test5.kcfg --- a/autotests/kconfig_compiler/test5.kcfg +++ b/autotests/kconfig_compiler/test5.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 setValueForChoice(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::setValueForChoice(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,7 @@ QString label; QString toolTip; QString whatsThis; + QString val; }; class Choices { @@ -104,6 +105,7 @@ QString paramName; QString paramType; Choices choices; + QHash enumValues; QList signalList; QStringList paramValues; QStringList paramDefaultValues; 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 @@ -313,25 +313,30 @@ void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key) { - stream() << " " << itemPath(entry, cfg()) << " = " + const QString itemVarStr = itemPath(entry, cfg()); + stream() << " " << itemVarStr << " = " << newItem(entry, key, entry->defaultValue, cfg()) << '\n'; if (!entry->min.isEmpty()) { - stream() << " " << itemPath(entry, cfg()) << "->setMinValue(" << entry->min << ");\n"; + stream() << " " << itemVarStr << "->setMinValue(" << entry->min << ");\n"; } if (!entry->max.isEmpty()) { - stream() << " " << itemPath(entry, cfg()) << "->setMaxValue(" << entry->max << ");\n"; + stream() << " " << itemVarStr << "->setMaxValue(" << entry->max << ");\n"; } if (cfg().setUserTexts) { stream() << userTextsFunctions(entry, cfg()); } if (cfg().allNotifiers || cfg().notifiers.contains(entry->name)) { - stream() << " " << itemPath(entry, cfg()) << "->setWriteFlags(KConfigBase::Notify);\n"; + stream() << " " << itemVarStr << "->setWriteFlags(KConfigBase::Notify);\n"; } - stream() << " addItem( " << itemPath(entry, cfg()); + for (QHash::const_iterator it = entry->enumValues.cbegin(), end = entry->enumValues.cend(); it != end; ++it) { + stream() << " " << itemVarStr << "->setValueForChoice(QStringLiteral( \"" << it.key() << "\" ), QStringLiteral( \"" << it.value() << "\" ));\n"; + } + + stream() << " addItem( " << itemVarStr; QString quotedName = entry->name; addQuotes(quotedName); if (quotedName != key) { @@ -352,6 +357,12 @@ stream() << " " << itemVarStr << " = " << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), QStringLiteral("[%1]").arg(i)) << '\n'; + for (QHash::const_iterator it = entry->enumValues.cbegin(), end = entry->enumValues.cend(); it != end; ++it) { + stream() << " " << itemVarStr << "->setValueForChoice(QStringLiteral( \"" << it.key() << "\" ), QStringLiteral( \"" << it.value() << "\" ));\n"; + } + + stream() << " // " << entry->enumValues.count() << "\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.enumValues.insert(choice.name, choice.val); + } + } } void KConfigXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element) @@ -418,6 +430,7 @@ result->code = readEntry.code; result->defaultValue = readEntry.defaultValue; result->choices = readEntry.choices; + result->enumValues = readEntry.enumValues; result->signalList = readEntry.signalList; result->hidden = readEntry.hidden; 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 @@ +