aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autotests/kconfig_compiler/CMakeLists.txt25
-rw-r--r--autotests/kconfig_compiler/kconfigcompiler_test_signals.cpp214
-rw-r--r--autotests/kconfig_compiler/signals_test.kcfg17
-rw-r--r--autotests/kconfig_compiler/signals_test_no_singleton.kcfgc9
-rw-r--r--autotests/kconfig_compiler/signals_test_no_singleton_dpointer.kcfgc9
-rw-r--r--autotests/kconfig_compiler/signals_test_singleton.kcfgc9
-rw-r--r--autotests/kconfig_compiler/signals_test_singleton_dpointer.kcfgc9
-rw-r--r--autotests/kconfig_compiler/test_signal.cpp.ref22
-rw-r--r--autotests/kconfig_compiler/test_signal.h.ref3
-rw-r--r--src/core/kcoreconfigskeleton.cpp74
-rw-r--r--src/core/kcoreconfigskeleton.h51
-rw-r--r--src/kconfig_compiler/kconfig_compiler.cpp79
12 files changed, 489 insertions, 32 deletions
diff --git a/autotests/kconfig_compiler/CMakeLists.txt b/autotests/kconfig_compiler/CMakeLists.txt
index a2ebb945..289e9583 100644
--- a/autotests/kconfig_compiler/CMakeLists.txt
+++ b/autotests/kconfig_compiler/CMakeLists.txt
@@ -3,14 +3,19 @@
# ../kconfig_compiler_kf5 $(srcdir)/test5.kcfg $(srcdir)/test5.kcfgc
macro(GEN_KCFG_TEST_SOURCE _testName _srcs)
+ cmake_parse_arguments(ARG "" "KCFG" "" ${ARGN} )
+ set(_kcfgFile ${ARG_KCFG})
+ if (NOT _kcfgFile)
+ set(_kcfgFile "${_testName}.kcfg")
+ endif()
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_testName}.cpp ${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h
- COMMAND ${KConfig_KCFGC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfg ${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfgc
- DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfg ${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfgc kconfig_compiler_kf5)
+ COMMAND ${KConfig_KCFGC_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${_kcfgFile} ${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfgc
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_kcfgFile} ${CMAKE_CURRENT_SOURCE_DIR}/${_testName}.kcfgc kconfig_compiler_kf5)
-# set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h PROPERTIES GENERATED TRUE)
+ # set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h PROPERTIES GENERATED TRUE)
qt5_generate_moc(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h ${CMAKE_CURRENT_BINARY_DIR}/${_testName}.moc )
-# do not run automoc on the generated file
+ # do not run automoc on the generated file
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${_testName}.cpp PROPERTIES SKIP_AUTOMOC TRUE)
set( ${_srcs} ${${_srcs}} ${CMAKE_CURRENT_BINARY_DIR}/${_testName}.cpp ${CMAKE_CURRENT_BINARY_DIR}/${_testName}.h )
@@ -197,6 +202,18 @@ target_link_libraries(test_signal KF5::ConfigGui)
########### next target ###############
+set(kconfigcompiler_test_signals_SRCS kconfigcompiler_test_signals.cpp)
+gen_kcfg_test_source(signals_test_singleton kconfigcompiler_test_signals_SRCS KCFG signals_test.kcfg)
+gen_kcfg_test_source(signals_test_no_singleton kconfigcompiler_test_signals_SRCS KCFG signals_test.kcfg)
+gen_kcfg_test_source(signals_test_singleton_dpointer kconfigcompiler_test_signals_SRCS KCFG signals_test.kcfg)
+gen_kcfg_test_source(signals_test_no_singleton_dpointer kconfigcompiler_test_signals_SRCS KCFG signals_test.kcfg)
+add_executable(kconfigcompiler_test_signals ${kconfigcompiler_test_signals_SRCS})
+ecm_mark_as_test(kconfigcompiler_test_signals)
+target_link_libraries(kconfigcompiler_test_signals Qt5::Test KF5::ConfigGui)
+add_test(kconfig-kconfigcompiler-signals kconfigcompiler_test_signals)
+
+########### next target ###############
+
set(kconfigcompiler_test_SRCS kconfigcompiler_test.cpp )
add_executable(kconfigcompiler_test ${kconfigcompiler_test_SRCS})
ecm_mark_as_test(kconfigcompiler_test)
diff --git a/autotests/kconfig_compiler/kconfigcompiler_test_signals.cpp b/autotests/kconfig_compiler/kconfigcompiler_test_signals.cpp
new file mode 100644
index 00000000..3017fe2f
--- /dev/null
+++ b/autotests/kconfig_compiler/kconfigcompiler_test_signals.cpp
@@ -0,0 +1,214 @@
+/*
+Copyright (c) 2014 Alexander Richardson <alex.richardson@gmx.de>
+
+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 "signals_test_singleton.h"
+#include "signals_test_no_singleton.h"
+#include "signals_test_singleton_dpointer.h"
+#include "signals_test_no_singleton_dpointer.h"
+#include <QtTest/QtTestGui>
+#include <QtTest/QSignalSpy>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QtGlobal>
+#include <QtCore/QDebug>
+#include <QTemporaryFile>
+#include <QFileInfo>
+#include <functional>
+
+class KConfigCompiler_Test_Signals : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testSetters();
+ void testSetters_data();
+ void testSetProperty();
+ void testSetProperty_data();
+ void initTestCase();
+ void cleanupTestCase();
+};
+
+static SignalsTestNoSingleton* noSingleton;
+static SignalsTestNoSingletonDpointer* noSingletonDpointer;
+
+void KConfigCompiler_Test_Signals::initTestCase()
+{
+ // These tests do a lot quite a few I/O operations, speed that up by using a QTemporaryFile.
+ // At least on Linux this is often a tmpfs which means only RAM operations
+ QTemporaryFile* tempFile1 = new QTemporaryFile(this);
+ QTemporaryFile* tempFile2 = new QTemporaryFile(this);
+ QTemporaryFile* tempFile3 = new QTemporaryFile(this);
+ QTemporaryFile* tempFile4 = new QTemporaryFile(this);
+ QVERIFY(tempFile1->open());
+ QVERIFY(tempFile2->open());
+ QVERIFY(tempFile3->open());
+ QVERIFY(tempFile4->open());
+
+ SignalsTestSingleton::instance(QFileInfo(*tempFile1).absoluteFilePath());
+ SignalsTestSingletonDpointer::instance(QFileInfo(*tempFile2).absoluteFilePath());
+ noSingleton = new SignalsTestNoSingleton(
+ KSharedConfig::openConfig(QFileInfo(*tempFile3).absoluteFilePath(), KConfig::SimpleConfig));
+ noSingletonDpointer = new SignalsTestNoSingletonDpointer(
+ KSharedConfig::openConfig(QFileInfo(*tempFile4).absoluteFilePath(), KConfig::SimpleConfig));
+}
+
+void KConfigCompiler_Test_Signals::cleanupTestCase()
+{
+ //ensure these instances are deleted before the temporary files are closed
+ delete noSingleton;
+ delete noSingletonDpointer;
+ delete SignalsTestSingleton::self();
+ delete SignalsTestSingletonDpointer::self();
+}
+
+struct TestSettersArg {
+ // default constructor required for Q_DECLARE_METATYPE
+ TestSettersArg() : obj(nullptr) {}
+ template<typename T>
+ TestSettersArg(T* object) : obj(object) {
+ // we can also call static methods using object->foo() so this works for all four cases
+ getter = [object]() { return object->foo(); };
+ defaultGetter = [object]() { return object->defaultFooValue(); };
+ setter = [object](const QString& s) { object->setFoo(s); };
+ }
+ KCoreConfigSkeleton* obj;
+ std::function<QString()> getter;
+ std::function<QString()> defaultGetter;
+ std::function<void(const QString&)> setter;
+};
+
+Q_DECLARE_METATYPE(TestSettersArg); // so that QFETCH works
+
+void KConfigCompiler_Test_Signals::testSetters_data()
+{
+ QTest::addColumn<TestSettersArg>("params");
+ QTest::newRow("singleton") << TestSettersArg(SignalsTestSingleton::self());
+ QTest::newRow("singleton dpointer") << TestSettersArg(SignalsTestSingletonDpointer::self());
+ QTest::newRow("non-singleton") << TestSettersArg(noSingleton);
+ QTest::newRow("non-singleton dpointer") << TestSettersArg(noSingleton);
+}
+
+/** Ensure that a signal is emitted whenever the data is changed by using the generated setters */
+void KConfigCompiler_Test_Signals::testSetters()
+{
+ QFETCH(TestSettersArg, params);
+ const char* signal = SIGNAL(fooChanged(QString));
+ const QString defaultValue = QStringLiteral("default");
+ const QString changedValue = QStringLiteral("changed");
+
+ // make sure we are in the default state
+ params.obj->setDefaults();
+ params.obj->writeConfig();
+
+ QList<QVariant> args;
+ QSignalSpy spy(params.obj, signal);
+ QVERIFY2(spy.isValid(), signal);
+
+ //change value via setter, should get signal
+ QCOMPARE(params.getter(), defaultValue);
+ QCOMPARE(defaultValue, params.defaultGetter());
+ QCOMPARE(params.getter(), params.defaultGetter());
+ QVERIFY(changedValue != params.getter());
+ params.setter(changedValue);
+ QCOMPARE(params.getter(), changedValue);
+ QCOMPARE(spy.count(), 0); //should have no change yet, only after writeConfig()
+ params.obj->writeConfig();
+ QCOMPARE(spy.count(), 1);
+ args = spy.takeFirst();
+ QCOMPARE(args.size(), 1);
+ QCOMPARE(args[0].value<QString>(), changedValue);
+
+ //reset to default values via setDefaults()
+ QVERIFY(params.getter() != params.defaultGetter());
+ QVERIFY(params.getter() != defaultValue);
+ QCOMPARE(params.getter(), changedValue);
+ params.obj->setDefaults();
+ QCOMPARE(params.getter(), params.defaultGetter());
+ QCOMPARE(params.getter(), defaultValue);
+
+ QCOMPARE(spy.count(), 0); //should have no change yet, only after writeConfig()
+ params.obj->writeConfig();
+ //TODO: This currently fails since setDefaults() does not yet cause emitting a signal
+ QCOMPARE(spy.count(), 1);
+ args = spy.takeFirst();
+ QCOMPARE(args.size(), 1);
+ QCOMPARE(args[0].value<QString>(), defaultValue);
+}
+
+Q_DECLARE_METATYPE(KCoreConfigSkeleton*);
+
+void KConfigCompiler_Test_Signals::testSetProperty_data()
+{
+ QTest::addColumn<KCoreConfigSkeleton*>("obj");
+ QTest::newRow("singleton") << static_cast<KCoreConfigSkeleton*>(SignalsTestSingleton::self());
+ QTest::newRow("singleton dpointer") << static_cast<KCoreConfigSkeleton*>(SignalsTestSingletonDpointer::self());
+ QTest::newRow("non-singleton") << static_cast<KCoreConfigSkeleton*>(noSingleton);
+ QTest::newRow("non-singleton dpointer") << static_cast<KCoreConfigSkeleton*>(noSingletonDpointer);
+}
+
+/** Test that the signal is emitted when modifying the values using the underlying KConfigSkeletonItem (bypassing the setters) */
+void KConfigCompiler_Test_Signals::testSetProperty()
+{
+ QFETCH(KCoreConfigSkeleton*, obj);
+ const char* signal = SIGNAL(fooChanged(QString));
+ const QString propertyName = QStringLiteral("foo");
+ const QString defaultValue = QStringLiteral("default");
+ const QString newValue = QStringLiteral("changed");
+ obj->setDefaults();
+ obj->writeConfig();
+
+ KConfigSkeletonItem* item = obj->findItem(propertyName);
+ QVERIFY2(item, "Item must exist");
+ QVERIFY2(!item->isImmutable(), "Item must not be immutable");
+ QVERIFY2(!obj->isImmutable(propertyName), "Item must not be immutable");
+
+ //listen for all expected signals
+ QSignalSpy spy(obj, signal);
+ QVERIFY2(spy.isValid(), signal);
+
+ QVERIFY(item->isEqual(defaultValue));
+ QVERIFY(!item->isEqual(newValue));
+
+ item->setProperty(newValue); //change value now
+ //should have no change yet, only after writeConfig()
+ QCOMPARE(spy.count(), 0);
+ obj->writeConfig();
+ //now check for the signal emissions
+ QCOMPARE(spy.count(), 1);
+ QList<QVariant> args = spy.takeFirst();
+ QCOMPARE(args.size(), 1);
+ QVERIFY(item->isEqual(args[0]));
+
+ //now reset to default
+ QVERIFY(!item->isEqual(defaultValue));
+ item->setDefault();
+ QVERIFY(item->isEqual(defaultValue));
+ //should have no change yet, only after writeConfig()
+ QCOMPARE(spy.count(), 0);
+ obj->writeConfig();
+ //now check for the signal emissions
+ QCOMPARE(spy.count(), 1);
+ args = spy.takeFirst();
+ QCOMPARE(args.size(), 1);
+ QVERIFY(item->isEqual(args[0]));
+}
+
+QTEST_MAIN(KConfigCompiler_Test_Signals)
+
+#include "kconfigcompiler_test_signals.moc"
diff --git a/autotests/kconfig_compiler/signals_test.kcfg b/autotests/kconfig_compiler/signals_test.kcfg
new file mode 100644
index 00000000..766c56eb
--- /dev/null
+++ b/autotests/kconfig_compiler/signals_test.kcfg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Author: Alex Richardson -->
+<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 arg="true"/>
+ <signal name="fooChanged">
+ <argument type="String">foo</argument>
+ </signal>
+ <group name="Something">
+ <entry key="foo" type="String">
+ <default>default</default>
+ <emit signal="fooChanged" />
+ </entry>
+ </group>
+</kcfg>
diff --git a/autotests/kconfig_compiler/signals_test_no_singleton.kcfgc b/autotests/kconfig_compiler/signals_test_no_singleton.kcfgc
new file mode 100644
index 00000000..4d096e08
--- /dev/null
+++ b/autotests/kconfig_compiler/signals_test_no_singleton.kcfgc
@@ -0,0 +1,9 @@
+File=signals_test.kcfg
+ClassName=SignalsTestNoSingleton
+Singleton=false
+Mutators=true
+MemberVariables=private
+GlobalEnums=false
+UseEnumTypes=false
+ItemAccessors=false
+DefaultValueGetters=true
diff --git a/autotests/kconfig_compiler/signals_test_no_singleton_dpointer.kcfgc b/autotests/kconfig_compiler/signals_test_no_singleton_dpointer.kcfgc
new file mode 100644
index 00000000..11b7c435
--- /dev/null
+++ b/autotests/kconfig_compiler/signals_test_no_singleton_dpointer.kcfgc
@@ -0,0 +1,9 @@
+File=signals_test.kcfg
+ClassName=SignalsTestNoSingletonDpointer
+Singleton=false
+Mutators=true
+MemberVariables=dpointer
+GlobalEnums=false
+UseEnumTypes=false
+ItemAccessors=false
+DefaultValueGetters=true
diff --git a/autotests/kconfig_compiler/signals_test_singleton.kcfgc b/autotests/kconfig_compiler/signals_test_singleton.kcfgc
new file mode 100644
index 00000000..59e04452
--- /dev/null
+++ b/autotests/kconfig_compiler/signals_test_singleton.kcfgc
@@ -0,0 +1,9 @@
+File=signals_test.kcfg
+ClassName=SignalsTestSingleton
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=false
+UseEnumTypes=false
+ItemAccessors=false
+DefaultValueGetters=true
diff --git a/autotests/kconfig_compiler/signals_test_singleton_dpointer.kcfgc b/autotests/kconfig_compiler/signals_test_singleton_dpointer.kcfgc
new file mode 100644
index 00000000..1f13493a
--- /dev/null
+++ b/autotests/kconfig_compiler/signals_test_singleton_dpointer.kcfgc
@@ -0,0 +1,9 @@
+File=signals_test.kcfg
+ClassName=SignalsTestSingletonDpointer
+Singleton=true
+Mutators=true
+MemberVariables=dpointer
+GlobalEnums=false
+UseEnumTypes=false
+ItemAccessors=false
+DefaultValueGetters=true
diff --git a/autotests/kconfig_compiler/test_signal.cpp.ref b/autotests/kconfig_compiler/test_signal.cpp.ref
index fd2d4bc9..35b5cba2 100644
--- a/autotests/kconfig_compiler/test_signal.cpp.ref
+++ b/autotests/kconfig_compiler/test_signal.cpp.ref
@@ -30,19 +30,21 @@ TestSignal::TestSignal( )
{
Q_ASSERT(!s_globalTestSignal()->q);
s_globalTestSignal()->q = this;
+ KConfigCompilerSignallingItem::NotifyFunction notifyFunction = static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&TestSignal::itemChanged);
+
setCurrentGroup( QLatin1String( "Appearance" ) );
- KConfigSkeleton::ItemString *itemEmoticonTheme;
- itemEmoticonTheme = new KConfigSkeleton::ItemString( currentGroup(), QLatin1String( "emoticonTheme" ), mEmoticonTheme, QLatin1String( "Default" ) );
+ KConfigCompilerSignallingItem *itemEmoticonTheme;
+ itemEmoticonTheme = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemString( currentGroup(), QLatin1String( "emoticonTheme" ), mEmoticonTheme, QLatin1String( "Default" ) ), this, notifyFunction, signalEmoticonSettingsChanged);
addItem( itemEmoticonTheme, QLatin1String( "emoticonTheme" ) );
- KConfigSkeleton::ItemBool *itemUseEmoticon;
- itemUseEmoticon = new KConfigSkeleton::ItemBool( currentGroup(), QLatin1String( "useEmoticon" ), mUseEmoticon, true );
+ KConfigCompilerSignallingItem *itemUseEmoticon;
+ itemUseEmoticon = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemBool( currentGroup(), QLatin1String( "useEmoticon" ), mUseEmoticon, true ), this, notifyFunction, signalEmoticonSettingsChanged);
addItem( itemUseEmoticon, QLatin1String( "useEmoticon" ) );
- KConfigSkeleton::ItemBool *itemEmoticonRequireSpace;
- itemEmoticonRequireSpace = new KConfigSkeleton::ItemBool( currentGroup(), QLatin1String( "emoticonRequireSpace" ), mEmoticonRequireSpace, true );
+ KConfigCompilerSignallingItem *itemEmoticonRequireSpace;
+ itemEmoticonRequireSpace = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemBool( currentGroup(), QLatin1String( "emoticonRequireSpace" ), mEmoticonRequireSpace, true ), this, notifyFunction, signalEmoticonSettingsChanged);
addItem( itemEmoticonRequireSpace, QLatin1String( "emoticonRequireSpace" ) );
- KConfigSkeleton::ItemString *itemStylePath;
- itemStylePath = new KConfigSkeleton::ItemString( currentGroup(), QLatin1String( "stylePath" ), mStylePath );
+ KConfigCompilerSignallingItem *itemStylePath;
+ itemStylePath = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemString( currentGroup(), QLatin1String( "stylePath" ), mStylePath ), this, notifyFunction, signalStyleChanged);
addItem( itemStylePath, QLatin1String( "stylePath" ) );
KConfigSkeleton::ItemString *itemStyleCSSVariant;
itemStyleCSSVariant = new KConfigSkeleton::ItemString( currentGroup(), QLatin1String( "styleVariant" ), mStyleCSSVariant );
@@ -69,5 +71,9 @@ bool TestSignal::usrWriteConfig()
return true;
}
+void TestSignal::itemChanged(quint64 flags) {
+ mSettingsChanged |= flags;
+}
+
#include "test_signal.moc"
diff --git a/autotests/kconfig_compiler/test_signal.h.ref b/autotests/kconfig_compiler/test_signal.h.ref
index 737718d0..19b8b400 100644
--- a/autotests/kconfig_compiler/test_signal.h.ref
+++ b/autotests/kconfig_compiler/test_signal.h.ref
@@ -132,6 +132,9 @@ class TestSignal : public KConfigSkeleton
*/
void styleChanged(const QString & stylePath, const QString & StyleCSSVariant);
+ private:
+ void itemChanged(quint64 flags);
+
protected:
TestSignal();
friend class TestSignalHelper;
diff --git a/src/core/kcoreconfigskeleton.cpp b/src/core/kcoreconfigskeleton.cpp
index d9b95b4b..98d9cdcc 100644
--- a/src/core/kcoreconfigskeleton.cpp
+++ b/src/core/kcoreconfigskeleton.cpp
@@ -1339,3 +1339,77 @@ KConfigSkeletonItem *KCoreConfigSkeleton::findItem(const QString &name) const
return d->mItemDict.value(name);
}
+KConfigCompilerSignallingItem::KConfigCompilerSignallingItem(KConfigSkeletonItem* item, QObject* object,
+ KConfigCompilerSignallingItem::NotifyFunction targetFunction, quint64 userData)
+ : KConfigSkeletonItem(item->group(), item->key()), mItem(item), mTargetFunction(targetFunction),
+ mObject(object), mUserData(userData)
+{
+ Q_ASSERT(mTargetFunction);
+ Q_ASSERT(mItem);
+ Q_ASSERT(mObject);
+}
+
+KConfigCompilerSignallingItem::~KConfigCompilerSignallingItem()
+{
+}
+
+bool KConfigCompilerSignallingItem::isEqual(const QVariant& p) const
+{
+ return mItem->isEqual(p);
+}
+
+QVariant KConfigCompilerSignallingItem::property() const
+{
+ return mItem->property();
+}
+
+void KConfigCompilerSignallingItem::readConfig(KConfig* c)
+{
+ QVariant oldValue = mItem->property();
+ mItem->readConfig(c);
+ //readConfig() changes mIsImmutable, update it here as well
+ KConfigGroup cg(c, mGroup );
+ readImmutability(cg);
+ if (!mItem->isEqual(oldValue)) {
+ invokeNotifyFunction();
+ }
+}
+
+void KConfigCompilerSignallingItem::readDefault(KConfig* c)
+{
+ mItem->readDefault(c);
+ //readDefault() changes mIsImmutable, update it here as well
+ KConfigGroup cg(c, mGroup );
+ readImmutability(cg);
+}
+
+void KConfigCompilerSignallingItem::writeConfig(KConfig* c)
+{
+ mItem->writeConfig(c);
+}
+
+void KConfigCompilerSignallingItem::setDefault()
+{
+ QVariant oldValue = mItem->property();
+ mItem->setDefault();
+ if (!mItem->isEqual(oldValue)) {
+ invokeNotifyFunction();
+ }
+}
+
+void KConfigCompilerSignallingItem::setProperty(const QVariant& p)
+{
+ if (!mItem->isEqual(p)) {
+ mItem->setProperty(p);
+ invokeNotifyFunction();
+ }
+}
+
+void KConfigCompilerSignallingItem::swapDefault()
+{
+ QVariant oldValue = mItem->property();
+ mItem->swapDefault();
+ if (!mItem->isEqual(oldValue)) {
+ invokeNotifyFunction();
+ }
+}
diff --git a/src/core/kcoreconfigskeleton.h b/src/core/kcoreconfigskeleton.h
index c1a15877..9cd07994 100644
--- a/src/core/kcoreconfigskeleton.h
+++ b/src/core/kcoreconfigskeleton.h
@@ -304,6 +304,57 @@ protected:
};
/**
+ * \class KConfigSkeletonChangeNotifyingItem kcoreconfigskeleton.h <KConfigSkeletonChangeNotifyingItem>
+ *
+ * @author Alex Richardson
+ * @see KConfigSkeletonItem
+ *
+ *
+ * This class wraps a @ref KConfigSkeletonItem and invokes a function whenever the value changes.
+ * That function must take one quint64 parameter. Whenever the property value of the wrapped KConfigSkeletonItem
+ * changes this function will be invoked with the stored user data passed in the constructor.
+ * It does not call a function with the new value since this class is designed solely for the kconfig_compiler generated
+ * code and is therefore probably not suited for any other usecases.
+ */
+class KCONFIGCORE_EXPORT KConfigCompilerSignallingItem : public KConfigSkeletonItem
+{
+public:
+ typedef void (QObject::* NotifyFunction)(quint64 arg);
+ /**
+ * Constructor.
+ *
+ * @param item the KConfigSkeletonItem to wrap
+ * @param targetFunction the method to invoke whenever the value of @p item changes
+ * @param object The object on which the method is invoked.
+ * @param userData This data will be passed to @p targetFunction on every property change
+ */
+ KConfigCompilerSignallingItem(KConfigSkeletonItem *item, QObject* object,
+ NotifyFunction targetFunction, quint64 userData);
+ virtual ~KConfigCompilerSignallingItem();
+
+ virtual void readConfig(KConfig *) Q_DECL_OVERRIDE;
+ virtual void writeConfig(KConfig *) Q_DECL_OVERRIDE;
+ virtual void readDefault(KConfig *) Q_DECL_OVERRIDE;
+ virtual void setProperty(const QVariant &p) Q_DECL_OVERRIDE;
+ virtual bool isEqual(const QVariant &p) const Q_DECL_OVERRIDE;
+ virtual QVariant property() const Q_DECL_OVERRIDE;
+ virtual void setDefault() Q_DECL_OVERRIDE;
+ virtual void swapDefault() Q_DECL_OVERRIDE;
+private:
+ inline void invokeNotifyFunction()
+ {
+ // call the pointer to member function using the strange ->* operator
+ (mObject->*mTargetFunction)(mUserData);
+ }
+private:
+ QScopedPointer<KConfigSkeletonItem> mItem;
+ NotifyFunction mTargetFunction;
+ QObject* mObject;
+ quint64 mUserData;
+};
+
+
+/**
* \class KCoreConfigSkeleton kcoreconfigskeleton.h <KCoreConfigSkeleton>
*
* @short Class for handling preferences settings for an application.
diff --git a/src/kconfig_compiler/kconfig_compiler.cpp b/src/kconfig_compiler/kconfig_compiler.cpp
index 0c4254a2..7d84cfbc 100644
--- a/src/kconfig_compiler/kconfig_compiler.cpp
+++ b/src/kconfig_compiler/kconfig_compiler.cpp
@@ -1152,12 +1152,17 @@ static QString itemDeclaration(const CfgEntry *e, const CfgConfig &cfg)
return QString();
}
+ QString type;
+ if (!e->signalList().isEmpty()) {
+ type = "KConfigCompilerSignallingItem";
+ } else {
+ type = cfg.inherits + "::Item" + itemType(e->type());
+ }
+
QString fCap = e->name();
fCap[0] = fCap[0].toUpper();
- return " " + cfg.inherits + "::Item" + itemType(e->type()) +
- " *item" + fCap +
- ((!e->param().isEmpty()) ? (QString("[%1]").arg(e->paramMax() + 1)) : QString()) +
- ";\n";
+ return " " + type + " *item" + fCap +
+ ( (!e->param().isEmpty())?(QString("[%1]").arg(e->paramMax()+1)) : QString()) + ";\n";
}
// returns the name of an item variable
@@ -1192,24 +1197,41 @@ static QString itemPath(const CfgEntry *e, const CfgConfig &cfg)
return result;
}
-QString newItem(const QString &type, const QString &name, const QString &key,
- const QString &defaultValue, const CfgConfig &cfg, const QString &param = QString())
-{
- QString t = "new " + cfg.inherits + "::Item" + itemType(type) +
- "( currentGroup(), " + key + ", " + varPath(name, cfg) + param;
- if (type == "Enum") {
- t += ", values" + name;
+QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue,
+ const CfgConfig &cfg, const QString &param = QString()) {
+
+ QList<Signal> sigs = entry->signalList();
+ QString t;
+ if (!sigs.isEmpty()) {
+ t += "new KConfigCompilerSignallingItem(";
+ }
+ t += "new "+ cfg.inherits + "::Item" + itemType(entry->type()) + "( currentGroup(), "
+ + key + ", " + varPath( entry->name(), cfg ) + param;
+
+ if (entry->type() == "Enum") {
+ t += ", values" + entry->name();
}
if (!defaultValue.isEmpty()) {
t += ", ";
- if (type == "String") {
+ if (entry->type() == "String") {
t += defaultValue;
} else {
t += defaultValue;
}
}
- t += " );";
+ t += " )";
+ if (!sigs.isEmpty()) {
+ t += ", this, notifyFunction, ";
+ //append the signal flags
+ for (int i = 0; i < sigs.size(); ++i) {
+ if (i != 0)
+ t += " | ";
+ t += signalEnumName(sigs[i].name);
+ }
+ t += ")";
+ }
+ t += ";";
return t;
}
@@ -2019,6 +2041,10 @@ int main(int argc, char **argv)
h << ");" << endl;
}
h << endl;
+
+ h << " private:" << endl;
+ h << " void itemChanged(quint64 flags);" << endl;
+ h << endl;
}
h << " protected:" << endl;
@@ -2185,7 +2211,10 @@ int main(int argc, char **argv)
}
cpp << endl << " // items" << endl;
for (itEntry = entries.constBegin(); itEntry != entries.constEnd(); ++itEntry) {
- cpp << " " + cfg.inherits + "::Item" << itemType((*itEntry)->type()) << " *" << itemVar(*itEntry, cfg);
+ const QString declType = (*itEntry)->signalList().isEmpty()
+ ? QString(cfg.inherits + "::Item" + itemType((*itEntry)->type()))
+ : "KConfigCompilerSignallingItem";
+ cpp << " " << declType << " *" << itemVar( *itEntry, cfg );
if (!(*itEntry)->param().isEmpty()) {
cpp << QString("[%1]").arg((*itEntry)->paramMax() + 1);
}
@@ -2302,6 +2331,14 @@ int main(int argc, char **argv)
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()) {
@@ -2353,7 +2390,7 @@ int main(int argc, char **argv)
if ((*itEntry)->param().isEmpty()) {
// Normal case
cpp << " " << itemPath(*itEntry, cfg) << " = "
- << newItem((*itEntry)->type(), (*itEntry)->name(), key, (*itEntry)->defaultValue(), cfg) << endl;
+ << newItem((*itEntry), key, (*itEntry)->defaultValue(), cfg) << endl;
if (!(*itEntry)->minValue().isEmpty()) {
cpp << " " << itemPath(*itEntry, cfg) << "->setMinValue(" << (*itEntry)->minValue() << ");" << endl;
@@ -2388,8 +2425,7 @@ int main(int argc, char **argv)
}
cpp << " " << itemVarStr << " = "
- << newItem((*itEntry)->type(), (*itEntry)->name(), paramString(key, *itEntry, i), defaultStr, cfg, QString("[%1]").arg(i))
- << endl;
+ << newItem((*itEntry), paramString(key, *itEntry, i), defaultStr, cfg, QString("[%1]").arg(i)) << endl;
if (cfg.setUserTexts) {
cpp << userTextsFunctions(*itEntry, cfg, itemVarStr, (*itEntry)->paramName());
@@ -2537,10 +2573,13 @@ int main(int argc, char **argv)
cpp << " " << varPath("settingsChanged", cfg) << " = 0;" << endl;
cpp << " return true;" << endl;
cpp << "}" << endl;
- }
- // Add includemoc if they are signals defined.
- if (hasSignals) {
+ cpp << endl;
+ cpp << "void " << cfg.className << "::" << "itemChanged(quint64 flags) {" << endl;
+ cpp << " " << varPath("settingsChanged", cfg) << " |= flags;" << endl;
+ cpp << "}" << endl;
+
+ // Add includemoc if they are signals defined.
cpp << endl;
cpp << "#include \"" << mocFileName << "\"" << endl;
cpp << endl;