Generate Q_PROPERTY entries out of KConfigSkeleton classes

It adds a configuration setting that makes it possible to generate
Q_PROPERTY instances out of each variable exposed by the configuration
class.
Especially useful when it comes to exposing these classes to QtQuick
interfaces.

REVIEW: 123367

CHANGELOG: Generate QML-proof classes using the kconfigcompiler
wilder
Aleix Pol 11 years ago
parent 611ab289b1
commit 97552ff2ec
  1. 11
      autotests/kconfig_compiler/CMakeLists.txt
  2. 2
      autotests/kconfig_compiler/kconfigcompiler_test.cpp
  3. 34
      autotests/kconfig_compiler/test13.cpp.ref
  4. 70
      autotests/kconfig_compiler/test13.h.ref
  5. 11
      autotests/kconfig_compiler/test13.kcfg
  6. 3
      autotests/kconfig_compiler/test13.kcfgc
  7. 28
      autotests/kconfig_compiler/test13main.cpp
  8. 11
      autotests/kconfig_compiler/test_signal.cpp.ref
  9. 18
      autotests/kconfig_compiler/test_signal.h.ref
  10. 162
      src/kconfig_compiler/kconfig_compiler.cpp

@ -190,6 +190,17 @@ ecm_mark_as_test(test12)
target_link_libraries(test12 KF5::ConfigGui)
########### next target ###############
set(test13_SRCS test13main.cpp )
gen_kcfg_test_source(test13 test13_SRCS)
add_executable(test13 ${test13_SRCS})
ecm_mark_as_test(test13)
target_link_libraries(test13 KF5::ConfigGui)
########### next target ###############
set(test_dpointer_SRCS test_dpointer_main.cpp )

@ -43,6 +43,7 @@ static CompilerTestSet testCases = {
"test11.h", "test11.cpp",
"test11a.h", "test11a.cpp",
"test12.h", "test12.cpp",
"test13.h", "test13.cpp",
"test_dpointer.cpp", "test_dpointer.h",
"test_signal.cpp", "test_signal.h",
"test_qdebugcategory.cpp", "test_qdebugcategory.h",
@ -63,6 +64,7 @@ static CompilerTestSet testCasesToRun = {
"test10",
"test11",
"test12",
"test13",
"test_dpointer",
"test_signal",
"test_qdebugcategory",

@ -0,0 +1,34 @@
// This file is generated by kconfig_compiler_kf5 from test13.kcfg.
// All changes you do to this file will be lost.
#include "test13.h"
Test13::Test13( )
: KConfigSkeleton( QLatin1String( "muondatasourcesrc" ) )
{
KConfigCompilerSignallingItem::NotifyFunction notifyFunction = static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&Test13::itemChanged);
setCurrentGroup( QLatin1String( "kamoso" ) );
KConfigSkeleton::ItemUrl *itemPicturesDir;
itemPicturesDir = new KConfigSkeleton::ItemUrl( currentGroup(), QLatin1String( "picturesDir" ), mPicturesDir );
addItem( itemPicturesDir, QLatin1String( "picturesDir" ) );
KConfigCompilerSignallingItem *itemBrightness;
itemBrightness = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemDouble( currentGroup(), QLatin1String( "brightness" ), mBrightness ), this, notifyFunction, signalBrightnessChanged);
addItem( itemBrightness, QLatin1String( "brightness" ) );
}
Test13::~Test13()
{
}
void Test13::itemChanged(quint64 flags) {
if ( flags & signalBrightnessChanged ) {
Q_EMIT brightnessChanged();
}
}
#include "test13.moc"

