diff --git a/autotests/kconfig_compiler/CMakeLists.txt b/autotests/kconfig_compiler/CMakeLists.txt index c459243..93ab9b0 100644 --- a/autotests/kconfig_compiler/CMakeLists.txt +++ b/autotests/kconfig_compiler/CMakeLists.txt @@ -1,267 +1,286 @@ # On Windows we have to generate the .h and .cpp inside ${CMAKE_BINARY_DIR}/bin because # otherwise QFINDTESTDATA will not be able to locate them. if(WIN32) set(KCFG_OUTPUT_DIR "${CMAKE_BINARY_DIR}/bin") else() set(KCFG_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") endif() # make sure the generated headers can be found include_directories(${KCFG_OUTPUT_DIR}) include(${CMAKE_SOURCE_DIR}/KF5ConfigMacros.cmake) macro(GEN_KCFG_TEST_SOURCE _testName _srcs) KCONFIG_ADD_KCFG_FILES(${_srcs} ${_testName}.kcfgc ${ARGN}) endmacro() include(ECMMarkAsTest) ########### next target ############### set(test1_SRCS test1main.cpp ) gen_kcfg_test_source(test1 test1_SRCS) ecm_add_test(TEST_NAME test1 ${test1_SRCS}) target_link_libraries(test1 KF5::ConfigGui) ########### next target ############### set(test2_SRCS test2main.cpp ) gen_kcfg_test_source(test2 test2_SRCS) ecm_add_test(TEST_NAME test2 ${test2_SRCS}) target_link_libraries(test2 KF5::ConfigGui) ########### next target ############### set(test3_SRCS test3main.cpp ) gen_kcfg_test_source(test3 test3_SRCS) ecm_add_test(TEST_NAME test3 ${test3_SRCS}) target_link_libraries(test3 KF5::ConfigGui) ########### next target ############### set(test3a_SRCS test3amain.cpp ) gen_kcfg_test_source(test3a test3a_SRCS) ecm_add_test(TEST_NAME test3a ${test3a_SRCS}) target_link_libraries(test3a KF5::ConfigGui) ########### next target ############### set(test4_SRCS test4main.cpp ) gen_kcfg_test_source(test4 test4_SRCS) ecm_add_test(TEST_NAME test4 ${test4_SRCS}) target_link_libraries(test4 KF5::ConfigGui) ########### next target ############### set(test5_SRCS test5main.cpp ) gen_kcfg_test_source(test5 test5_SRCS) ecm_add_test(TEST_NAME test5 ${test5_SRCS}) target_link_libraries(test5 KF5::ConfigGui) ########### next target ############### set(test6_SRCS test6main.cpp ) gen_kcfg_test_source(test6 test6_SRCS) ecm_add_test(TEST_NAME test6 ${test6_SRCS}) target_link_libraries(test6 KF5::ConfigGui) ########### next target ############### set(test7_SRCS test7main.cpp ) gen_kcfg_test_source(test7 test7_SRCS) ecm_add_test(TEST_NAME test7 ${test7_SRCS}) target_link_libraries(test7 KF5::ConfigGui) ########### next target ############### set(test8_SRCS test8main.cpp ) gen_kcfg_test_source(test8a test8_SRCS) gen_kcfg_test_source(test8b test8_SRCS) gen_kcfg_test_source(test8c test8_SRCS) ecm_add_test(TEST_NAME test8 ${test8_SRCS}) target_link_libraries(test8 KF5::ConfigGui) ########### next target ############### set(test9_SRCS test9main.cpp ) gen_kcfg_test_source(test9 test9_SRCS) ecm_add_test(TEST_NAME test9 ${test9_SRCS}) target_link_libraries(test9 KF5::ConfigGui) ########### next target ############### set(test10_SRCS test10main.cpp ) gen_kcfg_test_source(test10 test10_SRCS) ecm_add_test(TEST_NAME test10 ${test10_SRCS}) target_link_libraries(test10 KF5::ConfigGui) ########### next target ############### set(test11_SRCS test11main.cpp ) gen_kcfg_test_source(test11 test11_SRCS) gen_kcfg_test_source(test11a test11_SRCS) ecm_add_test(TEST_NAME test11 ${test11_SRCS}) target_link_libraries(test11 KF5::ConfigGui) ########### next target ############### set(test12_SRCS test12main.cpp ) gen_kcfg_test_source(test12 test12_SRCS) ecm_add_test(TEST_NAME test12 ${test12_SRCS}) target_link_libraries(test12 KF5::ConfigGui) ########### next target ############### set(test13_SRCS test13main.cpp ) gen_kcfg_test_source(test13 test13_SRCS GENERATE_MOC) ecm_add_test(TEST_NAME test13 ${test13_SRCS}) target_link_libraries(test13 KF5::ConfigGui) ########### next target ############### set(test_emptyentries_SRCS test_emptyentries_main.cpp ) gen_kcfg_test_source(test_emptyentries test_emptyentries_SRCS GENERATE_MOC) ecm_add_test(TEST_NAME test_emptyentries ${test_emptyentries_SRCS}) target_link_libraries(test_emptyentries KF5::ConfigGui) ########### next target ############### set(test_dpointer_SRCS test_dpointer_main.cpp ) gen_kcfg_test_source(test_dpointer test_dpointer_SRCS) ecm_add_test(TEST_NAME test_dpointer ${test_dpointer_SRCS}) target_link_libraries(test_dpointer KF5::ConfigGui) ########### next target ############### set(test_signal_SRCS test_signal_main.cpp ) gen_kcfg_test_source(test_signal test_signal_SRCS GENERATE_MOC) ecm_add_test(TEST_NAME test_signal ${test_signal_SRCS}) target_link_libraries(test_signal KF5::ConfigGui) ########### next target ############### set(test_notifiers_SRCS test_notifiers_main.cpp ) gen_kcfg_test_source(test_notifiers test_notifiers_SRCS) ecm_add_test(TEST_NAME test_notifiers ${test_notifiers_SRCS}) target_link_libraries(test_notifiers KF5::ConfigGui) ########### next target ############### set(kconfigcompiler_test_signals_SRCS kconfigcompiler_test_signals.cpp) gen_kcfg_test_source(signals_test_singleton kconfigcompiler_test_signals_SRCS GENERATE_MOC) gen_kcfg_test_source(signals_test_no_singleton kconfigcompiler_test_signals_SRCS GENERATE_MOC) gen_kcfg_test_source(signals_test_singleton_dpointer kconfigcompiler_test_signals_SRCS GENERATE_MOC) gen_kcfg_test_source(signals_test_no_singleton_dpointer kconfigcompiler_test_signals_SRCS GENERATE_MOC) ecm_add_test(${kconfigcompiler_test_signals_SRCS} TEST_NAME kconfigcompiler-signals-test LINK_LIBRARIES Qt5::Test KF5::ConfigGui ) +########### next target ############### + +set(test_properties_minmax_SRCS test_properties_minmax_main.cpp) +gen_kcfg_test_source(test_properties_minmax test_properties_minmax_SRCS GENERATE_MOC) + +ecm_add_test(${test_properties_minmax_SRCS} + TEST_NAME test-properties-minmax + LINK_LIBRARIES KF5::ConfigGui +) + +########### next target ############### + +set(test_param_minmax_SRCS test_param_minmax_main.cpp) +gen_kcfg_test_source(test_param_minmax test_param_minmax_SRCS GENERATE_MOC) + +ecm_add_test(${test_param_minmax_SRCS} + TEST_NAME test-param-minmax + LINK_LIBRARIES KF5::ConfigGui +) ########### next target ############### ecm_add_test(kconfigcompiler_test.cpp TEST_NAME kconfigcompiler-basic-test LINK_LIBRARIES Qt5::Test ) ########### next target ############### set(test_qcategory_SRCS test_qdebugcategorymain.cpp test_qdebugcategory_debug.cpp) gen_kcfg_test_source(test_qdebugcategory test_qcategory_SRCS) ecm_add_test(TEST_NAME test_qdebugcategory ${test_qcategory_SRCS}) target_link_libraries(test_qdebugcategory KF5::ConfigGui) ########### next target ############### set(test_translation_qt_SRCS test_translation_qt_main.cpp) gen_kcfg_test_source(test_translation_qt test_translation_qt_SRCS) ecm_add_test(TEST_NAME test_translation_qt ${test_translation_qt_SRCS}) target_link_libraries(test_translation_qt KF5::ConfigGui) ########### next target ############### set(test_translation_kde_SRCS test_translation_kde_main.cpp) gen_kcfg_test_source(test_translation_kde test_translation_kde_SRCS) ecm_add_test(TEST_NAME test_translation_kde ${test_translation_kde_SRCS}) target_link_libraries(test_translation_kde KF5::ConfigGui) ########### next target ############### set(test_translation_kde_domain_SRCS test_translation_kde_domain_main.cpp) gen_kcfg_test_source(test_translation_kde_domain test_translation_kde_domain_SRCS) ecm_add_test(TEST_NAME test_translation_kde_domain ${test_translation_kde_domain_SRCS}) target_link_libraries(test_translation_kde_domain KF5::ConfigGui) ########### next target ############### set(test_fileextensions_SRCS test_fileextensions_main.cxx) gen_kcfg_test_source(test_fileextensions test_fileextensions_SRCS) ecm_add_test(TEST_NAME test_fileextensions ${test_fileextensions_SRCS}) target_link_libraries(test_fileextensions KF5::ConfigGui) diff --git a/autotests/kconfig_compiler/kconfigcompiler_test.cpp b/autotests/kconfig_compiler/kconfigcompiler_test.cpp index d5b7baa..e4892e4 100644 --- a/autotests/kconfig_compiler/kconfigcompiler_test.cpp +++ b/autotests/kconfig_compiler/kconfigcompiler_test.cpp @@ -1,188 +1,192 @@ /* Tests for KConfig Compiler Copyright (c) 2005 by Duncan Mac-Vicar Copyright (c) 2009 by Pino Toscano ************************************************************************* * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * ************************************************************************* */ #include #include #include #include #include #include #include "kconfigcompiler_test.h" // QT5 TODO QTEST_GUILESS_MAIN(KConfigCompiler_Test) QTEST_MAIN(KConfigCompiler_Test) typedef const char *CompilerTestSet[]; static CompilerTestSet testCases = { "test1.cpp", "test1.h", "test2.cpp", "test2.h", "test3.cpp", "test3.h", "test3a.cpp", "test3a.h", "test4.cpp", "test4.h", "test5.cpp", "test5.h", "test6.cpp", "test6.h", "test7.cpp", "test7.h", "test8a.cpp", "test8a.h", "test8b.cpp", "test8b.h", "test8c.cpp", "test8c.h", "test9.h", "test9.cpp", "test10.h", "test10.cpp", "test11.h", "test11.cpp", "test11a.h", "test11a.cpp", "test12.h", "test12.cpp", "test13.h", "test13.cpp", "test_dpointer.cpp", "test_dpointer.h", "test_qdebugcategory.cpp", "test_qdebugcategory.h", "test_signal.cpp", "test_signal.h", "test_notifiers.cpp", "test_notifiers.h", "test_translation_kde.cpp", "test_translation_kde.h", "test_translation_kde_domain.cpp", "test_translation_kde_domain.h", "test_translation_qt.cpp", "test_translation_qt.h", "test_emptyentries.cpp", "test_emptyentries.h", + "test_properties_minmax.cpp", "test_properties_minmax.h", + "test_param_minmax.cpp", "test_param_minmax.h", nullptr }; static CompilerTestSet testCasesToRun = { "test1", "test2", "test3", "test3a", "test4", "test5", "test6", "test7", "test8", "test9", "test10", "test11", "test12", "test13", "test_dpointer", "test_qdebugcategory", "test_signal", "test_translation_kde", "test_translation_kde_domain", "test_translation_qt", "test_emptyentries", + "test_properties_minmax", + "test_param_minmax", nullptr }; #if 0 static CompilerTestSet willFailCases = { // where is that QDir coming from? //"test9.cpp", NULL NULL }; #endif void KConfigCompiler_Test::initTestCase() { m_diffExe = QStandardPaths::findExecutable( QStringLiteral("diff") ); if (m_diffExe.isEmpty()) { qDebug() << "diff command not found, detailed info on comparison failure will not be available."; } } void KConfigCompiler_Test::testBaselineComparison_data() { QTest::addColumn("testName"); for (const char **it = testCases; *it; ++it) { QTest::newRow(*it) << QString::fromLatin1(*it); } } void KConfigCompiler_Test::testBaselineComparison() { QFETCH(QString, testName); QFile file(QFINDTESTDATA(testName)); QFile fileRef(QFINDTESTDATA(testName + QLatin1String(".ref"))); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "Failed to open" << file.fileName() << "(" << testName << ")"; QFAIL("Can't open file for comparison"); } if (!fileRef.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "Failed to open" << fileRef.fileName() << "(" << testName << ".ref )"; QFAIL("Can't open file for comparison"); } QString content = file.readAll(); QString contentRef = fileRef.readAll(); if (content != contentRef) { appendFileDiff(fileRef.fileName(), file.fileName()); } QVERIFY(content == contentRef); } void KConfigCompiler_Test::testRunning_data() { QTest::addColumn("testName"); for (const char **it = testCasesToRun; *it; ++it) { QTest::newRow(*it) << QString::fromLatin1(*it); } } void KConfigCompiler_Test::testRunning() { QFETCH(QString, testName); #ifdef Q_OS_WIN testName += QStringLiteral(".exe"); #endif QString program = QFINDTESTDATA(testName); QVERIFY2(!program.isEmpty(), qPrintable(testName + QLatin1String(" must exist!"))); QVERIFY2(QFile::exists(program), qPrintable(program + QLatin1String(" must exist!"))); QProcess process; process.start(program, QStringList(), QIODevice::ReadOnly); if (process.waitForStarted()) { QVERIFY(process.waitForFinished()); } QCOMPARE((int)process.error(), (int)QProcess::UnknownError); QCOMPARE(process.exitCode(), 0); } void KConfigCompiler_Test::appendFileDiff(const QString &oldFile, const QString &newFile) { if (m_diffExe.isEmpty()) { return; } QStringList args({ QStringLiteral("-u"), QFileInfo(oldFile).absoluteFilePath(), QFileInfo(newFile).absoluteFilePath() }); QProcess process; process.start(m_diffExe, args, QIODevice::ReadOnly); process.waitForStarted(); process.waitForFinished(); if (process.exitCode() == 1) { const QString diffFileName = oldFile + QStringLiteral(".diff"); QFile diffFile(diffFileName); QVERIFY2(diffFile.open(QIODevice::WriteOnly), qPrintable(QLatin1String("Could not save diff file for ") + oldFile)); diffFile.write(process.readAllStandardOutput()); // force a failure to print the flename where we stored the diff. QVERIFY2(false, qPrintable(QLatin1String("This test failed, look at the following file for details: ") + diffFileName)); } } diff --git a/autotests/kconfig_compiler/test13.cpp.ref b/autotests/kconfig_compiler/test13.cpp.ref index 3fa79b1..c038c93 100644 --- a/autotests/kconfig_compiler/test13.cpp.ref +++ b/autotests/kconfig_compiler/test13.cpp.ref @@ -1,37 +1,39 @@ // 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( QStringLiteral( "muondatasourcesrc" ) ) { KConfigCompilerSignallingItem::NotifyFunction notifyFunction = static_cast(&Test13::itemChanged); setCurrentGroup( QStringLiteral( "kamoso" ) ); KConfigSkeleton::ItemUrl *itemPicturesDir; itemPicturesDir = new KConfigSkeleton::ItemUrl( currentGroup(), QStringLiteral( "picturesDir" ), mPicturesDir ); addItem( itemPicturesDir, QStringLiteral( "picturesDir" ) ); KConfigCompilerSignallingItem *itemBrightness; - itemBrightness = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "brightness" ), mBrightness ), this, notifyFunction, signalBrightnessChanged); + KConfigSkeleton::ItemDouble *innerItemBrightness; + innerItemBrightness = new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "brightness" ), mBrightness ); + itemBrightness = new KConfigCompilerSignallingItem(innerItemBrightness, this, notifyFunction, signalBrightnessChanged); addItem( itemBrightness, QStringLiteral( "brightness" ) ); KConfigSkeleton::ItemBool *itemStartsWithUppercase; itemStartsWithUppercase = new KConfigSkeleton::ItemBool( currentGroup(), QStringLiteral( "StartsWithUppercase" ), mStartsWithUppercase ); addItem( itemStartsWithUppercase, QStringLiteral( "StartsWithUppercase" ) ); } Test13::~Test13() { } void Test13::itemChanged(quint64 flags) { if ( flags & signalBrightnessChanged ) { Q_EMIT brightnessChanged(); } } #include "test13.moc" diff --git a/autotests/kconfig_compiler/test_param_minmax.cpp.ref b/autotests/kconfig_compiler/test_param_minmax.cpp.ref new file mode 100644 index 0000000..ceb224b --- /dev/null +++ b/autotests/kconfig_compiler/test_param_minmax.cpp.ref @@ -0,0 +1,41 @@ +// This file is generated by kconfig_compiler_kf5 from test_param_minmax.kcfg. +// All changes you do to this file will be lost. + +#include "test_param_minmax.h" + +TestParamMinMax::TestParamMinMax( KSharedConfig::Ptr config ) + : KConfigSkeleton( std::move( config ) ) +{ + setCurrentGroup( QStringLiteral( "Something" ) ); + + KConfigSkeleton::ItemDouble *itemFoo[6]; + itemFoo[0] = new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "foo_#0" ), mFoo[0], 1.23 ); + itemFoo[0]->setMinValue(0.01); + itemFoo[0]->setMaxValue(89898.23); + addItem( itemFoo[0], QStringLiteral( "Foo0" ) ); + itemFoo[1] = new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "foo_#1" ), mFoo[1], 1.23 ); + itemFoo[1]->setMinValue(0.01); + itemFoo[1]->setMaxValue(89898.23); + addItem( itemFoo[1], QStringLiteral( "Foo1" ) ); + itemFoo[2] = new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "foo_#2" ), mFoo[2], 1.23 ); + itemFoo[2]->setMinValue(0.01); + itemFoo[2]->setMaxValue(89898.23); + addItem( itemFoo[2], QStringLiteral( "Foo2" ) ); + itemFoo[3] = new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "foo_#3" ), mFoo[3], 1.23 ); + itemFoo[3]->setMinValue(0.01); + itemFoo[3]->setMaxValue(89898.23); + addItem( itemFoo[3], QStringLiteral( "Foo3" ) ); + itemFoo[4] = new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "foo_#4" ), mFoo[4], 1.23 ); + itemFoo[4]->setMinValue(0.01); + itemFoo[4]->setMaxValue(89898.23); + addItem( itemFoo[4], QStringLiteral( "Foo4" ) ); + itemFoo[5] = new KConfigSkeleton::ItemDouble( currentGroup(), QStringLiteral( "foo_#5" ), mFoo[5], 1.23 ); + itemFoo[5]->setMinValue(0.01); + itemFoo[5]->setMaxValue(89898.23); + addItem( itemFoo[5], QStringLiteral( "Foo5" ) ); +} + +TestParamMinMax::~TestParamMinMax() +{ +} + diff --git a/autotests/kconfig_compiler/test_param_minmax.h.ref b/autotests/kconfig_compiler/test_param_minmax.h.ref new file mode 100644 index 0000000..3cc8717 --- /dev/null +++ b/autotests/kconfig_compiler/test_param_minmax.h.ref @@ -0,0 +1,64 @@ +// This file is generated by kconfig_compiler_kf5 from test_param_minmax.kcfg. +// All changes you do to this file will be lost. +#ifndef TESTPARAMMINMAX_H +#define TESTPARAMMINMAX_H + +#include +#include +#include +#include + +class TestParamMinMax : public KConfigSkeleton +{ + public: + + TestParamMinMax( KSharedConfig::Ptr config = KSharedConfig::openConfig() ); + ~TestParamMinMax(); + + /** + Set foo_#$(myparam) + */ + void setFoo( int i, double v ) + { + if (v < 0.01) + { + qDebug() << "setFoo: value " << v << " is less than the minimum value of 0.01"; + v = 0.01; + } + + if (v > 89898.23) + { + qDebug() << "setFoo: value " << v << " is greater than the maximum value of 89898.23"; + v = 89898.23; + } + + if (!isFooImmutable( i )) + mFoo[i] = v; + } + + /** + Get foo_#$(myparam) + */ + double foo( int i ) const + { + return mFoo[i]; + } + + /** + Is foo_#$(myparam) Immutable + */ + bool isFooImmutable( int i ) const + { + return isImmutable( QStringLiteral( "Foo%1" ).arg( i ) ); + } + + protected: + + // Something + double mFoo[6]; + + private: +}; + +#endif + diff --git a/autotests/kconfig_compiler/test_param_minmax.kcfg b/autotests/kconfig_compiler/test_param_minmax.kcfg new file mode 100644 index 0000000..32046fd --- /dev/null +++ b/autotests/kconfig_compiler/test_param_minmax.kcfg @@ -0,0 +1,17 @@ + + + + + + + + 1.23 + 0.01 + 89898.23 + + + + diff --git a/autotests/kconfig_compiler/test_param_minmax.kcfgc b/autotests/kconfig_compiler/test_param_minmax.kcfgc new file mode 100644 index 0000000..3d9d276 --- /dev/null +++ b/autotests/kconfig_compiler/test_param_minmax.kcfgc @@ -0,0 +1,4 @@ +ClassName=TestParamMinMax +File=test_param_minmax.kcfgc +Mutators=true + diff --git a/autotests/kconfig_compiler/test_param_minmax_main.cpp b/autotests/kconfig_compiler/test_param_minmax_main.cpp new file mode 100644 index 0000000..57fc26a --- /dev/null +++ b/autotests/kconfig_compiler/test_param_minmax_main.cpp @@ -0,0 +1,29 @@ +/* +Copyright (c) 2020 Henri chain + +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 "test_param_minmax.h" + +int main(int, char **) +{ + TestParamMinMax *t = new TestParamMinMax(); + delete t; + return 0; +} + diff --git a/autotests/kconfig_compiler/test_properties_minmax.cpp.ref b/autotests/kconfig_compiler/test_properties_minmax.cpp.ref new file mode 100644 index 0000000..1736889 --- /dev/null +++ b/autotests/kconfig_compiler/test_properties_minmax.cpp.ref @@ -0,0 +1,35 @@ +// This file is generated by kconfig_compiler_kf5 from test_properties_minmax.kcfg. +// All changes you do to this file will be lost. + +#include "test_properties_minmax.h" + +TestPropertiesMinMax::TestPropertiesMinMax( KSharedConfig::Ptr config ) + : KConfigSkeleton( std::move( config ) ) +{ + KConfigCompilerSignallingItem::NotifyFunction notifyFunction = static_cast(&TestPropertiesMinMax::itemChanged); + + setCurrentGroup( QStringLiteral( "Something" ) ); + + KConfigCompilerSignallingItem *itemBar; + KConfigSkeleton::ItemInt *innerItemBar; + innerItemBar = new KConfigSkeleton::ItemInt( currentGroup(), QStringLiteral( "bar" ), mBar, 42 ); + itemBar = new KConfigCompilerSignallingItem(innerItemBar, this, notifyFunction, signalBarChanged); + innerItemBar->setMinValue(36); + innerItemBar->setMaxValue(102); + addItem( itemBar, QStringLiteral( "bar" ) ); +} + +TestPropertiesMinMax::~TestPropertiesMinMax() +{ +} + + +void TestPropertiesMinMax::itemChanged(quint64 flags) { + + if ( flags & signalBarChanged ) { + Q_EMIT barChanged(); + } +} + +#include "test_properties_minmax.moc" + diff --git a/autotests/kconfig_compiler/test_properties_minmax.h.ref b/autotests/kconfig_compiler/test_properties_minmax.h.ref new file mode 100644 index 0000000..26b79e6 --- /dev/null +++ b/autotests/kconfig_compiler/test_properties_minmax.h.ref @@ -0,0 +1,80 @@ +// This file is generated by kconfig_compiler_kf5 from test_properties_minmax.kcfg. +// All changes you do to this file will be lost. +#ifndef TESTPROPERTIESMINMAX_H +#define TESTPROPERTIESMINMAX_H + +#include +#include +#include +#include + +class TestPropertiesMinMax : public KConfigSkeleton +{ + Q_OBJECT + public: + + TestPropertiesMinMax( KSharedConfig::Ptr config = KSharedConfig::openConfig() ); + ~TestPropertiesMinMax(); + + /** + Set bar + */ + void setBar( int v ) + { + if (v < 36) + { + qDebug() << "setBar: value " << v << " is less than the minimum value of 36"; + v = 36; + } + + if (v > 102) + { + qDebug() << "setBar: value " << v << " is greater than the maximum value of 102"; + v = 102; + } + + if (v != mBar && !isBarImmutable()) { + mBar = v; + Q_EMIT barChanged(); + } + } + + Q_PROPERTY(int bar READ bar WRITE setBar NOTIFY barChanged) + Q_PROPERTY(bool isBarImmutable CONSTANT) + /** + Get bar + */ + int bar() const + { + return mBar; + } + + /** + Is bar Immutable + */ + bool isBarImmutable() const + { + return isImmutable( QStringLiteral( "bar" ) ); + } + + + enum { + signalBarChanged = 0x1 + }; + + Q_SIGNALS: + void barChanged(); + + private: + void itemChanged(quint64 flags); + + protected: + + // Something + int mBar; + + private: +}; + +#endif + diff --git a/autotests/kconfig_compiler/test_properties_minmax.kcfg b/autotests/kconfig_compiler/test_properties_minmax.kcfg new file mode 100644 index 0000000..621a793 --- /dev/null +++ b/autotests/kconfig_compiler/test_properties_minmax.kcfg @@ -0,0 +1,15 @@ + + + + + + + 42 + 36 + 102 + + + diff --git a/autotests/kconfig_compiler/test_properties_minmax.kcfgc b/autotests/kconfig_compiler/test_properties_minmax.kcfgc new file mode 100644 index 0000000..5cda329 --- /dev/null +++ b/autotests/kconfig_compiler/test_properties_minmax.kcfgc @@ -0,0 +1,5 @@ +ClassName=TestPropertiesMinMax +GenerateProperties=true +File=test_properties_minmax.kcfgc +Mutators=true + diff --git a/autotests/kconfig_compiler/test_properties_minmax_main.cpp b/autotests/kconfig_compiler/test_properties_minmax_main.cpp new file mode 100644 index 0000000..6d78971 --- /dev/null +++ b/autotests/kconfig_compiler/test_properties_minmax_main.cpp @@ -0,0 +1,28 @@ +/* +Copyright (c) 2020 Henri Chain + +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 "test_properties_minmax.h" + +int main(int, char **) +{ + TestPropertiesMinMax *t = new TestPropertiesMinMax(); + delete t; + return 0; +} diff --git a/autotests/kconfig_compiler/test_signal.cpp.ref b/autotests/kconfig_compiler/test_signal.cpp.ref index be06a74..71ab7a0 100644 --- a/autotests/kconfig_compiler/test_signal.cpp.ref +++ b/autotests/kconfig_compiler/test_signal.cpp.ref @@ -1,80 +1,88 @@ // This file is generated by kconfig_compiler_kf5 from test_signal.kcfg. // All changes you do to this file will be lost. #include "test_signal.h" #include #include class TestSignalHelper { public: TestSignalHelper() : q(nullptr) {} ~TestSignalHelper() { delete q; } TestSignalHelper(const TestSignalHelper&) = delete; TestSignalHelper& operator=(const TestSignalHelper&) = delete; TestSignal *q; }; Q_GLOBAL_STATIC(TestSignalHelper, s_globalTestSignal) TestSignal *TestSignal::self() { if (!s_globalTestSignal()->q) { new TestSignal; s_globalTestSignal()->q->read(); } return s_globalTestSignal()->q; } TestSignal::TestSignal( ) : KConfigSkeleton( QStringLiteral( "kconfig_compiler_kf5_test_rc" ) ) , mSettingsChanged(0) { Q_ASSERT(!s_globalTestSignal()->q); s_globalTestSignal()->q = this; KConfigCompilerSignallingItem::NotifyFunction notifyFunction = static_cast(&TestSignal::itemChanged); setCurrentGroup( QStringLiteral( "Appearance" ) ); KConfigCompilerSignallingItem *itemEmoticonTheme; - itemEmoticonTheme = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemString( currentGroup(), QStringLiteral( "emoticonTheme" ), mEmoticonTheme, QStringLiteral( "Default" ) ), this, notifyFunction, signalEmoticonSettingsChanged); + KConfigSkeleton::ItemString *innerItemEmoticonTheme; + innerItemEmoticonTheme = new KConfigSkeleton::ItemString( currentGroup(), QStringLiteral( "emoticonTheme" ), mEmoticonTheme, QStringLiteral( "Default" ) ); + itemEmoticonTheme = new KConfigCompilerSignallingItem(innerItemEmoticonTheme, this, notifyFunction, signalEmoticonSettingsChanged); addItem( itemEmoticonTheme, QStringLiteral( "emoticonTheme" ) ); KConfigCompilerSignallingItem *itemUseEmoticon; - itemUseEmoticon = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemBool( currentGroup(), QStringLiteral( "useEmoticon" ), mUseEmoticon, true ), this, notifyFunction, signalEmoticonSettingsChanged); + KConfigSkeleton::ItemBool *innerItemUseEmoticon; + innerItemUseEmoticon = new KConfigSkeleton::ItemBool( currentGroup(), QStringLiteral( "useEmoticon" ), mUseEmoticon, true ); + itemUseEmoticon = new KConfigCompilerSignallingItem(innerItemUseEmoticon, this, notifyFunction, signalEmoticonSettingsChanged); addItem( itemUseEmoticon, QStringLiteral( "useEmoticon" ) ); KConfigCompilerSignallingItem *itemEmoticonRequireSpace; - itemEmoticonRequireSpace = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemBool( currentGroup(), QStringLiteral( "emoticonRequireSpace" ), mEmoticonRequireSpace, true ), this, notifyFunction, signalEmoticonSettingsChanged); + KConfigSkeleton::ItemBool *innerItemEmoticonRequireSpace; + innerItemEmoticonRequireSpace = new KConfigSkeleton::ItemBool( currentGroup(), QStringLiteral( "emoticonRequireSpace" ), mEmoticonRequireSpace, true ); + itemEmoticonRequireSpace = new KConfigCompilerSignallingItem(innerItemEmoticonRequireSpace, this, notifyFunction, signalEmoticonSettingsChanged); addItem( itemEmoticonRequireSpace, QStringLiteral( "emoticonRequireSpace" ) ); KConfigCompilerSignallingItem *itemStylePath; - itemStylePath = new KConfigCompilerSignallingItem(new KConfigSkeleton::ItemString( currentGroup(), QStringLiteral( "stylePath" ), mStylePath ), this, notifyFunction, signalStyleChanged); + KConfigSkeleton::ItemString *innerItemStylePath; + innerItemStylePath = new KConfigSkeleton::ItemString( currentGroup(), QStringLiteral( "stylePath" ), mStylePath ); + itemStylePath = new KConfigCompilerSignallingItem(innerItemStylePath, this, notifyFunction, signalStyleChanged); addItem( itemStylePath, QStringLiteral( "stylePath" ) ); KConfigSkeleton::ItemString *itemStyleCSSVariant; itemStyleCSSVariant = new KConfigSkeleton::ItemString( currentGroup(), QStringLiteral( "styleVariant" ), mStyleCSSVariant ); addItem( itemStyleCSSVariant, QStringLiteral( "StyleCSSVariant" ) ); } TestSignal::~TestSignal() { s_globalTestSignal()->q = nullptr; } bool TestSignal::usrSave() { const bool res = KConfigSkeleton::usrSave(); if (!res) return false; 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" diff --git a/src/kconfig_compiler/KConfigCommonStructs.h b/src/kconfig_compiler/KConfigCommonStructs.h index 71bf666..06c8b80 100644 --- a/src/kconfig_compiler/KConfigCommonStructs.h +++ b/src/kconfig_compiler/KConfigCommonStructs.h @@ -1,196 +1,208 @@ /* This file is part of the KDE libraries Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCONFIGCOMMONSTRUCTS_H #define KCONFIGCOMMONSTRUCTS_H #include #include #include #include "KConfigParameters.h" struct Param { QString name; QString type; }; struct Signal { QString name; QString label; QList arguments; bool modify = false; }; class CfgEntry { public: struct Choice { QString name; QString context; QString label; QString toolTip; QString whatsThis; }; class Choices { public: Choices() {} Choices(const QList &d, const QString &n, const QString &p) : prefix(p), choices(d), mName(n) { int i = n.indexOf(QLatin1String("::")); if (i >= 0) { mExternalQual = n.left(i + 2); } } QString prefix; QList choices; const QString &name() const { return mName; } const QString &externalQualifier() const { return mExternalQual; } bool external() const { return !mExternalQual.isEmpty(); } private: QString mName; QString mExternalQual; }; public: QString group; QString type; QString key; QString name; QString labelContext; QString label; QString toolTipContext; QString toolTip; QString whatsThisContext; QString whatsThis; QString code; QString defaultValue; QString param; QString paramName; QString paramType; Choices choices; QList signalList; QStringList paramValues; QStringList paramDefaultValues; int paramMax; bool hidden; QString min; QString max; }; struct ParseResult { QString cfgFileName; bool cfgFileNameArg = false; QList parameters; QList signalList; QStringList includes; QList entries; bool hasNonModifySignals = false; }; // TODO: Move those methods QString enumName(const QString &n); QString enumName(const QString &n, const CfgEntry::Choices &c); QString param(const QString &t); QString cppType(const QString &t); QString itemType(const QString &type); QString changeSignalName(const QString &n); QString enumType(const CfgEntry *e, bool globalEnums); QString getDefaultFunction(const QString &n, const QString &className = QString()); QString setFunction(const QString &n, const QString &className = QString()); QString getFunction(const QString &n, const QString &className = QString()); QString immutableFunction(const QString &n, const QString &className = QString()); QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg); QString signalEnumName(const QString &signalName); bool isUnsigned(const QString &type); // returns the name of an member variable // use itemPath to know the full path // like using d-> in case of dpointer QString varName(const QString &n, const KConfigParameters &cfg); QString varPath(const QString &n, const KConfigParameters &cfg); // returns the name of an item variable // use itemPath to know the full path // like using d-> in case of dpointer QString itemVar(const CfgEntry *e, const KConfigParameters &cfg); +// returns the name of the local inner item if there is one +// (before wrapping with KConfigCompilerSignallingItem) +// Otherwise return itemVar() +QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg); + QString itemPath(const CfgEntry *e, const KConfigParameters &cfg); QString filenameOnly(const QString &path); QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg); QString translatedString( const KConfigParameters &cfg, const QString &string, const QString &context = QString(), const QString ¶m = QString(), const QString ¶mValue = QString()); // TODO: Sanitize those functions. QString newItem( - const CfgEntry* entry, + const CfgEntry *entry, + const QString &key, + const QString &defaultValue, + const KConfigParameters &cfg, + const QString ¶m = QString()); + +QString newInnerItem( + const CfgEntry *entry, const QString &key, - const QString& defaultValue, + const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m = QString()); QString userTextsFunctions( const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr = QString(), const QString &i = QString()); QString paramString(const QString &s, const CfgEntry *e, int i); QString paramString(const QString &group, const QList ¶meters); QString defaultValue(const QString &t); QString memberGetDefaultBody(const CfgEntry *e); QString literalString(const QString &s); QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c); void addQuotes(QString &s); void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n); #endif diff --git a/src/kconfig_compiler/KConfigSourceGenerator.cpp b/src/kconfig_compiler/KConfigSourceGenerator.cpp index 63f5b6b..33e0ed6 100644 --- a/src/kconfig_compiler/KConfigSourceGenerator.cpp +++ b/src/kconfig_compiler/KConfigSourceGenerator.cpp @@ -1,672 +1,692 @@ /* This file is part of the KDE libraries Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KConfigSourceGenerator.h" #include "KConfigCommonStructs.h" KConfigSourceGenerator::KConfigSourceGenerator( const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &result) : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.sourceExtension, cfg, result) { } void KConfigSourceGenerator::start() { KConfigCodeGeneratorBase::start(); stream() << '\n'; createHeaders(); if (!cfg().nameSpace.isEmpty()) { stream() << "using namespace " << cfg().nameSpace << ";"; stream() << "\n\n"; } createPrivateDPointerImplementation(); createSingletonImplementation(); createPreamble(); doConstructor(); doGetterSetterDPointerMode(); createDefaultValueGetterSetter(); createDestructor(); createNonModifyingSignalsHelper(); createSignalFlagsHandler(); includeMoc(); } void KConfigSourceGenerator::createHeaders() { QString headerName = cfg().baseName + QLatin1Char('.') + cfg().headerExtension; // TODO: Make addQuotes return a string instead of replacing it inplace. addQuotes(headerName); addHeaders({ headerName }); stream() << '\n'; addHeaders(cfg().sourceIncludes); if (cfg().setUserTexts && cfg().translationSystem == KConfigParameters::KdeTranslation) { addHeaders({QStringLiteral("klocalizedstring.h")}); stream() << '\n'; } // Header required by singleton implementation if (cfg().singleton) { addHeaders({QStringLiteral("qglobal.h"), QStringLiteral("QFile")}); // HACK: Add single line to fix test. if (cfg().singleton && parseResult.cfgFileNameArg) { stream() << '\n'; } } if (cfg().singleton && parseResult.cfgFileNameArg) { addHeaders({QStringLiteral("QDebug")}); } if (cfg().singleton) { stream() << '\n'; } } void KConfigSourceGenerator::createPrivateDPointerImplementation() { // private class implementation if (!cfg().dpointer) { return; } QString group; beginNamespaces(); stream() << "class " << cfg().className << "Private\n"; stream() << "{\n"; stream() << " public:\n"; // Create Members for (auto *entry : parseResult.entries) { if (entry->group != group) { group = entry->group; stream() << '\n'; stream() << " // " << group << '\n'; } stream() << " " << cppType(entry->type) << " " << varName(entry->name, cfg()); if (!entry->param.isEmpty()) { stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); } stream() << ";\n"; } stream() << "\n // items\n"; // Create Items. for (auto *entry : parseResult.entries) { const QString declType = entry->signalList.isEmpty() ? QString(cfg().inherits + QStringLiteral("::Item") + itemType(entry->type)) : QStringLiteral("KConfigCompilerSignallingItem"); stream() << " " << declType << " *" << itemVar( entry, cfg() ); if (!entry->param.isEmpty()) { stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); } stream() << ";\n"; } if (parseResult.hasNonModifySignals) { stream() << " uint " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n"; } stream() << "};\n\n"; endNamespaces(); } void KConfigSourceGenerator::createSingletonImplementation() { // Singleton implementation if (!cfg().singleton) { return; } beginNamespaces(); stream() << "class " << cfg().className << "Helper\n"; stream() << '{' << '\n'; stream() << " public:\n"; stream() << " " << cfg().className << "Helper() : q(nullptr) {}\n"; stream() << " ~" << cfg().className << "Helper() { delete q; }\n"; stream() << " " << cfg().className << "Helper(const " << cfg().className << "Helper&) = delete;\n"; stream() << " " << cfg().className << "Helper& operator=(const " << cfg().className << "Helper&) = delete;\n"; stream() << " " << cfg().className << " *q;\n"; stream() << "};\n"; endNamespaces(); stream() << "Q_GLOBAL_STATIC(" << cfg().className << "Helper, s_global" << cfg().className << ")\n"; stream() << cfg().className << " *" << cfg().className << "::self()\n"; stream() << "{\n"; if (parseResult.cfgFileNameArg) { stream() << " if (!s_global" << cfg().className << "()->q)\n"; stream() << " qFatal(\"you need to call " << cfg().className << "::instance before using\");\n"; } else { stream() << " if (!s_global" << cfg().className << "()->q) {\n"; stream() << " new " << cfg().className << ';' << '\n'; stream() << " s_global" << cfg().className << "()->q->read();\n"; stream() << " }\n\n"; } stream() << " return s_global" << cfg().className << "()->q;\n"; stream() << "}\n\n"; if (parseResult.cfgFileNameArg) { auto instance = [this] (const QString &type, const QString &arg, bool isString) { stream() << "void " << cfg().className << "::instance(" << type << " " << arg << ")\n"; stream() << "{\n"; stream() << " if (s_global" << cfg().className << "()->q) {\n"; stream() << " qDebug() << \"" << cfg().className << "::instance called after the first use - ignoring\";\n"; stream() << " return;\n"; stream() << " }\n"; stream() << " new " << cfg().className << "("; if (isString) { stream() << "KSharedConfig::openConfig(" << arg << ")"; } else { stream() << "std::move(" << arg << ")"; } stream() << ");\n"; stream() << " s_global" << cfg().className << "()->q->read();\n"; stream() << "}\n\n"; }; instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true); instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false); } } void KConfigSourceGenerator::createPreamble() { QString cppPreamble; for (const auto entry : parseResult.entries) { if (entry->paramValues.isEmpty()) { continue; } cppPreamble += QStringLiteral("const char* const ") + cfg().className + QStringLiteral("::") + enumName(entry->param); cppPreamble += cfg().globalEnums ? QStringLiteral("ToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n") : QStringLiteral("::enumToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n"); } if (!cppPreamble.isEmpty()) { stream() << cppPreamble << '\n'; } } void KConfigSourceGenerator::createConstructorParameterList() { if (parseResult.cfgFileNameArg) { if (!cfg().forceStringFilename) { stream() << " KSharedConfig::Ptr config"; } else { stream() << " const QString& config"; } stream() << (parseResult.parameters.isEmpty() ? "" : ","); } for (QList::ConstIterator it = parseResult.parameters.constBegin(); it != parseResult.parameters.constEnd(); ++it) { if (it != parseResult.parameters.constBegin()) { stream() << ","; } stream() << " " << param((*it).type) << " " << (*it).name; } if (cfg().parentInConstructor) { if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) { stream() << ","; } stream() << " QObject *parent"; } } void KConfigSourceGenerator::createParentConstructorCall() { stream() << cfg().inherits << "("; if (!parseResult.cfgFileName.isEmpty()) { stream() << " QStringLiteral( \"" << parseResult.cfgFileName << "\" "; } if (parseResult.cfgFileNameArg) { if (! cfg().forceStringFilename) { stream() << " std::move( config ) "; } else { stream() << " config "; } } if (!parseResult.cfgFileName.isEmpty()) { stream() << ") "; } stream() << ")\n"; } void KConfigSourceGenerator::createInitializerList() { for (const auto ¶meter : parseResult.parameters) { stream() << " , mParam" << parameter.name << "(" << parameter.name << ")\n"; } if (parseResult.hasNonModifySignals && !cfg().dpointer) { stream() << " , " << varName(QStringLiteral("settingsChanged"), cfg()) << "(0)\n"; } } void KConfigSourceGenerator::createEnums(const CfgEntry *entry) { if (entry->type != QLatin1String("Enum")) { return; } stream() << " QList<" << cfg().inherits << "::ItemEnum::Choice> values" << entry->name << ";\n"; for (const auto &choice : qAsConst(entry->choices.choices)) { stream() << " {\n"; stream() << " " << cfg().inherits << "::ItemEnum::Choice choice;\n"; stream() << " choice.name = QStringLiteral(\"" << choice.name << "\");\n"; if (cfg().setUserTexts) { if (!choice.label.isEmpty()) { stream() << " choice.label = " << translatedString(cfg(), choice.label, choice.context) << ";\n"; } if (!choice.toolTip.isEmpty()) { stream() << " choice.toolTip = " << translatedString(cfg(), choice.toolTip, choice.context) << ";\n"; } if (!choice.whatsThis.isEmpty()) { stream() << " choice.whatsThis = " << translatedString(cfg(), choice.whatsThis, choice.context) << ";\n"; } } stream() << " values" << entry->name << ".append( choice );\n"; stream() << " }\n"; } } void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key) { - stream() << " " << 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()) << " = " << newItem(entry, key, entry->defaultValue, cfg()) << '\n'; if (!entry->min.isEmpty()) { - stream() << " " << itemPath(entry, cfg()) << "->setMinValue(" << entry->min << ");\n"; + stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n"; } + if (!entry->max.isEmpty()) { - stream() << " " << itemPath(entry, cfg()) << "->setMaxValue(" << entry->max << ");\n"; + stream() << " " << innerItemVarStr << "->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() << " addItem( " << itemPath(entry, cfg()); QString quotedName = entry->name; addQuotes(quotedName); if (quotedName != key) { stream() << ", QStringLiteral( \"" << entry->name << "\" )"; } stream() << " );\n"; } void KConfigSourceGenerator::createIndexedEntry(const CfgEntry *entry, const QString &key) { for (int i = 0; i <= entry->paramMax; i++) { - QString itemVarStr(itemPath(entry, cfg()) + QStringLiteral("[%1]").arg(i)); + const QString argBracket = QStringLiteral("[%1]").arg(i); + const QString innerItemVarStr = innerItemVar(entry, cfg()) + argBracket; + + const QString defaultStr = !entry->paramDefaultValues[i].isEmpty() + ? entry->paramDefaultValues[i] + : !entry->defaultValue.isEmpty() ? paramString(entry->defaultValue, entry, i) : defaultValue(entry->type); + + if (!entry->signalList.isEmpty()) { + stream() << " " << innerItemVarStr << " = " + << newInnerItem(entry, paramString(key, entry, i), defaultStr, cfg(), argBracket) << '\n'; + } + + const QString itemVarStr = itemPath(entry, cfg()) + argBracket; - QString defaultStr = !entry->paramDefaultValues[i].isEmpty() ? entry->paramDefaultValues[i] - : !entry->defaultValue.isEmpty() ? paramString(entry->defaultValue, entry, i) - : defaultValue(entry->type); - stream() << " " << itemVarStr << " = " - << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), QStringLiteral("[%1]").arg(i)) << '\n'; + << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), argBracket) << '\n'; + + if (!entry->min.isEmpty()) { + stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n"; + } + if (!entry->max.isEmpty()) { + stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n"; + } if (cfg().setUserTexts) { stream() << userTextsFunctions(entry, cfg(), itemVarStr, entry->paramName); } // Make mutators for enum parameters work by adding them with $(..) replaced by the // param name. The check for isImmutable in the set* functions doesn't have the param // name available, just the corresponding enum value (int), so we need to store the // param names in a separate static list!. const bool isEnum = entry->paramType == QLatin1String("Enum"); const QString arg = isEnum ? entry->paramValues[i] : QString::number(i); QString paramName = entry->paramName; stream() << " addItem( " << itemVarStr << ", QStringLiteral( \""; - stream() << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), QLatin1String("%1")).arg( arg ); + stream() << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), QLatin1String("%1")).arg(arg); stream() << "\" ) );\n"; } } void KConfigSourceGenerator::handleCurrentGroupChange(const CfgEntry *entry) { if (entry->group == mCurrentGroup) { return; } // HACK: This fixes one spacing line in the diff. Remove this in the future and adapt the testcases. static bool first = true; if (!entry->group.isEmpty()) { if (!first) { stream() << '\n'; } first = false; } mCurrentGroup = entry->group; stream() << " setCurrentGroup( " << paramString(mCurrentGroup, parseResult.parameters) << " );"; stream() << "\n\n"; } void KConfigSourceGenerator::doConstructor() { // Constructor stream() << cfg().className << "::" << cfg().className << "("; createConstructorParameterList(); stream() << " )\n"; stream() << " : "; createParentConstructorCall(); createInitializerList(); stream() << "{\n"; if (cfg().parentInConstructor) { stream() << " setParent(parent);\n"; } if (cfg().dpointer) { stream() << " d = new " << cfg().className << "Private;\n"; if (parseResult.hasNonModifySignals) { stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;\n"; } } // Needed in case the singleton class is used as baseclass for // another singleton. if (cfg().singleton) { stream() << " Q_ASSERT(!s_global" << cfg().className << "()->q);\n"; stream() << " s_global" << cfg().className << "()->q = this;\n"; } if (!parseResult.signalList.isEmpty()) { // this cast to base-class pointer-to-member is valid C++ // https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/ stream() << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction =" << " static_cast(&" << cfg().className << "::itemChanged);\n"; stream() << '\n'; } for (auto *entry : parseResult.entries) { handleCurrentGroupChange(entry); const QString key = paramString(entry->key, parseResult.parameters); if (!entry->code.isEmpty()) { stream() << entry->code << '\n'; } createEnums(entry); - if (!cfg().dpointer) { - stream() << itemDeclaration(entry, cfg()); - } + stream() << itemDeclaration(entry, cfg()); if (entry->param.isEmpty()) { createNormalEntry(entry, key); } else { createIndexedEntry(entry, key); } } stream() << "}\n\n"; } void KConfigSourceGenerator::createGetterDPointerMode(const CfgEntry *entry) { // Accessor if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { stream() << enumType(entry, cfg().globalEnums); } else { stream() << cppType(entry->type); } stream() << " " << getFunction(entry->name, cfg().className) << "("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } stream() << ")" << Const() << '\n'; // function body inline only if not using dpointer // for BC mode startScope(); // HACK: Fix memberAccessorBody stream() << " " << memberAccessorBody(entry, cfg().globalEnums); endScope(); stream() << '\n'; } void KConfigSourceGenerator::createImmutableGetterDPointerMode(const CfgEntry *entry) { stream() << whitespace() << ""; stream() << "bool " << " " << immutableFunction(entry->name, cfg().className) << "("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } stream() << ")" << Const() << '\n'; startScope(); memberImmutableBody(entry, cfg().globalEnums); endScope(); stream() << '\n'; } void KConfigSourceGenerator::createSetterDPointerMode(const CfgEntry *entry) { // Manipulator if (!(cfg().allMutators || cfg().mutators.contains(entry->name))) { return; } stream() << "void " << setFunction(entry->name, cfg().className) << "( "; if (!entry->param.isEmpty()) { stream() << cppType(entry->paramType) << " i, "; } if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { stream() << enumType(entry, cfg().globalEnums); } else { stream() << param(entry->type); } stream() << " v )\n"; // function body inline only if not using dpointer // for BC mode startScope(); memberMutatorBody(entry); endScope(); stream() << '\n'; } void KConfigSourceGenerator::createItemGetterDPointerMode(const CfgEntry *entry) { // Item accessor if (!cfg().itemAccessors) { return; } stream() << '\n'; stream() << cfg().inherits << "::Item" << itemType(entry->type) << " *" << getFunction(entry->name, cfg().className) << "Item("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } stream() << ")\n"; startScope(); stream() << " " << itemAccessorBody(entry, cfg()); endScope(); } void KConfigSourceGenerator::doGetterSetterDPointerMode() { if (!cfg().dpointer) { return; } // setters and getters go in Cpp if in dpointer mode for (auto *entry : parseResult.entries) { createSetterDPointerMode(entry); createGetterDPointerMode(entry); createImmutableGetterDPointerMode(entry); createItemGetterDPointerMode(entry); stream() << '\n'; } } void KConfigSourceGenerator::createDefaultValueGetterSetter() { // default value getters always go in Cpp for (auto *entry : parseResult.entries) { QString n = entry->name; QString t = entry->type; // Default value Accessor, as "helper" function if ((cfg().allDefaultGetters || cfg().defaultGetters.contains(n)) && !entry->defaultValue.isEmpty()) { stream() << cppType(t) << " " << getDefaultFunction(n, cfg().className) << "_helper("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } stream() << ")" << Const() << '\n'; startScope(); stream() << memberGetDefaultBody(entry) << '\n'; endScope(); stream() << '\n'; } } } void KConfigSourceGenerator::createDestructor() { stream() << cfg().className << "::~" << cfg().className << "()\n"; startScope(); if (cfg().dpointer) { stream() << " delete d;\n"; } if (cfg().singleton) { stream() << " s_global" << cfg().className << "()->q = nullptr;\n"; } endScope(); stream() << '\n'; } void KConfigSourceGenerator::createNonModifyingSignalsHelper() { if (!parseResult.hasNonModifySignals) { return; } stream() << "bool " << cfg().className << "::" << "usrSave()\n"; startScope(); stream() << " const bool res = " << cfg().inherits << "::usrSave();\n"; stream() << " if (!res) return false;\n\n"; for (const Signal &signal : parseResult.signalList) { if (signal.modify) { continue; } stream() << " if ( " << varPath(QStringLiteral("settingsChanged"), cfg()) << " & " << signalEnumName(signal.name) << " )\n"; stream() << " Q_EMIT " << signal.name << "("; QList::ConstIterator it, itEnd = signal.arguments.constEnd(); for (it = signal.arguments.constBegin(); it != itEnd;) { Param argument = *it; bool cast = false; if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) { for (int i = 0, end = parseResult.entries.count(); i < end; ++i) { if (parseResult.entries.at(i)->name == argument.name) { stream() << "static_cast<" << enumType(parseResult.entries.at(i), cfg().globalEnums) << ">("; cast = true; break; } } } stream() << varPath(argument.name, cfg()); if (cast) { stream() << ")"; } if (++it != itEnd) { stream() << ", "; } } stream() << ");\n"; } stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;\n"; stream() << " return true;\n"; endScope(); } void KConfigSourceGenerator::createSignalFlagsHandler() { if (parseResult.signalList.isEmpty()) { return; } stream() << '\n'; stream() << "void " << cfg().className << "::" << "itemChanged(quint64 flags) {\n"; if (parseResult.hasNonModifySignals) stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " |= flags;\n"; if (!parseResult.signalList.isEmpty()) stream() << '\n'; for (const Signal &signal : parseResult.signalList) { if (signal.modify) { stream() << " if ( flags & " << signalEnumName(signal.name) << " ) {\n"; stream() << " Q_EMIT " << signal.name << "();\n"; stream() << " }\n"; } } stream() << "}\n"; } void KConfigSourceGenerator::includeMoc() { const QString mocFileName = cfg().baseName + QStringLiteral(".moc"); if (parseResult.signalList.count() || cfg().generateProperties) { // Add includemoc if they are signals defined. stream() << '\n'; stream() << "#include \"" << mocFileName << "\"\n"; stream() << '\n'; } } diff --git a/src/kconfig_compiler/KConfigXmlParser.cpp b/src/kconfig_compiler/KConfigXmlParser.cpp index b403dbd..2f31beb 100644 --- a/src/kconfig_compiler/KConfigXmlParser.cpp +++ b/src/kconfig_compiler/KConfigXmlParser.cpp @@ -1,557 +1,557 @@ /* This file is part of the KDE libraries Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KConfigXmlParser.h" #include "KConfigParameters.h" #include #include #include #include #include #include #include #include //TODO: Move preprocessDefault to Header / CPP implementation. // it makes no sense for a parser to process those values and generate code. static void preProcessDefault(QString &defaultValue, const QString &name, const QString &type, const CfgEntry::Choices &choices, QString &code, const KConfigParameters &cfg) { if (type == QLatin1String("String") && !defaultValue.isEmpty()) { defaultValue = literalString(defaultValue); } else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) { defaultValue = literalString(defaultValue); } else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) { // Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did. defaultValue = QLatin1String("QUrl::fromUserInput( ") + literalString(defaultValue) + QLatin1Char(')'); } else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) { QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); if (!code.isEmpty()) { cpp << '\n'; } if (type == QLatin1String("UrlList")) { cpp << " QList default" << name << ";\n"; } else { cpp << " QStringList default" << name << ";\n"; } const QStringList defaults = defaultValue.split(QLatin1Char(',')); QStringList::ConstIterator it; for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { cpp << " default" << name << ".append( "; if (type == QLatin1String("UrlList")) { cpp << "QUrl::fromUserInput("; } cpp << "QString::fromUtf8( \"" << *it << "\" ) "; if (type == QLatin1String("UrlList")) { cpp << ") "; } cpp << ");\n"; } defaultValue = QLatin1String("default") + name; } else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) { const QRegularExpression colorRe(QRegularExpression::anchoredPattern( QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?"))); if (colorRe.match(defaultValue).hasMatch()) { defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )"); } else { defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )"); } } else if (type == QLatin1String("Enum")) { QList::ConstIterator it; for (it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it) { if ((*it).name == defaultValue) { if (cfg.globalEnums && choices.name().isEmpty()) { defaultValue.prepend(choices.prefix); } else { defaultValue.prepend(enumTypeQualifier(name, choices) + choices.prefix); } break; } } } else if (type == QLatin1String("IntList")) { QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); if (!code.isEmpty()) { cpp << '\n'; } cpp << " QList default" << name << ";\n"; if (!defaultValue.isEmpty()) { const QStringList defaults = defaultValue.split(QLatin1Char(',')); QStringList::ConstIterator it; for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { cpp << " default" << name << ".append( " << *it << " );\n"; } } defaultValue = QLatin1String("default") + name; } } static QString dumpNode(const QDomNode &node) { QString msg; QTextStream s(&msg, QIODevice::WriteOnly); node.save(s, 0); msg = msg.simplified(); if (msg.length() > 40) { return msg.left(37) + QLatin1String("..."); } return msg; } void KConfigXmlParser::readParameterFromEntry(CfgEntry &readEntry, const QDomElement &e) { readEntry.param = e.attribute(QStringLiteral("name")); readEntry.paramType = e.attribute(QStringLiteral("type")); if (readEntry.param.isEmpty()) { std::cerr << "Parameter must have a name: " << qPrintable(dumpNode(e)) << std::endl; exit (1); } if (readEntry.paramType.isEmpty()) { std::cerr << "Parameter must have a type: " << qPrintable(dumpNode(e)) << std::endl; exit(1); } if ((readEntry.paramType == QLatin1String("Int")) || (readEntry.paramType == QLatin1String("UInt"))) { bool ok; readEntry.paramMax = e.attribute(QStringLiteral("max")).toInt(&ok); if (!ok) { std::cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " << qPrintable(dumpNode(e)) << std::endl; exit(1); } } else if (readEntry.paramType == QLatin1String("Enum")) { for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() == QLatin1String("values")) { for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { if (e3.tagName() == QLatin1String("value")) { readEntry.paramValues.append(e3.text()); } } break; } } if (readEntry.paramValues.isEmpty()) { std::cerr << "No values specified for parameter '" << qPrintable(readEntry.param) << "'." << std::endl; exit(1); } readEntry.paramMax = readEntry.paramValues.count() - 1; } else { std::cerr << "Parameter '" << qPrintable(readEntry.param) << "' has type " << qPrintable(readEntry.paramType) << " but must be of type int, uint or Enum." << std::endl; exit(1); } } bool KConfigXmlParser::hasDefaultCode(CfgEntry &readEntry, const QDomElement &element) { for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { if (e.attribute(QStringLiteral("param")).isEmpty()) { if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) { return true; } } } return false; } void KConfigXmlParser::readChoicesFromEntry(CfgEntry &readEntry, const QDomElement &e) { QList chlist; 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; } for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { if (e3.tagName() == QLatin1String("label")) { choice.label = e3.text(); choice.context = e3.attribute(QStringLiteral("context")); } if (e3.tagName() == QLatin1String("tooltip")) { choice.toolTip = e3.text(); choice.context = e3.attribute(QStringLiteral("context")); } if (e3.tagName() == QLatin1String("whatsthis")) { choice.whatsThis = e3.text(); choice.context = e3.attribute(QStringLiteral("context")); } } chlist.append(choice); } QString name = e.attribute(QStringLiteral("name")); QString prefix = e.attribute(QStringLiteral("prefix")); readEntry.choices = CfgEntry::Choices(chlist, name, prefix); } void KConfigXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element) { for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { QString tag = e.tagName(); if (tag == QLatin1String("label")) { readEntry.label = e.text(); readEntry.labelContext = e.attribute(QStringLiteral("context")); } else if (tag == QLatin1String("tooltip")) { readEntry.toolTip = e.text(); readEntry.toolTipContext = e.attribute(QStringLiteral("context")); } else if (tag == QLatin1String("whatsthis")) { readEntry.whatsThis = e.text(); readEntry.whatsThisContext = e.attribute(QStringLiteral("context")); } else if (tag == QLatin1String("min")) { readEntry.min = e.text(); } else if (tag == QLatin1String("max")) { readEntry.max = e.text(); } else if (tag == QLatin1String("code")) { readEntry.code = e.text(); } else if (tag == QLatin1String("parameter")) { readParameterFromEntry(readEntry, e); } else if (tag == QLatin1String("default")) { if (e.attribute(QStringLiteral("param")).isEmpty()) { readEntry.defaultValue = e.text(); } } else if (tag == QLatin1String("choices")) { readChoicesFromEntry(readEntry, e); } else if (tag == QLatin1String("emit")) { Signal signal; signal.name = e.attribute(QStringLiteral("signal")); readEntry.signalList.append(signal); } } } void KConfigXmlParser::createChangedSignal(CfgEntry &readEntry) { if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(readEntry.name))) { Signal s; s.name = changeSignalName(readEntry.name); s.modify = true; readEntry.signalList.append(s); } } void KConfigXmlParser::validateNameAndKey(CfgEntry &readEntry, const QDomElement &element) { bool nameIsEmpty = readEntry.name.isEmpty(); if (nameIsEmpty && readEntry.key.isEmpty()) { std::cerr << "Entry must have a name or a key: " << qPrintable(dumpNode(element)) << std::endl; exit (1); } if (readEntry.key.isEmpty()) { readEntry.key = readEntry.name; } if (nameIsEmpty) { readEntry.name = readEntry.key; readEntry.name.remove(QLatin1Char(' ')); } else if (readEntry.name.contains(QLatin1Char(' '))) { std::cout << "Entry '" << qPrintable(readEntry.name) << "' contains spaces! elements can not contain spaces!" << std::endl; readEntry.name.remove(QLatin1Char(' ')); } if (readEntry.name.contains(QStringLiteral("$("))) { if (readEntry.param.isEmpty()) { std::cerr << "Name may not be parameterized: " << qPrintable(readEntry.name) << std::endl; exit (1); } } else { if (!readEntry.param.isEmpty()) { std::cerr << "Name must contain '$(" << qPrintable(readEntry.param) << ")': " << qPrintable(readEntry.name) << std::endl; exit (1); } } } void KConfigXmlParser::readParamDefaultValues(CfgEntry &readEntry, const QDomElement &element) { if (readEntry.param.isEmpty()) { return; } // Adjust name readEntry.paramName = readEntry.name; readEntry.name.remove(QStringLiteral("$(") + readEntry.param + QLatin1Char(')')); // Lookup defaults for indexed entries for (int i = 0; i <= readEntry.paramMax; i++) { readEntry.paramDefaultValues.append(QString()); } for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { QString tag = e.tagName(); if (tag != QLatin1String("default")) { continue; } QString index = e.attribute(QStringLiteral("param")); if (index.isEmpty()) { continue; } bool ok; int i = index.toInt(&ok); if (!ok) { i = readEntry.paramValues.indexOf(index); if (i == -1) { std::cerr << "Index '" << qPrintable(index) << "' for default value is unknown." << std::endl; exit (1); } } if ((i < 0) || (i > readEntry.paramMax)) { std::cerr << "Index '" << i << "' for default value is out of range [0, " << readEntry.paramMax << "]." << std::endl; exit (1); } QString tmpDefaultValue = e.text(); if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) { preProcessDefault(tmpDefaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); } readEntry.paramDefaultValues[i] = tmpDefaultValue; } } CfgEntry *KConfigXmlParser::parseEntry(const QString &group, const QDomElement &element) { CfgEntry readEntry; readEntry.type = element.attribute(QStringLiteral("type")); readEntry.name = element.attribute(QStringLiteral("name")); readEntry.key = element.attribute(QStringLiteral("key")); readEntry.hidden = element.attribute(QStringLiteral("hidden")) == QLatin1String("true");; readEntry.group = group; const bool nameIsEmpty = readEntry.name.isEmpty(); readGroupElements(readEntry, element); - createChangedSignal(readEntry); validateNameAndKey(readEntry, element); if (readEntry.label.isEmpty()) { readEntry.label = readEntry.key; } if (readEntry.type.isEmpty()) { readEntry.type = QStringLiteral("String"); // XXX : implicit type might be bad } readParamDefaultValues(readEntry, element); if (!mValidNameRegexp.match(readEntry.name).hasMatch()) { if (nameIsEmpty) std::cerr << "The key '" << qPrintable(readEntry.key) << "' can not be used as name for the entry because " "it is not a valid name. You need to specify a valid name for this entry." << std::endl; else { std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not a valid name for an entry." << std::endl; } exit (1); } if (mAllNames.contains(readEntry.name)) { if (nameIsEmpty) std::cerr << "The key '" << qPrintable(readEntry.key) << "' can not be used as name for the entry because " "it does not result in a unique name. You need to specify a unique name for this entry." << std::endl; else { std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not unique." << std::endl; } exit (1); } mAllNames.append(readEntry.name); if (!hasDefaultCode(readEntry, element)) { // TODO: Move all the options to CfgEntry. preProcessDefault(readEntry.defaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); } // TODO: Try to Just return the CfgEntry we populated instead of // creating another one to fill the code. CfgEntry *result = new CfgEntry(); result->group = readEntry.group; result->type = readEntry.type; result->key = readEntry.key; result->name = readEntry.name; result->labelContext = readEntry.labelContext; result->label = readEntry.label; result->toolTipContext = readEntry.toolTipContext; result->toolTip = readEntry.toolTip; result->whatsThisContext = readEntry.whatsThisContext; result->whatsThis = readEntry.whatsThis; result->code = readEntry.code; result->defaultValue = readEntry.defaultValue; result->choices = readEntry.choices; result->signalList = readEntry.signalList; result->hidden = readEntry.hidden; if (!readEntry.param.isEmpty()) { result->param = readEntry.param; result->paramName = readEntry.paramName; result->paramType = readEntry.paramType; result->paramValues = readEntry.paramValues; result->paramDefaultValues = readEntry.paramDefaultValues; result->paramMax = readEntry.paramMax; } result->min = readEntry.min; result->max = readEntry.max; + createChangedSignal(*result); return result; } // TODO: Change the name of the config variable. KConfigXmlParser::KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName) : cfg(cfg), mInputFileName(inputFileName) { mValidNameRegexp.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*"))); } void KConfigXmlParser::start() { QFile input(mInputFileName); QDomDocument doc; QString errorMsg; int errorRow; int errorCol; if (!doc.setContent(&input, &errorMsg, &errorRow, &errorCol)) { std::cerr << "Unable to load document." << std::endl; std::cerr << "Parse error in " << qPrintable(mInputFileName) << ", line " << errorRow << ", col " << errorCol << ": " << qPrintable(errorMsg) << std::endl; exit (1); } QDomElement cfgElement = doc.documentElement(); if (cfgElement.isNull()) { std::cerr << "No document in kcfg file" << std::endl; exit (1); } for (QDomElement element = cfgElement.firstChildElement(); !element.isNull(); element = element.nextSiblingElement()) { QString tag = element.tagName(); if (tag == QLatin1String("include")) { readIncludeTag(element); } else if (tag == QLatin1String("kcfgfile")) { readKcfgfileTag(element); } else if (tag == QLatin1String("group")) { readGroupTag(element); } else if (tag == QLatin1String("signal")) { readSignalTag(element); } } } ParseResult KConfigXmlParser::getParseResult() const { return mParseResult; } void KConfigXmlParser::readIncludeTag(const QDomElement &e) { QString includeFile = e.text(); if (!includeFile.isEmpty()) { mParseResult.includes.append(includeFile); } } void KConfigXmlParser::readGroupTag(const QDomElement &e) { QString group = e.attribute(QStringLiteral("name")); if (group.isEmpty()) { std::cerr << "Group without name" << std::endl; exit (1); } for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() != QLatin1String("entry")) { continue; } CfgEntry *entry = parseEntry(group, e2); if (entry) { mParseResult.entries.append(entry); } else { std::cerr << "Can not parse entry." << std::endl; exit (1); } } } void KConfigXmlParser::readKcfgfileTag(const QDomElement &e) { mParseResult.cfgFileName = e.attribute(QStringLiteral("name")); mParseResult.cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true"); for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() == QLatin1String("parameter")) { Param p; p.name = e2.attribute(QStringLiteral("name")); p.type = e2.attribute(QStringLiteral("type")); if (p.type.isEmpty()) { p.type = QStringLiteral("String"); } mParseResult.parameters.append(p); } } } void KConfigXmlParser::readSignalTag(const QDomElement &e) { QString signalName = e.attribute(QStringLiteral("name")); if (signalName.isEmpty()) { std::cerr << "Signal without name." << std::endl; exit (1); } Signal theSignal; theSignal.name = signalName; for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() == QLatin1String("argument")) { Param argument; argument.type = e2.attribute(QStringLiteral("type")); if (argument.type.isEmpty()) { std::cerr << "Signal argument without type." << std::endl; exit (1); } argument.name= e2.text(); theSignal.arguments.append(argument); } else if (e2.tagName() == QLatin1String("label")) { theSignal.label = e2.text(); } } mParseResult.signalList.append(theSignal); } diff --git a/src/kconfig_compiler/kconfig_compiler.cpp b/src/kconfig_compiler/kconfig_compiler.cpp index 989e260..4e76f37 100644 --- a/src/kconfig_compiler/kconfig_compiler.cpp +++ b/src/kconfig_compiler/kconfig_compiler.cpp @@ -1,788 +1,808 @@ /* This file is part of KDE. Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Compiling this file with this flag is just crazy #undef QT_NO_CAST_FROM_ASCII #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../kconfig_version.h" #include "KConfigParameters.h" #include "KConfigCommonStructs.h" #include "KConfigHeaderGenerator.h" #include "KConfigSourceGenerator.h" #include "KConfigXmlParser.h" QString varName(const QString &n, const KConfigParameters &cfg) { QString result; if (!cfg.dpointer) { result = QChar::fromLatin1('m') + n; result[1] = result[1].toUpper(); } else { result = n; result[0] = result[0].toLower(); } return result; } QString varPath(const QString &n, const KConfigParameters &cfg) { QString result; if (cfg.dpointer) { result = "d->" + varName(n, cfg); } else { result = varName(n, cfg); } return result; } QString enumName(const QString &n) { QString result = QLatin1String("Enum") + n; result[4] = result[4].toUpper(); return result; } QString enumName(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + n; result[4] = result[4].toUpper(); } return result; } QString enumType(const CfgEntry *e, bool globalEnums) { QString result = e->choices.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + e->name; if (!globalEnums) { result += QLatin1String("::type"); } result[4] = result[4].toUpper(); } return result; } QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + n + QLatin1String("::"); result[4] = result[4].toUpper(); } else if (c.external()) { result = c.externalQualifier(); } else { result.clear(); } return result; } QString setFunction(const QString &n, const QString &className) { QString result = QLatin1String("set") + n; result[3] = result[3].toUpper(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } QString changeSignalName(const QString &n) { return n+QStringLiteral("Changed"); } QString getDefaultFunction(const QString &n, const QString &className) { QString result = QLatin1String("default") + n + QLatin1String("Value"); result[7] = result[7].toUpper(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } QString getFunction(const QString &n, const QString &className) { QString result = n; result[0] = result[0].toLower(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } QString immutableFunction(const QString &n, const QString &className) { QString result = QLatin1String("is") + n; result[2] = result[2].toUpper(); result += "Immutable"; if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } void addQuotes(QString &s) { if (!s.startsWith(QLatin1Char('"'))) { s.prepend(QLatin1Char('"')); } if (!s.endsWith(QLatin1Char('"'))) { s.append(QLatin1Char('"')); } } static QString quoteString(const QString &s) { QString r = s; r.replace(QLatin1Char('\\'), QLatin1String("\\\\")); r.replace(QLatin1Char('\"'), QLatin1String("\\\"")); r.remove(QLatin1Char('\r')); r.replace(QLatin1Char('\n'), QLatin1String("\\n\"\n\"")); return QLatin1Char('\"') + r + QLatin1Char('\"'); } QString literalString(const QString &s) { bool isAscii = true; for (int i = s.length(); i--;) if (s[i].unicode() > 127) { isAscii = false; } if (isAscii) { return QLatin1String("QStringLiteral( ") + quoteString(s) + QLatin1String(" )"); } else { return QLatin1String("QString::fromUtf8( ") + quoteString(s) + QLatin1String(" )"); } } QString signalEnumName(const QString &signalName) { QString result; result = QLatin1String("signal") + signalName; result[6] = result[6].toUpper(); return result; } bool isUnsigned(const QString &type) { if (type == QLatin1String("UInt")) { return true; } if (type == QLatin1String("ULongLong")) { return true; } return false; } /** Return parameter declaration for given type. */ QString param(const QString &t) { const QString type = t.toLower(); if (type == QLatin1String("string")) { return QStringLiteral("const QString &"); } else if (type == QLatin1String("stringlist")) { return QStringLiteral("const QStringList &"); } else if (type == QLatin1String("font")) { return QStringLiteral("const QFont &"); } else if (type == QLatin1String("rect")) { return QStringLiteral("const QRect &"); } else if (type == QLatin1String("size")) { return QStringLiteral("const QSize &"); } else if (type == QLatin1String("color")) { return QStringLiteral("const QColor &"); } else if (type == QLatin1String("point")) { return QStringLiteral("const QPoint &"); } else if (type == QLatin1String("int")) { return QStringLiteral("int"); } else if (type == QLatin1String("uint")) { return QStringLiteral("uint"); } else if (type == QLatin1String("bool")) { return QStringLiteral("bool"); } else if (type == QLatin1String("double")) { return QStringLiteral("double"); } else if (type == QLatin1String("datetime")) { return QStringLiteral("const QDateTime &"); } else if (type == QLatin1String("longlong")) { return QStringLiteral("qint64"); } else if (type == QLatin1String("ulonglong")) { return QStringLiteral("quint64"); } else if (type == QLatin1String("intlist")) { return QStringLiteral("const QList &"); } else if (type == QLatin1String("enum")) { return QStringLiteral("int"); } else if (type == QLatin1String("path")) { return QStringLiteral("const QString &"); } else if (type == QLatin1String("pathlist")) { return QStringLiteral("const QStringList &"); } else if (type == QLatin1String("password")) { return QStringLiteral("const QString &"); } else if (type == QLatin1String("url")) { return QStringLiteral("const QUrl &"); } else if (type == QLatin1String("urllist")) { return QStringLiteral("const QList &"); } else { std::cerr << "kconfig_compiler_kf5 does not support type \"" << qPrintable(type) << "\"" << std::endl; return QStringLiteral("QString"); //For now, but an assert would be better } } /** Actual C++ storage type for given type. */ QString cppType(const QString &t) { const QString type = t.toLower(); if (type == QLatin1String("string")) { return QStringLiteral("QString"); } else if (type == QLatin1String("stringlist")) { return QStringLiteral("QStringList"); } else if (type == QLatin1String("font")) { return QStringLiteral("QFont"); } else if (type == QLatin1String("rect")) { return QStringLiteral("QRect"); } else if (type == QLatin1String("size")) { return QStringLiteral("QSize"); } else if (type == QLatin1String("color")) { return QStringLiteral("QColor"); } else if (type == QLatin1String("point")) { return QStringLiteral("QPoint"); } else if (type == QLatin1String("int")) { return QStringLiteral("int"); } else if (type == QLatin1String("uint")) { return QStringLiteral("uint"); } else if (type == QLatin1String("bool")) { return QStringLiteral("bool"); } else if (type == QLatin1String("double")) { return QStringLiteral("double"); } else if (type == QLatin1String("datetime")) { return QStringLiteral("QDateTime"); } else if (type == QLatin1String("longlong")) { return QStringLiteral("qint64"); } else if (type == QLatin1String("ulonglong")) { return QStringLiteral("quint64"); } else if (type == QLatin1String("intlist")) { return QStringLiteral("QList"); } else if (type == QLatin1String("enum")) { return QStringLiteral("int"); } else if (type == QLatin1String("path")) { return QStringLiteral("QString"); } else if (type == QLatin1String("pathlist")) { return QStringLiteral("QStringList"); } else if (type == QLatin1String("password")) { return QStringLiteral("QString"); } else if (type == QLatin1String("url")) { return QStringLiteral("QUrl"); } else if (type == QLatin1String("urllist")) { return QStringLiteral("QList"); } else { std::cerr << "kconfig_compiler_kf5 does not support type \"" << qPrintable(type) << "\"" << std::endl; return QStringLiteral("QString"); //For now, but an assert would be better } } QString defaultValue(const QString &t) { const QString type = t.toLower(); if (type == QLatin1String("string")) { return QStringLiteral("\"\""); // Use empty string, not null string! } else if (type == QLatin1String("stringlist")) { return QStringLiteral("QStringList()"); } else if (type == QLatin1String("font")) { return QStringLiteral("QFont()"); } else if (type == QLatin1String("rect")) { return QStringLiteral("QRect()"); } else if (type == QLatin1String("size")) { return QStringLiteral("QSize()"); } else if (type == QLatin1String("color")) { return QStringLiteral("QColor(128, 128, 128)"); } else if (type == QLatin1String("point")) { return QStringLiteral("QPoint()"); } else if (type == QLatin1String("int")) { return QStringLiteral("0"); } else if (type == QLatin1String("uint")) { return QStringLiteral("0"); } else if (type == QLatin1String("bool")) { return QStringLiteral("false"); } else if (type == QLatin1String("double")) { return QStringLiteral("0.0"); } else if (type == QLatin1String("datetime")) { return QStringLiteral("QDateTime()"); } else if (type == QLatin1String("longlong")) { return QStringLiteral("0"); } else if (type == QLatin1String("ulonglong")) { return QStringLiteral("0"); } else if (type == QLatin1String("intlist")) { return QStringLiteral("QList()"); } else if (type == QLatin1String("enum")) { return QStringLiteral("0"); } else if (type == QLatin1String("path")) { return QStringLiteral("\"\""); // Use empty string, not null string! } else if (type == QLatin1String("pathlist")) { return QStringLiteral("QStringList()"); } else if (type == QLatin1String("password")) { return QStringLiteral("\"\""); // Use empty string, not null string! } else if (type == QLatin1String("url")) { return QStringLiteral("QUrl()"); } else if (type == QLatin1String("urllist")) { return QStringLiteral("QList()"); } else { std::cerr << "Error, kconfig_compiler_kf5 does not support the \"" << qPrintable(type) << "\" type!" << std::endl; return QStringLiteral("QString"); //For now, but an assert would be better } } QString itemType(const QString &type) { QString t; t = type; t.replace(0, 1, t.left(1).toUpper()); return t; } QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg) { - if (cfg.itemAccessors) { - return QString(); + const QString type = cfg.inherits + "::Item" + itemType(e->type); + + QString fCap = e->name; + fCap[0] = fCap[0].toUpper(); + const QString argSuffix = (!e->param.isEmpty()) ? (QStringLiteral("[%1]").arg(e->paramMax + 1)) : QString(); + QString result; + + if (!cfg.itemAccessors && !cfg.dpointer) { + result += " " + (!e->signalList.isEmpty() ? QStringLiteral("KConfigCompilerSignallingItem") : type) + + " *item" + fCap + argSuffix + ";\n"; } - QString type; if (!e->signalList.isEmpty()) { - type = QStringLiteral("KConfigCompilerSignallingItem"); - } else { - type = cfg.inherits + "::Item" + itemType(e->type); + result += " " + type + " *" + innerItemVar(e, cfg) + argSuffix + ";\n"; } - QString fCap = e->name; - fCap[0] = fCap[0].toUpper(); - return " " + type + " *item" + fCap + - ( (!e->param.isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax+1)) : QString()) + ";\n"; + return result; } // returns the name of an item variable // use itemPath to know the full path // like using d-> in case of dpointer QString itemVar(const CfgEntry *e, const KConfigParameters &cfg) { QString result; if (cfg.itemAccessors) { if (!cfg.dpointer) { result = 'm' + e->name + "Item"; result[1] = result[1].toUpper(); } else { result = e->name + "Item"; result[0] = result[0].toLower(); } } else { result = "item" + e->name; result[4] = result[4].toUpper(); } return result; } +// returns the name of the local inner item if there is one +// (before wrapping with KConfigCompilerSignallingItem) +// Otherwise return itemVar() +QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg) +{ + if (e->signalList.isEmpty()) { + return itemVar(e, cfg); + } else { + QString result = "innerItem" + e->name; + result[9] = result[9].toUpper(); + return result; + } +} + QString itemPath(const CfgEntry *e, const KConfigParameters &cfg) { QString result; if (cfg.dpointer) { result = "d->" + itemVar(e, cfg); } else { result = itemVar(e, cfg); } return result; } -QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue, +QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m) { - - QList sigs = entry->signalList; - QString t; - if (!sigs.isEmpty()) { - t += QLatin1String("new KConfigCompilerSignallingItem("); - } - t += "new "+ cfg.inherits + "::Item" + itemType(entry->type) + "( currentGroup(), " + QString t = "new "+ cfg.inherits + "::Item" + itemType(entry->type) + "( currentGroup(), " + key + ", " + varPath( entry->name, cfg ) + param; if (entry->type == QLatin1String("Enum")) { t += ", values" + entry->name; } if (!defaultValue.isEmpty()) { t += QLatin1String(", ") + defaultValue; } - t += QLatin1String(" )"); + t += QLatin1String(" );"); + + return t; +} + +QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, + const KConfigParameters &cfg, const QString ¶m) { + QList sigs = entry->signalList; + QString t; if (!sigs.isEmpty()) { + t += QLatin1String("new KConfigCompilerSignallingItem(") + innerItemVar(entry, cfg) + param; t += QLatin1String(", this, notifyFunction, "); //append the signal flags for (int i = 0; i < sigs.size(); ++i) { if (i != 0) t += QLatin1String(" | "); t += signalEnumName(sigs[i].name); } - t += QLatin1String(")"); + t += QLatin1String(");"); + } else { + t += newInnerItem(entry, key, defaultValue, cfg, param); } - t += QLatin1String(";"); return t; } QString paramString(const QString &s, const CfgEntry *e, int i) { QString result = s; QString needle = "$(" + e->param + ')'; if (result.contains(needle)) { QString tmp; if (e->paramType == QLatin1String("Enum")) { tmp = e->paramValues.at(i); } else { tmp = QString::number(i); } result.replace(needle, tmp); } return result; } QString paramString(const QString &group, const QList ¶meters) { QString paramString = group; QString arguments; int i = 1; for (QList::ConstIterator it = parameters.constBegin(); it != parameters.constEnd(); ++it) { if (paramString.contains("$(" + (*it).name + ')')) { const QString tmp = QStringLiteral("%%1").arg(i++); paramString.replace("$(" + (*it).name + ')', tmp); arguments += ".arg( mParam" + (*it).name + " )"; } } if (arguments.isEmpty()) { return "QStringLiteral( \"" + group + "\" )"; } return "QStringLiteral( \"" + paramString + "\" )" + arguments; } QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString ¶m, const QString ¶mValue) { QString result; switch (cfg.translationSystem) { case KConfigParameters::QtTranslation: if (!context.isEmpty()) { result += "/*: " + context + " */ QCoreApplication::translate(\""; } else { result += QLatin1String("QCoreApplication::translate(\""); } result += cfg.className + "\", "; break; case KConfigParameters::KdeTranslation: if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) { result += "i18ndc(" + quoteString(cfg.translationDomain) + ", " + quoteString(context) + ", "; } else if (!cfg.translationDomain.isEmpty()) { result += "i18nd(" + quoteString(cfg.translationDomain) + ", "; } else if (!context.isEmpty()) { result += "i18nc(" + quoteString(context) + ", "; } else { result += QLatin1String("i18n("); } break; } if (!param.isEmpty()) { QString resolvedString = string; resolvedString.replace("$(" + param + ')', paramValue); result += quoteString(resolvedString); } else { result += quoteString(string); } result += ')'; return result; } /* int i is the value of the parameter */ QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i) { QString txt; if (itemVarStr.isNull()) { itemVarStr = itemPath(e, cfg); } if (!e->label.isEmpty()) { txt += " " + itemVarStr + "->setLabel( "; txt += translatedString(cfg, e->label, e->labelContext, e->param, i); txt += QLatin1String(" );\n"); } if (!e->toolTip.isEmpty()) { txt += " " + itemVarStr + "->setToolTip( "; txt += translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i); txt += QLatin1String(" );\n"); } if (!e->whatsThis.isEmpty()) { txt += " " + itemVarStr + "->setWhatsThis( "; txt += translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i); txt += QLatin1String(" );\n"); } return txt; } // returns the member mutator implementation // which should go in the h file if inline // or the cpp file if not inline //TODO: Fix add Debug Method, it should also take the debug string. void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n) { if (cfg.qCategoryLoggingName.isEmpty()) { out << " qDebug() << \"" << setFunction(n); } else { out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n); } } // returns the member get default implementation // which should go in the h file if inline // or the cpp file if not inline QString memberGetDefaultBody(const CfgEntry *e) { QString result = e->code; QTextStream out(&result, QIODevice::WriteOnly); out << '\n'; if (!e->param.isEmpty()) { out << " switch (i) {\n"; for (int i = 0; i <= e->paramMax; ++i) { if (!e->paramDefaultValues[i].isEmpty()) { out << " case " << i << ": return " << e->paramDefaultValues[i] << ";\n"; } } QString defaultValue = e->defaultValue; out << " default:\n"; out << " return " << defaultValue.replace("$(" + e->param + ')', QLatin1String("i")) << ";\n"; out << " }\n"; } else { out << " return " << e->defaultValue << ';'; } return result; } // returns the item accesor implementation // which should go in the h file if inline // or the cpp file if not inline QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg) { QString result; QTextStream out(&result, QIODevice::WriteOnly); out << "return " << itemPath(e, cfg); if (!e->param.isEmpty()) { out << "[i]"; } out << ";\n"; return result; } //indents text adding X spaces per line QString indent(QString text, int spaces) { QString result; QTextStream out(&result, QIODevice::WriteOnly); QTextStream in(&text, QIODevice::ReadOnly); QString currLine; while (!in.atEnd()) { currLine = in.readLine(); if (!currLine.isEmpty()) for (int i = 0; i < spaces; i++) { out << " "; } out << currLine << '\n'; } return result; } bool hasErrors(KConfigXmlParser &parser, const ParseResult& parseResult, const KConfigParameters &cfg) { if (cfg.className.isEmpty()) { std::cerr << "Class name missing" << std::endl; return true; } if (cfg.singleton && !parseResult.parameters.isEmpty()) { std::cerr << "Singleton class can not have parameters" << std::endl; return true; } if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) { std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl; return true; } /* TODO: For some reason some configuration files prefer to have *no* entries * at all in it, and the generated code is mostly bogus as KConfigXT will not * handle save / load / properties, etc, nothing. * * The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop * project. * I think we should remove the possibility of creating configuration classes from configuration * files that don't really have configuration in it. but I'm changing this right now to allow * kdevelop files to pass. * * Remove for KDE 6 * (to make things more interesting, it failed in a code that's never used within KDevelop... ) */ if (parseResult.entries.isEmpty()) { std::cerr << "No entries." << std::endl; return false; } return false; } int main(int argc, char **argv) { QCoreApplication app(argc, argv); app.setApplicationName(QStringLiteral("kconfig_compiler")); app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING)); QString inputFilename, codegenFilename; QCommandLineOption targetDirectoryOption(QStringList { QStringLiteral("d"), QStringLiteral("directory") }, QCoreApplication::translate("main", "Directory to generate files in [.]"), QCoreApplication::translate("main", "directory"), QStringLiteral(".")); QCommandLineOption licenseOption ( QStringList { QStringLiteral("l"), QStringLiteral("license") }, QCoreApplication::translate("main", "Display software license.")); QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file")); parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file")); parser.addOption(targetDirectoryOption); parser.addOption (licenseOption); parser.addVersionOption(); parser.addHelpOption(); parser.process(app); if (parser.isSet(licenseOption)) { std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl; std::cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl; std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl; std::cout << "You may redistribute copies of this program" << std::endl; std::cout << "under the terms of the GNU Library Public License." << std::endl; std::cout << "For more information about these matters, see the file named COPYING." << std::endl; return 0; } const QStringList args = parser.positionalArguments(); if (args.count() < 2) { std::cerr << "Too few arguments." << std::endl; return 1; } if (args.count() > 2) { std::cerr << "Too many arguments." << std::endl; return 1; } inputFilename = args.at(0); codegenFilename = args.at(1); // TODO: Transform baseDir into a helper. QString baseDir = parser.value(targetDirectoryOption); #ifdef Q_OS_WIN if (!baseDir.endsWith('/') && !baseDir.endsWith('\\')) #else if (!baseDir.endsWith('/')) #endif baseDir.append("/"); KConfigParameters cfg(codegenFilename); KConfigXmlParser xmlParser(cfg, inputFilename); // The Xml Parser aborts in the case of an error, so if we get // to parseResult, we have a working Xml file. xmlParser.start(); ParseResult parseResult = xmlParser.getParseResult(); if (hasErrors(xmlParser, parseResult, cfg)) { return 1; } // TODO: Move this to somewhere saner. for (const auto &signal : qAsConst(parseResult.signalList)) { parseResult.hasNonModifySignals |= !signal.modify; } // remove '.kcfg' from the name. const QString baseName = inputFilename.mid(0, inputFilename.size()-5); KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult); headerGenerator.start(); headerGenerator.save(); KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult); sourceGenerator.start(); sourceGenerator.save(); qDeleteAll(parseResult.entries); }