1/*
2 This file is part of the KDE libraries
3
4 SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
5 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
6 SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
7 SPDX-FileCopyrightText: 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
8 SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
9 SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
10
11 SPDX-License-Identifier: LGPL-2.0-or-later
12*/
13
14#include "KConfigSourceGenerator.h"
15
16#include <QRegularExpression>
17
18KConfigSourceGenerator::KConfigSourceGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &parseResult)
19 : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1String(".cpp"), cfg, parseResult)
20{
21}
22
23void KConfigSourceGenerator::start()
24{
25 KConfigCodeGeneratorBase::start();
26 stream() << '\n';
27 createHeaders();
28
29 if (!cfg().nameSpace.isEmpty()) {
30 stream() << "using namespace " << cfg().nameSpace << ";";
31 stream() << "\n\n";
32 }
33
34 createPrivateDPointerImplementation();
35 createSingletonImplementation();
36 createPreamble();
37 doConstructor();
38 doGetterSetterDPointerMode();
39 createDefaultValueGetterSetter();
40 createDestructor();
41 createNonModifyingSignalsHelper();
42 createSignalFlagsHandler();
43 includeMoc();
44}
45
46void KConfigSourceGenerator::createHeaders()
47{
48 QString headerName = cfg().baseName + QLatin1Char('.') + cfg().headerExtension;
49
50 // TODO: Make addQuotes return a string instead of replacing it inplace.
51 addQuotes(s&: headerName);
52
53 addHeaders(header: {headerName});
54 stream() << '\n';
55
56 addHeaders(header: cfg().sourceIncludes);
57 if (cfg().setUserTexts && cfg().translationSystem == KConfigParameters::KdeTranslation) {
58 addHeaders(header: {QStringLiteral("klocalizedstring.h")});
59 stream() << '\n';
60 }
61
62 // Header required by singleton implementation
63 if (cfg().singleton) {
64 addHeaders(header: {QStringLiteral("qglobal.h"), QStringLiteral("QFile")});
65
66 // HACK: Add single line to fix test.
67 if (cfg().singleton && parseResult.cfgFileNameArg) {
68 stream() << '\n';
69 }
70 }
71
72 if (cfg().singleton && parseResult.cfgFileNameArg) {
73 addHeaders(header: {QStringLiteral("QDebug")});
74 }
75
76 if (cfg().dpointer && parseResult.hasNonModifySignals) {
77 addHeaders(header: {QStringLiteral("QSet")});
78 }
79
80 if (cfg().singleton) {
81 stream() << '\n';
82 }
83}
84
85void KConfigSourceGenerator::createPrivateDPointerImplementation()
86{
87 // private class implementation
88 if (!cfg().dpointer) {
89 return;
90 }
91
92 QString group;
93 beginNamespaces();
94 stream() << "class " << cfg().className << "Private\n";
95 stream() << "{\n";
96 stream() << " public:\n";
97
98 // Create Members
99 for (const auto *entry : std::as_const(t&: parseResult.entries)) {
100 if (entry->group != group) {
101 group = entry->group;
102 stream() << '\n';
103 stream() << " // " << group << '\n';
104 }
105 stream() << " " << cppType(t: entry->type) << " " << varName(n: entry->name, cfg: cfg());
106 if (!entry->param.isEmpty()) {
107 stream() << QStringLiteral("[%1]").arg(a: entry->paramMax + 1);
108 }
109 stream() << ";\n";
110 }
111 stream() << "\n // items\n";
112
113 // Create Items.
114 for (const auto *entry : std::as_const(t&: parseResult.entries)) {
115 const QString declType = entry->signalList.isEmpty() ? QString(cfg().inherits + QStringLiteral("::Item") + itemType(type: entry->type))
116 : QStringLiteral("KConfigCompilerSignallingItem");
117
118 stream() << " " << declType << " *" << itemVar(e: entry, cfg: cfg());
119 if (!entry->param.isEmpty()) {
120 stream() << QStringLiteral("[%1]").arg(a: entry->paramMax + 1);
121 }
122 stream() << ";\n";
123 }
124
125 if (parseResult.hasNonModifySignals) {
126 stream() << " QSet<quint64> " << varName(QStringLiteral("settingsChanged"), cfg: cfg()) << ";\n";
127 }
128
129 stream() << "};\n\n";
130 endNamespaces();
131}
132
133void KConfigSourceGenerator::createSingletonImplementation()
134{
135 // Singleton implementation
136 if (!cfg().singleton) {
137 return;
138 }
139
140 beginNamespaces();
141 stream() << "class " << cfg().className << "Helper\n";
142 stream() << '{' << '\n';
143 stream() << " public:\n";
144 stream() << " " << cfg().className << "Helper() : q(nullptr) {}\n";
145 stream() << " ~" << cfg().className << "Helper() { delete q; q = nullptr; }\n";
146 stream() << " " << cfg().className << "Helper(const " << cfg().className << "Helper&) = delete;\n";
147 stream() << " " << cfg().className << "Helper& operator=(const " << cfg().className << "Helper&) = delete;\n";
148 stream() << " " << cfg().className << " *q;\n";
149 stream() << "};\n";
150 endNamespaces();
151
152 stream() << "Q_GLOBAL_STATIC(" << cfg().className << "Helper, s_global" << cfg().className << ")\n";
153
154 stream() << cfg().className << " *" << cfg().className << "::self()\n";
155 stream() << "{\n";
156 if (parseResult.cfgFileNameArg) {
157 stream() << " if (!s_global" << cfg().className << "()->q)\n";
158 stream() << " qFatal(\"you need to call " << cfg().className << "::instance before using\");\n";
159 } else {
160 stream() << " if (!s_global" << cfg().className << "()->q) {\n";
161 stream() << " new " << cfg().className << ';' << '\n';
162 stream() << " s_global" << cfg().className << "()->q->read();\n";
163 stream() << " }\n\n";
164 }
165 stream() << " return s_global" << cfg().className << "()->q;\n";
166 stream() << "}\n\n";
167
168 if (parseResult.cfgFileNameArg) {
169 auto instance = [this](const QString &type, const QString &arg, bool isString) {
170 stream() << "void " << cfg().className << "::instance(" << type << " " << arg << ")\n";
171 stream() << "{\n";
172 stream() << " if (s_global" << cfg().className << "()->q) {\n";
173 stream() << " qDebug() << \"" << cfg().className << "::instance called after the first use - ignoring\";\n";
174 stream() << " return;\n";
175 stream() << " }\n";
176 stream() << " new " << cfg().className << "(";
177 if (parseResult.cfgStateConfig) {
178 stream() << "KSharedConfig::openStateConfig(" << arg << ")";
179 } else if (isString) {
180 stream() << "KSharedConfig::openConfig(" << arg << ")";
181 } else {
182 stream() << "std::move(" << arg << ")";
183 }
184 stream() << ");\n";
185 stream() << " s_global" << cfg().className << "()->q->read();\n";
186 stream() << "}\n\n";
187 };
188 instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true);
189 instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false);
190 }
191}
192
193void KConfigSourceGenerator::createPreamble()
194{
195 QString cppPreamble;
196 for (const auto *entry : std::as_const(t&: parseResult.entries)) {
197 if (entry->paramValues.isEmpty()) {
198 continue;
199 }
200
201 cppPreamble += QStringLiteral("const char* const ") + cfg().className + QStringLiteral("::") + enumName(n: entry->param);
202 cppPreamble += cfg().globalEnums
203 ? QStringLiteral("ToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n")
204 : QStringLiteral("::enumToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n");
205 }
206
207 if (!cppPreamble.isEmpty()) {
208 stream() << cppPreamble << '\n';
209 }
210}
211
212void KConfigSourceGenerator::createConstructorParameterList()
213{
214 if (parseResult.cfgFileNameArg) {
215 if (!cfg().forceStringFilename) {
216 stream() << " KSharedConfig::Ptr config";
217 } else {
218 stream() << " const QString& config";
219 }
220 stream() << (parseResult.parameters.isEmpty() ? "" : ",");
221 }
222
223 for (auto it = parseResult.parameters.cbegin(); it != parseResult.parameters.cend(); ++it) {
224 if (it != parseResult.parameters.cbegin()) {
225 stream() << ",";
226 }
227 stream() << " " << param(t: (*it).type) << " " << (*it).name;
228 }
229
230 if (cfg().parentInConstructor) {
231 if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) {
232 stream() << ",";
233 }
234 stream() << " QObject *parent";
235 }
236}
237
238void KConfigSourceGenerator::createParentConstructorCall()
239{
240 stream() << cfg().inherits << "(";
241 if (parseResult.cfgStateConfig) {
242 stream() << " KSharedConfig::openStateConfig(QStringLiteral( \"" << parseResult.cfgFileName << "\") ";
243 } else if (!parseResult.cfgFileName.isEmpty()) {
244 stream() << " QStringLiteral( \"" << parseResult.cfgFileName << "\" ";
245 }
246 if (parseResult.cfgFileNameArg) {
247 if (!cfg().forceStringFilename) {
248 stream() << " std::move( config ) ";
249 } else {
250 stream() << " config ";
251 }
252 }
253 if (!parseResult.cfgFileName.isEmpty()) {
254 stream() << ") ";
255 }
256 stream() << ")\n";
257}
258
259void KConfigSourceGenerator::createInitializerList()
260{
261 for (const auto &parameter : std::as_const(t&: parseResult.parameters)) {
262 stream() << " , mParam" << parameter.name << "(" << parameter.name << ")\n";
263 }
264}
265
266void KConfigSourceGenerator::createEnums(const CfgEntry *entry)
267{
268 if (entry->type != QLatin1String("Enum")) {
269 return;
270 }
271 stream() << " QList<" << cfg().inherits << "::ItemEnum::Choice> values" << entry->name << ";\n";
272
273 for (const auto &choice : std::as_const(t: entry->choices.choices)) {
274 stream() << " {\n";
275 stream() << " " << cfg().inherits << "::ItemEnum::Choice choice;\n";
276 stream() << " choice.name = QStringLiteral(\"" << choice.name << "\");\n";
277 if (cfg().setUserTexts) {
278 if (!choice.label.isEmpty()) {
279 stream() << " choice.label = " << translatedString(cfg: cfg(), string: choice.label, context: choice.context) << ";\n";
280 }
281 if (!choice.toolTip.isEmpty()) {
282 stream() << " choice.toolTip = " << translatedString(cfg: cfg(), string: choice.toolTip, context: choice.context) << ";\n";
283 }
284 if (!choice.whatsThis.isEmpty()) {
285 stream() << " choice.whatsThis = " << translatedString(cfg: cfg(), string: choice.whatsThis, context: choice.context) << ";\n";
286 }
287 }
288 stream() << " values" << entry->name << ".append( choice );\n";
289 stream() << " }\n";
290 }
291}
292
293void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key)
294{
295 const QString itemVarStr = itemPath(e: entry, cfg: cfg());
296 const QString innerItemVarStr = innerItemVar(e: entry, cfg: cfg());
297 if (!entry->signalList.isEmpty()) {
298 stream() << " " << innerItemVarStr << " = " << newInnerItem(entry, key, defaultValue: entry->defaultValue, cfg: cfg()) << '\n';
299 }
300
301 stream() << " " << itemVarStr << " = " << newItem(entry, key, defaultValue: entry->defaultValue, cfg: cfg()) << '\n';
302
303 if (!entry->min.isEmpty()) {
304 stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n";
305 }
306
307 if (!entry->max.isEmpty()) {
308 stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n";
309 }
310
311 if (cfg().setUserTexts) {
312 stream() << userTextsFunctions(e: entry, cfg: cfg());
313 }
314
315 if (cfg().allNotifiers || cfg().notifiers.contains(str: entry->name)) {
316 stream() << " " << itemVarStr << "->setWriteFlags(KConfigBase::Notify);\n";
317 }
318
319 for (const CfgEntry::Choice &choice : std::as_const(t: entry->choices.choices)) {
320 if (!choice.val.isEmpty()) {
321 stream() << " " << innerItemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val
322 << "\" ));\n";
323 }
324 }
325
326 if (!entry->parentGroup.isEmpty()) {
327 stream() << " " << itemVarStr << "->setGroup(cg" << QString(entry->group).remove(re: QRegularExpression(QStringLiteral("\\W"))) << ");\n";
328 }
329
330 stream() << " addItem( " << itemVarStr;
331 QString quotedName = entry->name;
332 addQuotes(s&: quotedName);
333 if (quotedName != key) {
334 stream() << ", QStringLiteral( \"" << entry->name << "\" )";
335 }
336 stream() << " );\n";
337}
338
339// TODO : Some compiler option won't work or generate bogus settings file.
340// * Does not manage properly Notifiers=true kcfgc option for parameterized entries :
341// ** KConfigCompilerSignallingItem generated with wrong userData parameter (4th one).
342// ** setWriteFlags() is missing.
343// * Q_PROPERTY signal won't work
344void KConfigSourceGenerator::createIndexedEntry(const CfgEntry *entry, const QString &key)
345{
346 for (int i = 0; i <= entry->paramMax; i++) {
347 const QString argBracket = QStringLiteral("[%1]").arg(a: i);
348 const QString innerItemVarStr = innerItemVar(e: entry, cfg: cfg()) + argBracket;
349
350 const QString defaultStr = !entry->paramDefaultValues[i].isEmpty() ? entry->paramDefaultValues[i]
351 : !entry->defaultValue.isEmpty() ? paramString(s: entry->defaultValue, e: entry, i)
352 : defaultValue(t: entry->type);
353
354 if (!entry->signalList.isEmpty()) {
355 stream() << " " << innerItemVarStr << " = " << newInnerItem(entry, key: paramString(s: key, e: entry, i), defaultValue: defaultStr, cfg: cfg(), param: argBracket) << '\n';
356 }
357
358 const QString itemVarStr = itemPath(e: entry, cfg: cfg()) + argBracket;
359
360 stream() << " " << itemVarStr << " = " << newItem(entry, key: paramString(s: key, e: entry, i), defaultValue: defaultStr, cfg: cfg(), param: argBracket) << '\n';
361
362 if (!entry->min.isEmpty()) {
363 stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n";
364 }
365 if (!entry->max.isEmpty()) {
366 stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n";
367 }
368
369 for (const CfgEntry::Choice &choice : std::as_const(t: entry->choices.choices)) {
370 if (!choice.val.isEmpty()) {
371 stream() << " " << innerItemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val
372 << "\" ));\n";
373 }
374 }
375
376 if (cfg().setUserTexts) {
377 stream() << userTextsFunctions(e: entry, cfg: cfg(), itemVarStr, i: entry->paramName);
378 }
379
380 // Make mutators for enum parameters work by adding them with $(..) replaced by the
381 // param name. The check for isImmutable in the set* functions doesn't have the param
382 // name available, just the corresponding enum value (int), so we need to store the
383 // param names in a separate static list!.
384 const bool isEnum = entry->paramType == QLatin1String("Enum");
385 const QString arg = isEnum ? entry->paramValues[i] : QString::number(i);
386
387 QString paramName = entry->paramName;
388
389 stream() << " addItem( " << itemVarStr << ", QStringLiteral( \"";
390 stream() << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), after: QLatin1String("%1")).arg(a: arg);
391 stream() << "\" ) );\n";
392 }
393}
394
395void KConfigSourceGenerator::handleCurrentGroupChange(const CfgEntry *entry)
396{
397 if (entry->group == mCurrentGroup) {
398 return;
399 }
400
401 // HACK: This fixes one spacing line in the diff. Remove this in the future and adapt the testcases.
402 static bool first = true;
403 if (!entry->group.isEmpty()) {
404 if (!first) {
405 stream() << '\n';
406 }
407 first = false;
408 }
409
410 mCurrentGroup = entry->group;
411
412 if (!entry->parentGroup.isEmpty()) {
413 QString parentGroup = QString(entry->parentGroup).remove(re: QRegularExpression(QStringLiteral("\\W")));
414 if (!mConfigGroupList.contains(str: parentGroup)) {
415 stream() << " KConfigGroup cg" << parentGroup << "(this->config(), " << paramString(group: entry->parentGroup, parameters: parseResult.parameters) << ");\n";
416 mConfigGroupList << parentGroup;
417 }
418 QString currentGroup = QString(mCurrentGroup).remove(re: QRegularExpression(QStringLiteral("\\W")));
419 if (!mConfigGroupList.contains(str: currentGroup)) {
420 stream() << " KConfigGroup cg" << currentGroup << " = cg" << QString(entry->parentGroup).remove(re: QRegularExpression(QStringLiteral("\\W")))
421 << ".group(" << paramString(group: mCurrentGroup, parameters: parseResult.parameters) << ");\n";
422 mConfigGroupList << currentGroup;
423 }
424 } else {
425 stream() << " setCurrentGroup( " << paramString(group: mCurrentGroup, parameters: parseResult.parameters) << " );";
426 stream() << "\n\n";
427 }
428}
429
430void KConfigSourceGenerator::doConstructor()
431{
432 // Constructor
433 stream() << cfg().className << "::" << cfg().className << "(";
434 createConstructorParameterList();
435 stream() << " )\n";
436 stream() << " : ";
437 createParentConstructorCall();
438 createInitializerList();
439
440 stream() << "{\n";
441
442 if (cfg().parentInConstructor) {
443 stream() << " setParent(parent);\n";
444 }
445
446 if (cfg().dpointer) {
447 stream() << " d = new " << cfg().className << "Private;\n";
448 }
449
450 // Needed in case the singleton class is used as baseclass for
451 // another singleton.
452 if (cfg().singleton) {
453 stream() << " Q_ASSERT(!s_global" << cfg().className << "()->q);\n";
454 stream() << " s_global" << cfg().className << "()->q = this;\n";
455 }
456
457 if (!parseResult.signalList.isEmpty()) {
458 // this cast to base-class pointer-to-member is valid C++
459 // https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/
460 stream() << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction ="
461 << " static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&" << cfg().className << "::itemChanged);\n";
462
463 stream() << '\n';
464 }
465
466 for (const auto *entry : std::as_const(t&: parseResult.entries)) {
467 handleCurrentGroupChange(entry);
468
469 const QString key = paramString(group: entry->key, parameters: parseResult.parameters);
470 if (!entry->code.isEmpty()) {
471 stream() << entry->code << '\n';
472 }
473 createEnums(entry);
474
475 stream() << itemDeclaration(e: entry, cfg: cfg());
476
477 if (entry->param.isEmpty()) {
478 createNormalEntry(entry, key);
479 } else {
480 createIndexedEntry(entry, key);
481 }
482 }
483
484 stream() << "}\n\n";
485}
486
487void KConfigSourceGenerator::createGetterDPointerMode(const CfgEntry *entry)
488{
489 // Accessor
490 if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
491 stream() << enumType(e: entry, globalEnums: cfg().globalEnums);
492 } else {
493 stream() << cppType(t: entry->type);
494 }
495
496 stream() << " " << getFunction(n: entry->name, className: cfg().className) << "(";
497 if (!entry->param.isEmpty()) {
498 stream() << " " << cppType(t: entry->paramType) << " i ";
499 }
500 stream() << ")" << Const() << '\n';
501
502 // function body inline only if not using dpointer
503 // for BC mode
504 startScope();
505 // HACK: Fix memberAccessorBody
506 stream() << " " << memberAccessorBody(e: entry, globalEnums: cfg().globalEnums);
507 endScope();
508 stream() << '\n';
509}
510
511void KConfigSourceGenerator::createImmutableGetterDPointerMode(const CfgEntry *entry)
512{
513 stream() << whitespace() << "";
514 stream() << "bool "
515 << " " << immutableFunction(n: entry->name, className: cfg().className) << "(";
516 if (!entry->param.isEmpty()) {
517 stream() << " " << cppType(t: entry->paramType) << " i ";
518 }
519 stream() << ")" << Const() << '\n';
520 startScope();
521 memberImmutableBody(e: entry, globalEnums: cfg().globalEnums);
522 endScope();
523 stream() << '\n';
524}
525
526void KConfigSourceGenerator::createSetterDPointerMode(const CfgEntry *entry)
527{
528 // Manipulator
529 if (!(cfg().allMutators || cfg().mutators.contains(str: entry->name))) {
530 return;
531 }
532
533 stream() << "void " << setFunction(n: entry->name, className: cfg().className) << "( ";
534 if (!entry->param.isEmpty()) {
535 stream() << cppType(t: entry->paramType) << " i, ";
536 }
537
538 if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
539 stream() << enumType(e: entry, globalEnums: cfg().globalEnums);
540 } else {
541 stream() << param(t: entry->type);
542 }
543 stream() << " v )\n";
544
545 // function body inline only if not using dpointer
546 // for BC mode
547 startScope();
548 memberMutatorBody(e: entry);
549 endScope();
550 stream() << '\n';
551}
552
553void KConfigSourceGenerator::createItemGetterDPointerMode(const CfgEntry *entry)
554{
555 // Item accessor
556 if (!cfg().itemAccessors) {
557 return;
558 }
559 stream() << '\n';
560 stream() << cfg().inherits << "::Item" << itemType(type: entry->type) << " *" << getFunction(n: entry->name, className: cfg().className) << "Item(";
561 if (!entry->param.isEmpty()) {
562 stream() << " " << cppType(t: entry->paramType) << " i ";
563 }
564 stream() << ")\n";
565 startScope();
566 stream() << " " << itemAccessorBody(e: entry, cfg: cfg());
567 endScope();
568}
569
570void KConfigSourceGenerator::doGetterSetterDPointerMode()
571{
572 if (!cfg().dpointer) {
573 return;
574 }
575
576 // setters and getters go in Cpp if in dpointer mode
577 for (const auto *entry : std::as_const(t&: parseResult.entries)) {
578 createSetterDPointerMode(entry);
579 createGetterDPointerMode(entry);
580 createImmutableGetterDPointerMode(entry);
581 createItemGetterDPointerMode(entry);
582 stream() << '\n';
583 }
584}
585
586void KConfigSourceGenerator::createDefaultValueGetterSetter()
587{
588 // default value getters always go in Cpp
589 for (const auto *entry : std::as_const(t&: parseResult.entries)) {
590 QString n = entry->name;
591 QString t = entry->type;
592
593 // Default value Accessor, as "helper" function
594 if ((cfg().allDefaultGetters || cfg().defaultGetters.contains(str: n)) && !entry->defaultValue.isEmpty()) {
595 stream() << cppType(t) << " " << getDefaultFunction(n, className: cfg().className) << "_helper(";
596 if (!entry->param.isEmpty()) {
597 stream() << " " << cppType(t: entry->paramType) << " i ";
598 }
599 stream() << ")" << Const() << '\n';
600 startScope();
601 stream() << memberGetDefaultBody(e: entry) << '\n';
602 endScope();
603 stream() << '\n';
604 }
605 }
606}
607
608void KConfigSourceGenerator::createDestructor()
609{
610 stream() << cfg().className << "::~" << cfg().className << "()\n";
611 startScope();
612 if (cfg().dpointer) {
613 stream() << " delete d;\n";
614 }
615 if (cfg().singleton) {
616 const QString qgs = QLatin1String("s_global") + cfg().className;
617 stream() << " if (" << qgs << ".exists() && !" << qgs << ".isDestroyed()) {\n";
618 stream() << " " << qgs << "()->q = nullptr;\n";
619 stream() << " }\n";
620 }
621 endScope();
622 stream() << '\n';
623}
624
625void KConfigSourceGenerator::createNonModifyingSignalsHelper()
626{
627 if (!parseResult.hasNonModifySignals) {
628 return;
629 }
630 stream() << "bool " << cfg().className << "::"
631 << "usrSave()\n";
632 startScope();
633 stream() << " const bool res = " << cfg().inherits << "::usrSave();\n";
634 stream() << " if (!res) return false;\n\n";
635 for (const Signal &signal : std::as_const(t&: parseResult.signalList)) {
636 if (signal.modify) {
637 continue;
638 }
639
640 stream() << " if (" << varPath(QStringLiteral("settingsChanged"), cfg: cfg()) << ".contains(" << signalEnumName(signalName: signal.name) << "))\n";
641 stream() << " Q_EMIT " << signal.name << "(";
642 auto it = signal.arguments.cbegin();
643 const auto itEnd = signal.arguments.cend();
644 while (it != itEnd) {
645 Param argument = *it;
646 bool cast = false;
647 if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) {
648 for (int i = 0, end = parseResult.entries.count(); i < end; ++i) {
649 if (parseResult.entries.at(i)->name == argument.name) {
650 stream() << "static_cast<" << enumType(e: parseResult.entries.at(i), globalEnums: cfg().globalEnums) << ">(";
651 cast = true;
652 break;
653 }
654 }
655 }
656 stream() << varPath(n: argument.name, cfg: cfg());
657 if (cast) {
658 stream() << ")";
659 }
660 if (++it != itEnd) {
661 stream() << ", ";
662 }
663 }
664
665 stream() << ");\n";
666 }
667
668 stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg: cfg()) << ".clear();\n";
669 stream() << " return true;\n";
670 endScope();
671}
672
673void KConfigSourceGenerator::createSignalFlagsHandler()
674{
675 if (parseResult.signalList.isEmpty()) {
676 return;
677 }
678
679 stream() << '\n';
680 stream() << "void " << cfg().className << "::"
681 << "itemChanged(quint64 signalFlag) {\n";
682 if (parseResult.hasNonModifySignals) {
683 stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg: cfg()) << ".insert(signalFlag);\n";
684 }
685
686 if (!parseResult.signalList.isEmpty()) {
687 stream() << '\n';
688 }
689
690 bool modifySignalsWritten = false;
691 for (const Signal &signal : std::as_const(t&: parseResult.signalList)) {
692 if (signal.modify) {
693 if (!modifySignalsWritten) {
694 stream() << " switch (signalFlag) {\n";
695 modifySignalsWritten = true;
696 }
697 stream() << " case " << signalEnumName(signalName: signal.name) << ":\n";
698 stream() << " Q_EMIT " << signal.name << "();\n";
699 stream() << " break;\n";
700 }
701 }
702 if (modifySignalsWritten) {
703 stream() << " }\n";
704 }
705
706 stream() << "}\n";
707}
708
709void KConfigSourceGenerator::includeMoc()
710{
711 const QString mocFileName = cfg().baseName + QStringLiteral(".moc");
712
713 if (parseResult.signalList.count() || cfg().generateProperties) {
714 // Add includemoc if they are signals defined.
715 stream() << '\n';
716 stream() << "#include \"" << mocFileName << "\"\n";
717 stream() << '\n';
718 }
719}
720

source code of kconfig/src/kconfig_compiler/KConfigSourceGenerator.cpp