@ -0,0 +1,70 @@
// This file is generated by kconfig_compiler_kf5 from test13.kcfg.
// All changes you do to this file will be lost.
#ifndef TEST13_H
#define TEST13_H
#include <qglobal.h>
#include <kconfigskeleton.h>
#include <QCoreApplication>
#include <QDebug>
class Test13 : public KConfigSkeleton
{
Q_OBJECT
public:
Test13( );
~Test13();
Q_PROPERTY(QUrl picturesDir READ picturesDir CONSTANT)
/**
Get picturesDir
*/
QUrl picturesDir() const
{
return mPicturesDir;
}
/**
Set brightness
*/
void setBrightness( double v )
{
if (v != mBrightness && !isImmutable( QString::fromLatin1( "brightness" ) )) {
mBrightness = v;
Q_EMIT brightnessChanged();
}
}
Q_PROPERTY(double brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged)
/**
Get brightness
*/
double brightness() const
{
return mBrightness;
}
enum {
signalBrightnessChanged = 0x1
};
Q_SIGNALS:
void brightnessChanged();
private:
void itemChanged(quint64 flags);
protected:
// kamoso
QUrl mPicturesDir;
double mBrightness;
private:
};
#endif

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="muondatasourcesrc"/>
<group name="kamoso">
<entry name="picturesDir" type="Url" />
<entry name="brightness" type="double" />
</group>
</kcfg>

@ -0,0 +1,3 @@
ClassName=Test13
GenerateProperties=true
Mutators=brightness

