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 @@ -802,9 +802,20 @@ 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(const QString &name) const; + + /** + * Stores a choice value for name + */ + void setValueForChoice(const QString &name, const QString &valueForChoice); + private: QList mChoices; }; diff --git a/src/core/kcoreconfigskeleton.cpp b/src/core/kcoreconfigskeleton.cpp --- a/src/core/kcoreconfigskeleton.cpp +++ b/src/core/kcoreconfigskeleton.cpp @@ -590,6 +590,19 @@ mMax = v; } +QString KCoreConfigSkeleton::ItemEnum::valueForChoice(const QString &name) const +{ + // HACK for BC concerns + // TODO KF6: remove KConfigSkeletonItemPrivate::mValues and add a value field to KCoreConfigSkeleton::ItemEnum::Choice + const auto inHash = d_ptr->mValues.value(name); + return !inHash.isEmpty() ? inHash : name; +} + +void KCoreConfigSkeleton::ItemEnum::setValueForChoice(const QString &name, const QString &value) +{ + d_ptr->mValues.insert(name, value); +} + KCoreConfigSkeleton::ItemEnum::ItemEnum(const QString &_group, const QString &_key, qint32 &reference, const QList &choices, @@ -609,7 +622,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; } @@ -630,7 +644,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/core/kcoreconfigskeleton_p.h b/src/core/kcoreconfigskeleton_p.h --- a/src/core/kcoreconfigskeleton_p.h +++ b/src/core/kcoreconfigskeleton_p.h @@ -62,6 +62,7 @@ QString mToolTip; ///< The ToolTip text for this item QString mWhatsThis; ///< The What's This text for this item KConfigGroup mConfigGroup; ///< KConfigGroup, allow to read/write item in nested groups + QHash mValues; /// The values used for ItemEnum's choices, name -> value (if set) // HACK: Necessary to avoid introducing new virtuals in KConfigSkeletonItem std::function mIsDefaultImpl; 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() ? val : name; + } }; class Choices { 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,13 +313,14 @@ void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key) { + const QString itemVarStr = itemPath(entry, cfg()); const QString innerItemVarStr = innerItemVar(entry, cfg()); if (!entry->signalList.isEmpty()) { stream() << " " << innerItemVarStr << " = " << newInnerItem(entry, key, entry->defaultValue, cfg()) << '\n'; } - stream() << " " << itemPath(entry, cfg()) << " = " + stream() << " " << itemVarStr << " = " << newItem(entry, key, entry->defaultValue, cfg()) << '\n'; if (!entry->min.isEmpty()) { @@ -335,10 +336,16 @@ } 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 (const CfgEntry::Choice &choice : qAsConst(entry->choices.choices)) { + if (!choice.val.isEmpty()) { + stream() << " " << itemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val << "\" ));\n"; + } + } + + stream() << " addItem( " << itemVarStr; QString quotedName = entry->name; addQuotes(quotedName); if (quotedName != key) { @@ -374,6 +381,12 @@ stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n"; } + for (const CfgEntry::Choice &choice : qAsConst(entry->choices.choices)) { + if (!choice.val.isEmpty()) { + stream() << " " << itemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val << "\" ));\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 auto choiceNameRegex = QRegularExpression(QStringLiteral("\\w+")); + for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() != QLatin1String("choice")) { continue; } CfgEntry::Choice choice; choice.name = e2.attribute(QStringLiteral("name")); if (choice.name.isEmpty()) { std::cerr << "Tag 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(); 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 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 @@ +