@ -0,0 +1,28 @@
/*
Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "test13.h"
int main(int, char **)
{
Test13 *t = new Test13();
delete t;
return 0;
}

@ -61,18 +61,17 @@ bool TestSignal::usrWriteConfig()
const bool res = KConfigSkeleton::usrWriteConfig();
if (!res) return false;
if ( mSettingsChanged & signalEmoticonSettingsChanged )
emit emoticonSettingsChanged();
if ( mSettingsChanged & signalStyleChanged )
emit styleChanged(mStylePath, mStyleCSSVariant);
if ( mSettingsChanged & signalEmoticonSettingsChanged )
Q_EMIT emoticonSettingsChanged();
if ( mSettingsChanged & signalStyleChanged )
Q_EMIT styleChanged(mStylePath, mStyleCSSVariant);
mSettingsChanged = 0;
return true;
}
void TestSignal::itemChanged(quint64 flags) {
mSettingsChanged |= flags;
}
#include "test_signal.moc"

@ -12,11 +12,6 @@ class TestSignal : public KConfigSkeleton
Q_OBJECT
public:
enum {
signalEmoticonSettingsChanged = 0x1,
signalStyleChanged = 0x2
};
static TestSignal *self();
~TestSignal();
@ -26,7 +21,7 @@ class TestSignal : public KConfigSkeleton
static
void setEmoticonTheme( const QString & v )
{
if (!self()->isImmutable( QString::fromLatin1( "emoticonTheme" ) )) {
if (v != self()->mEmoticonTheme && !self()->isImmutable( QString::fromLatin1( "emoticonTheme" ) )) {
self()->mEmoticonTheme = v;
self()->mSettingsChanged |= signalEmoticonSettingsChanged;
}
@ -47,7 +42,7 @@ class TestSignal : public KConfigSkeleton
static
void setUseEmoticon( bool v )
{
if (!self()->isImmutable( QString::fromLatin1( "useEmoticon" ) )) {
if (v != self()->mUseEmoticon && !self()->isImmutable( QString::fromLatin1( "useEmoticon" ) )) {
self()->mUseEmoticon = v;
self()->mSettingsChanged |= signalEmoticonSettingsChanged;
}
@ -68,7 +63,7 @@ class TestSignal : public KConfigSkeleton
static
void setEmoticonRequireSpace( bool v )
{
if (!self()->isImmutable( QString::fromLatin1( "emoticonRequireSpace" ) )) {
if (v != self()->mEmoticonRequireSpace && !self()->isImmutable( QString::fromLatin1( "emoticonRequireSpace" ) )) {
self()->mEmoticonRequireSpace = v;
self()->mSettingsChanged |= signalEmoticonSettingsChanged;
}
@ -89,7 +84,7 @@ class TestSignal : public KConfigSkeleton
static
void setStylePath( const QString & v )
{
if (!self()->isImmutable( QString::fromLatin1( "stylePath" ) )) {
if (v != self()->mStylePath && !self()->isImmutable( QString::fromLatin1( "stylePath" ) )) {
self()->mStylePath = v;
self()->mSettingsChanged |= signalStyleChanged;
}
@ -124,6 +119,11 @@ class TestSignal : public KConfigSkeleton
}
enum {
signalEmoticonSettingsChanged = 0x1,
signalStyleChanged = 0x2
};
Q_SIGNALS:
void emoticonSettingsChanged();

@ -97,6 +97,7 @@ public:
globalEnums = codegenConfig.value("GlobalEnums", false).toBool();
useEnumTypes = codegenConfig.value("UseEnumTypes", false).toBool();
const QString trString = codegenConfig.value("TranslationSystem").toString().toLower();
generateProperties = codegenConfig.value("GenerateProperties", false).toBool();
if (trString == "kde") {
translationSystem = KdeTranslation;
} else {
@ -137,6 +138,7 @@ public:
bool useEnumTypes;
bool itemAccessors;
TranslationSystem translationSystem;
bool generateProperties;
};
struct SignalArguments {
@ -147,9 +149,12 @@ struct SignalArguments {
class Signal
{
public:
Signal() : modify(false) {}
QString name;
QString label;
QList<SignalArguments> arguments;
bool modify;
};
class CfgEntry
@ -549,6 +554,11 @@ static QString setFunction(const QString &n, const QString &className = QString(
return result;
}
static QString changeSignalName(const QString &n)
{
return n+QStringLiteral("Changed");
}
static QString getDefaultFunction(const QString &n, const QString &className = QString())
{
QString result = QString::fromLatin1("default") + n + QString::fromLatin1("Value");
@ -844,6 +854,13 @@ CfgEntry *parseEntry(const QString &group, const QDomElement &element, const Cfg
}
}
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;
@ -1404,7 +1421,14 @@ QString memberMutatorBody(CfgEntry *e, const CfgConfig &cfg)
out << "}" << endl << endl;
}
out << "if (!" << This << "isImmutable( QString::fromLatin1( \"";
const QString varExpression = This + varPath(n, cfg) + (e->param().isEmpty() ? QString() : "[i]");
const bool hasBody = !e->signalList().empty() || cfg.generateProperties;
out << "if (";
if (hasBody) {
out << "v != " << varExpression << " && ";
}
out << "!" << This << "isImmutable( QString::fromLatin1( \"";
if (!e->param().isEmpty()) {
out << e->paramName().replace("$(" + e->param() + ")", "%1") << "\" ).arg( ";
if (e->paramType() == "Enum") {
@ -1424,17 +1448,17 @@ QString memberMutatorBody(CfgEntry *e, const CfgConfig &cfg)
} else {
out << n << "\" )";
}
out << " ))" << (!e->signalList().empty() ? " {" : "") << endl;
out << " " << This << varPath(n, cfg);
if (!e->param().isEmpty()) {
out << "[i]";
}
out << " = v;" << endl;
out << " ))" << (hasBody ? " {" : "") << endl;
out << " " << varExpression << " = v;" << endl;
if (!e->signalList().empty()) {
Q_FOREACH (const Signal &signal, e->signalList()) {
Q_FOREACH (const Signal &signal, e->signalList()) {
if (signal.modify) {
out << " Q_EMIT " << This << signal.name << "();" << endl;
} else {
out << " " << This << varPath("settingsChanged", cfg) << " |= " << signalEnumName(signal.name) << ";" << endl;
}
}
if (hasBody) {
out << "}" << endl;
}
@ -1619,7 +1643,6 @@ int main(int argc, char **argv)
QList<Param> parameters;
QList<Signal> signalList;
QStringList includes;
bool hasSignals = false;
QList<CfgEntry *> entries;
@ -1718,7 +1741,6 @@ int main(int argc, char **argv)
}
#endif
hasSignals = !signalList.empty();
QString headerFileName = baseName + ".h";
QString implementationFileName = baseName + ".cpp";
QString mocFileName = baseName + ".moc";
@ -1788,7 +1810,7 @@ int main(int argc, char **argv)
h << "{" << endl;
// Add Q_OBJECT macro if the config need signals.
if (hasSignals) {
if (!signalList.isEmpty() || cfg.generateProperties) {
h << " Q_OBJECT" << endl;
}
h << " public:" << endl;
@ -1842,25 +1864,9 @@ int main(int argc, char **argv)
}
}
}
if (hasSignals) {
h << "\n enum {" << endl;
unsigned val = 1;
QList<Signal>::ConstIterator it, itEnd = signalList.constEnd();
for (it = signalList.constBegin(); it != itEnd; val <<= 1) {
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;
}
h << endl;
// Constructor or singleton accessor
if (!cfg.singleton) {
h << " " << cfg.className << "(";
@ -1930,6 +1936,33 @@ int main(int argc, char **argv)
}
}
h << endl;
QString returnType;
if (cfg.useEnumTypes && t == "Enum") {
returnType = enumType(*itEntry, cfg.globalEnums);
} else {
returnType = cppType(t);
}
if (cfg.generateProperties) {
h << " Q_PROPERTY(" << returnType << ' ' << n;
h << " READ " << 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;
@ -1938,11 +1971,7 @@ int main(int argc, char **argv)
h << " static" << endl;
}
h << " ";
if (cfg.useEnumTypes && t == "Enum") {
h << enumType(*itEntry, cfg.globalEnums);
} else {
h << cppType(t);
}
h << returnType;
h << " " << getFunction(n) << "(";
if (!(*itEntry)->param().isEmpty()) {
h << " " << cppType((*itEntry)->paramType()) << " i ";
@ -2021,8 +2050,27 @@ int main(int argc, char **argv)
}
// Signal definition.
const bool hasSignals = !signalList.isEmpty();
bool hasNonModifySignals = false;
if (hasSignals) {
h << endl;
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:";
Q_FOREACH (const Signal &signal, signalList) {
h << endl;
@ -2070,7 +2118,7 @@ int main(int argc, char **argv)
h << " friend class " << cfg.className << "Helper;" << endl << endl;
}
if (hasSignals) {
if (hasNonModifySignals) {
h << " virtual bool usrWriteConfig();" << endl;
}
@ -2122,7 +2170,7 @@ int main(int argc, char **argv)
h << ";" << endl;
}
}
if (hasSignals) {
if (hasNonModifySignals) {
h << " uint " << varName("settingsChanged", cfg) << ";" << endl;
}
@ -2231,7 +2279,7 @@ int main(int argc, char **argv)
}
cpp << ";" << endl;
}
if (hasSignals) {
if (hasNonModifySignals) {
cpp << " uint " << varName("settingsChanged", cfg) << ";" << endl;
}
@ -2321,7 +2369,7 @@ int main(int argc, char **argv)
cpp << " , mParam" << (*it).name << "(" << (*it).name << ")" << endl;
}
if (hasSignals && !cfg.dpointer) {
if (hasNonModifySignals && !cfg.dpointer) {
cpp << " , " << varName("settingsChanged", cfg) << "(0)" << endl;
}
@ -2329,7 +2377,7 @@ int main(int argc, char **argv)
if (cfg.dpointer) {
cpp << " d = new " + cfg.className + "Private;" << endl;
if (hasSignals) {
if (hasNonModifySignals) {
cpp << " " << varPath("settingsChanged", cfg) << " = 0;" << endl;
}
}
@ -2550,14 +2598,18 @@ int main(int argc, char **argv)
}
cpp << "}" << endl << endl;
if (hasSignals) {
if (hasNonModifySignals) {
cpp << "bool " << cfg.className << "::" << "usrWriteConfig()" << endl;
cpp << "{" << endl;
cpp << " const bool res = " << cfg.inherits << "::usrWriteConfig();" << endl;
cpp << " if (!res) return false;" << endl << endl;
Q_FOREACH (const Signal &signal, signalList) {
cpp << " if ( " << varPath("settingsChanged", cfg) << " & " << signalEnumName(signal.name) << " ) " << endl;
cpp << " emit " << signal.name << "(";
if (signal.modify) {
continue;
}
cpp << " if ( " << varPath("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;
@ -2579,17 +2631,35 @@ int main(int argc, char **argv)
cpp << ", ";
}
}
cpp << ");" << endl << endl;
cpp << ");" << endl;
}
cpp << " " << varPath("settingsChanged", cfg) << " = 0;" << endl;
cpp << " return true;" << endl;
cpp << "}" << endl;
}
if (hasSignals) {
cpp << endl;
cpp << "void " << cfg.className << "::" << "itemChanged(quint64 flags) {" << endl;
cpp << " " << varPath("settingsChanged", cfg) << " |= flags;" << endl;
if (hasNonModifySignals)
cpp << " " << varPath("settingsChanged", cfg) << " |= flags;" << endl;
if (!signalList.isEmpty())
cpp << endl;
Q_FOREACH (const Signal &signal, 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;

Loading…
Cancel
Save