1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2003 Benjamin C Meyer <ben+kdelibs at meyerhome dot net>
4 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
5 SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
6 SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
7 SPDX-FileCopyrightText: 2020 Cyril Rossi <cyril.rossi@enioka.com>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10*/
11
12#include "kconfigdialogmanager.h"
13#include "kconfigdialogmanager_p.h"
14#include "kconfigwidgets_debug.h"
15
16#include <QComboBox>
17#include <QGroupBox>
18#include <QLabel>
19#include <QLayout>
20#include <QMetaObject>
21#include <QMetaProperty>
22#include <QRadioButton>
23#include <QTimer>
24
25#include <KConfigSkeleton>
26
27typedef QHash<QString, QByteArray> MyHash;
28Q_GLOBAL_STATIC(MyHash, s_propertyMap)
29Q_GLOBAL_STATIC(MyHash, s_changedMap)
30
31KConfigDialogManager::KConfigDialogManager(QWidget *parent, KCoreConfigSkeleton *conf)
32 : QObject(parent)
33 , d(new KConfigDialogManagerPrivate(this))
34{
35 d->m_conf = conf;
36 d->m_dialog = parent;
37 init(trackChanges: true);
38}
39
40KConfigDialogManager::~KConfigDialogManager() = default;
41
42// KF6: Drop this and get signals only from metaObject and/or widget's dynamic properties kcfg_property/kcfg_propertyNotify
43void KConfigDialogManager::initMaps()
44{
45 if (s_propertyMap()->isEmpty()) {
46 s_propertyMap()->insert(QStringLiteral("KButtonGroup"), value: "current");
47 s_propertyMap()->insert(QStringLiteral("KColorButton"), value: "color");
48 s_propertyMap()->insert(QStringLiteral("KColorCombo"), value: "color");
49 s_propertyMap()->insert(QStringLiteral("KKeySequenceWidget"), value: "keySequence");
50 }
51
52 if (s_changedMap()->isEmpty()) {
53 // QT
54 s_changedMap()->insert(QStringLiteral("QCheckBox"), SIGNAL(stateChanged(int)));
55 s_changedMap()->insert(QStringLiteral("QPushButton"), SIGNAL(clicked(bool)));
56 s_changedMap()->insert(QStringLiteral("QRadioButton"), SIGNAL(toggled(bool)));
57 s_changedMap()->insert(QStringLiteral("QGroupBox"), SIGNAL(toggled(bool)));
58 s_changedMap()->insert(QStringLiteral("QComboBox"), SIGNAL(activated(int)));
59 s_changedMap()->insert(QStringLiteral("QDateEdit"), SIGNAL(dateChanged(QDate)));
60 s_changedMap()->insert(QStringLiteral("QTimeEdit"), SIGNAL(timeChanged(QTime)));
61 s_changedMap()->insert(QStringLiteral("QDateTimeEdit"), SIGNAL(dateTimeChanged(QDateTime)));
62 s_changedMap()->insert(QStringLiteral("QDial"), SIGNAL(valueChanged(int)));
63 s_changedMap()->insert(QStringLiteral("QDoubleSpinBox"), SIGNAL(valueChanged(double)));
64 s_changedMap()->insert(QStringLiteral("QLineEdit"), SIGNAL(textChanged(QString)));
65 s_changedMap()->insert(QStringLiteral("QSlider"), SIGNAL(valueChanged(int)));
66 s_changedMap()->insert(QStringLiteral("QSpinBox"), SIGNAL(valueChanged(int)));
67 s_changedMap()->insert(QStringLiteral("QTextEdit"), SIGNAL(textChanged()));
68 s_changedMap()->insert(QStringLiteral("QTextBrowser"), SIGNAL(sourceChanged(QString)));
69 s_changedMap()->insert(QStringLiteral("QPlainTextEdit"), SIGNAL(textChanged()));
70 s_changedMap()->insert(QStringLiteral("QTabWidget"), SIGNAL(currentChanged(int)));
71
72 // KDE
73 s_changedMap()->insert(QStringLiteral("KComboBox"), SIGNAL(activated(int)));
74 s_changedMap()->insert(QStringLiteral("KFontComboBox"), SIGNAL(activated(int)));
75 s_changedMap()->insert(QStringLiteral("KFontRequester"), SIGNAL(fontSelected(QFont)));
76 s_changedMap()->insert(QStringLiteral("KFontChooser"), SIGNAL(fontSelected(QFont)));
77 s_changedMap()->insert(QStringLiteral("KColorCombo"), SIGNAL(activated(QColor)));
78 s_changedMap()->insert(QStringLiteral("KColorButton"), SIGNAL(changed(QColor)));
79 s_changedMap()->insert(QStringLiteral("KDatePicker"), SIGNAL(dateSelected(QDate)));
80 s_changedMap()->insert(QStringLiteral("KDateWidget"), SIGNAL(changed(QDate)));
81 s_changedMap()->insert(QStringLiteral("KDateTimeWidget"), SIGNAL(valueChanged(QDateTime)));
82 s_changedMap()->insert(QStringLiteral("KEditListWidget"), SIGNAL(changed()));
83 s_changedMap()->insert(QStringLiteral("KListWidget"), SIGNAL(itemSelectionChanged()));
84 s_changedMap()->insert(QStringLiteral("KLineEdit"), SIGNAL(textChanged(QString)));
85 s_changedMap()->insert(QStringLiteral("KRestrictedLine"), SIGNAL(textChanged(QString)));
86 s_changedMap()->insert(QStringLiteral("KTextEdit"), SIGNAL(textChanged()));
87 s_changedMap()->insert(QStringLiteral("KUrlRequester"), SIGNAL(textChanged(QString)));
88 s_changedMap()->insert(QStringLiteral("KUrlComboRequester"), SIGNAL(textChanged(QString)));
89 s_changedMap()->insert(QStringLiteral("KUrlComboBox"), SIGNAL(urlActivated(QUrl)));
90 s_changedMap()->insert(QStringLiteral("KButtonGroup"), SIGNAL(changed(int)));
91 }
92}
93
94QHash<QString, QByteArray> *KConfigDialogManager::propertyMap()
95{
96 initMaps();
97 return s_propertyMap();
98}
99
100void KConfigDialogManager::init(bool trackChanges)
101{
102 initMaps();
103 d->trackChanges = trackChanges;
104
105 // Go through all of the children of the widgets and find all known widgets
106 (void)parseChildren(widget: d->m_dialog, trackChanges);
107}
108
109void KConfigDialogManager::addWidget(QWidget *widget)
110{
111 (void)parseChildren(widget, trackChanges: true);
112}
113
114void KConfigDialogManager::setupWidget(QWidget *widget, KConfigSkeletonItem *item)
115{
116 QVariant minValue = item->minValue();
117 if (minValue.isValid()) {
118 // KSelector is using this property
119 if (widget->metaObject()->indexOfProperty(name: "minValue") != -1) {
120 widget->setProperty(name: "minValue", value: minValue);
121 }
122 if (widget->metaObject()->indexOfProperty(name: "minimum") != -1) {
123 widget->setProperty(name: "minimum", value: minValue);
124 }
125 }
126 QVariant maxValue = item->maxValue();
127 if (maxValue.isValid()) {
128 // KSelector is using this property
129 if (widget->metaObject()->indexOfProperty(name: "maxValue") != -1) {
130 widget->setProperty(name: "maxValue", value: maxValue);
131 }
132 if (widget->metaObject()->indexOfProperty(name: "maximum") != -1) {
133 widget->setProperty(name: "maximum", value: maxValue);
134 }
135 }
136
137 if (widget->whatsThis().isEmpty()) {
138 QString whatsThis = item->whatsThis();
139 if (!whatsThis.isEmpty()) {
140 widget->setWhatsThis(whatsThis);
141 }
142 }
143
144 if (widget->toolTip().isEmpty()) {
145 QString toolTip = item->toolTip();
146 if (!toolTip.isEmpty()) {
147 widget->setToolTip(toolTip);
148 }
149 }
150
151 // If it is a QGroupBox with only autoExclusive buttons
152 // and has no custom property and the config item type
153 // is an integer, assume we want to save the index like we did with
154 // KButtonGroup instead of if it is checked or not
155 QGroupBox *gb = qobject_cast<QGroupBox *>(object: widget);
156 if (gb && getCustomProperty(widget: gb).isEmpty()) {
157 const KConfigSkeletonItem *item = d->m_conf->findItem(name: widget->objectName().mid(position: 5));
158 if (item->property().userType() == QMetaType::Int) {
159 QObjectList children = gb->children();
160 children.removeAll(t: gb->layout());
161 const QList<QAbstractButton *> buttons = gb->findChildren<QAbstractButton *>();
162 bool allAutoExclusiveDirectChildren = true;
163 for (QAbstractButton *button : buttons) {
164 allAutoExclusiveDirectChildren = allAutoExclusiveDirectChildren && button->autoExclusive() && button->parent() == gb;
165 }
166 if (allAutoExclusiveDirectChildren) {
167 d->allExclusiveGroupBoxes << widget;
168 }
169 }
170 }
171
172 if (!item->isEqual(p: property(w: widget))) {
173 setProperty(w: widget, v: item->property());
174 }
175
176 d->updateWidgetIndicator(configId: item->name(), widget);
177}
178
179bool KConfigDialogManager::parseChildren(const QWidget *widget, bool trackChanges)
180{
181 bool valueChanged = false;
182 const QList<QObject *> listOfChildren = widget->children();
183 if (listOfChildren.isEmpty()) { //?? XXX
184 return valueChanged;
185 }
186
187 const QMetaMethod onWidgetModifiedSlot = metaObject()->method(index: metaObject()->indexOfSlot(slot: "onWidgetModified()"));
188 Q_ASSERT(onWidgetModifiedSlot.isValid() && metaObject()->indexOfSlot("onWidgetModified()") >= 0);
189
190 for (QObject *object : listOfChildren) {
191 if (!object->isWidgetType()) {
192 continue; // Skip non-widgets
193 }
194
195 QWidget *childWidget = static_cast<QWidget *>(object);
196
197 QString widgetName = childWidget->objectName();
198 bool bParseChildren = true;
199 bool bSaveInsideGroupBox = d->insideGroupBox;
200
201 if (widgetName.startsWith(s: QLatin1String("kcfg_"))) {
202 // This is one of our widgets!
203 QString configId = widgetName.mid(position: 5);
204 KConfigSkeletonItem *item = d->m_conf->findItem(name: configId);
205 if (item) {
206 d->knownWidget.insert(key: configId, value: childWidget);
207
208 setupWidget(widget: childWidget, item);
209
210 if (trackChanges) {
211 bool changeSignalFound = false;
212
213 if (d->allExclusiveGroupBoxes.contains(value: childWidget)) {
214 const QList<QAbstractButton *> buttons = childWidget->findChildren<QAbstractButton *>();
215 for (QAbstractButton *button : buttons) {
216 connect(sender: button, signal: &QAbstractButton::toggled, context: this, slot: [this] {
217 d->onWidgetModified();
218 });
219 }
220 }
221
222 QByteArray propertyChangeSignal = getCustomPropertyChangedSignal(widget: childWidget);
223 if (propertyChangeSignal.isEmpty()) {
224 propertyChangeSignal = getUserPropertyChangedSignal(widget: childWidget);
225 }
226
227 if (propertyChangeSignal.isEmpty()) {
228 // get the change signal from the meta object
229 const QMetaObject *metaObject = childWidget->metaObject();
230 QByteArray userproperty = getCustomProperty(widget: childWidget);
231 if (userproperty.isEmpty()) {
232 userproperty = getUserProperty(widget: childWidget);
233 }
234 if (!userproperty.isEmpty()) {
235 const int indexOfProperty = metaObject->indexOfProperty(name: userproperty.constData());
236 if (indexOfProperty != -1) {
237 const QMetaProperty property = metaObject->property(index: indexOfProperty);
238 const QMetaMethod notifySignal = property.notifySignal();
239 if (notifySignal.isValid()) {
240 connect(sender: childWidget, signal: notifySignal, receiver: this, method: onWidgetModifiedSlot);
241 changeSignalFound = true;
242 }
243 }
244 } else {
245 qCWarning(KCONFIG_WIDGETS_LOG) << "Don't know how to monitor widget" << childWidget->metaObject()->className() << "for changes!";
246 }
247 } else {
248 connect(sender: childWidget, signal: propertyChangeSignal.constData(), receiver: this, SLOT(onWidgetModified()));
249 changeSignalFound = true;
250 }
251
252 if (changeSignalFound) {
253 QComboBox *cb = qobject_cast<QComboBox *>(object: childWidget);
254 if (cb && cb->isEditable()) {
255 connect(sender: cb, signal: &QComboBox::editTextChanged, context: this, slot: &KConfigDialogManager::widgetModified);
256 }
257 }
258 }
259 QGroupBox *gb = qobject_cast<QGroupBox *>(object: childWidget);
260 if (!gb) {
261 bParseChildren = false;
262 } else {
263 d->insideGroupBox = true;
264 }
265 } else {
266 qCWarning(KCONFIG_WIDGETS_LOG) << "A widget named" << widgetName << "was found but there is no setting named" << configId;
267 }
268 } else if (QLabel *label = qobject_cast<QLabel *>(object: childWidget)) {
269 QWidget *buddy = label->buddy();
270 if (!buddy) {
271 continue;
272 }
273 QString buddyName = buddy->objectName();
274 if (buddyName.startsWith(s: QLatin1String("kcfg_"))) {
275 // This is one of our widgets!
276 QString configId = buddyName.mid(position: 5);
277 d->buddyWidget.insert(key: configId, value: childWidget);
278 }
279 }
280 // kf5: commented out to reduce debug output
281 // #ifndef NDEBUG
282 // else if (!widgetName.isEmpty() && trackChanges)
283 // {
284 // QHash<QString, QByteArray>::const_iterator changedIt = s_changedMap()->constFind(childWidget->metaObject()->className());
285 // if (changedIt != s_changedMap()->constEnd())
286 // {
287 // if ((!d->insideGroupBox || !qobject_cast<QRadioButton*>(childWidget)) &&
288 // !qobject_cast<QGroupBox*>(childWidget) &&!qobject_cast<QTabWidget*>(childWidget) )
289 // qCDebug(KCONFIG_WIDGETS_LOG) << "Widget '" << widgetName << "' (" << childWidget->metaObject()->className() << ") remains unmanaged.";
290 // }
291 // }
292 // #endif
293
294 if (bParseChildren) {
295 // this widget is not known as something we can store.
296 // Maybe we can store one of its children.
297 valueChanged |= parseChildren(widget: childWidget, trackChanges);
298 }
299 d->insideGroupBox = bSaveInsideGroupBox;
300 }
301 return valueChanged;
302}
303
304void KConfigDialogManager::updateWidgets()
305{
306 bool changed = false;
307 bool bSignalsBlocked = signalsBlocked();
308 blockSignals(b: true);
309
310 QWidget *widget;
311 QHashIterator<QString, QWidget *> it(d->knownWidget);
312 while (it.hasNext()) {
313 it.next();
314 widget = it.value();
315
316 KConfigSkeletonItem *item = d->m_conf->findItem(name: it.key());
317 if (!item) {
318 qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
319 continue;
320 }
321
322 if (!item->isEqual(p: property(w: widget))) {
323 setProperty(w: widget, v: item->property());
324 // qCDebug(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "[" << widget->className() << "] has changed";
325 changed = true;
326 }
327 if (item->isImmutable()) {
328 widget->setEnabled(false);
329 QWidget *buddy = d->buddyWidget.value(key: it.key(), defaultValue: nullptr);
330 if (buddy) {
331 buddy->setEnabled(false);
332 }
333 }
334 }
335 blockSignals(b: bSignalsBlocked);
336
337 if (changed) {
338 QTimer::singleShot(interval: 0, receiver: this, slot: &KConfigDialogManager::widgetModified);
339 d->updateAllWidgetIndicators();
340 }
341}
342
343void KConfigDialogManager::updateWidgetsDefault()
344{
345 bool bUseDefaults = d->m_conf->useDefaults(b: true);
346 updateWidgets();
347 d->m_conf->useDefaults(b: bUseDefaults);
348 d->updateAllWidgetIndicators();
349}
350
351void KConfigDialogManager::setDefaultsIndicatorsVisible(bool enabled)
352{
353 d->setDefaultsIndicatorsVisible(enabled);
354}
355
356void KConfigDialogManager::updateSettings()
357{
358 bool changed = false;
359
360 QWidget *widget;
361 QHashIterator<QString, QWidget *> it(d->knownWidget);
362 while (it.hasNext()) {
363 it.next();
364 widget = it.value();
365
366 KConfigSkeletonItem *item = d->m_conf->findItem(name: it.key());
367 if (!item) {
368 qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
369 continue;
370 }
371
372 QVariant fromWidget = property(w: widget);
373 if (!item->isEqual(p: fromWidget)) {
374 item->setProperty(fromWidget);
375 changed = true;
376 }
377 }
378 if (changed) {
379 d->m_conf->save();
380 Q_EMIT settingsChanged();
381 d->updateAllWidgetIndicators();
382 }
383}
384
385QByteArray KConfigDialogManager::getUserProperty(const QWidget *widget) const
386{
387 MyHash *map = s_propertyMap();
388 const QMetaObject *metaObject = widget->metaObject();
389 const QString className(QLatin1String(metaObject->className()));
390 auto it = map->find(key: className);
391 if (it == map->end()) {
392 const QMetaProperty userProp = metaObject->userProperty();
393 if (userProp.isValid()) {
394 it = map->insert(key: className, value: userProp.name());
395 // qCDebug(KCONFIG_WIDGETS_LOG) << "class name: '" << className
396 //<< " 's USER property: " << metaProperty.name() << endl;
397 } else {
398 return QByteArray(); // no USER property
399 }
400 }
401
402 const QComboBox *cb = qobject_cast<const QComboBox *>(object: widget);
403 if (cb) {
404 const char *qcomboUserPropertyName = cb->QComboBox::metaObject()->userProperty().name();
405 const int qcomboUserPropertyIndex = qcomboUserPropertyName ? cb->QComboBox::metaObject()->indexOfProperty(name: qcomboUserPropertyName) : -1;
406 const char *widgetUserPropertyName = metaObject->userProperty().name();
407 const int widgetUserPropertyIndex = widgetUserPropertyName ? cb->metaObject()->indexOfProperty(name: widgetUserPropertyName) : -1;
408
409 // no custom user property set on subclass of QComboBox?
410 if (qcomboUserPropertyIndex == widgetUserPropertyIndex) {
411 return QByteArray(); // use the q/kcombobox special code
412 }
413 }
414
415 return it != map->end() ? it.value() : QByteArray{};
416}
417
418QByteArray KConfigDialogManager::getCustomProperty(const QWidget *widget) const
419{
420 QVariant prop(widget->property(name: "kcfg_property"));
421 if (prop.isValid()) {
422 if (!prop.canConvert<QByteArray>()) {
423 qCWarning(KCONFIG_WIDGETS_LOG) << "kcfg_property on" << widget->metaObject()->className() << "is not of type ByteArray";
424 } else {
425 return prop.toByteArray();
426 }
427 }
428 return QByteArray();
429}
430
431QByteArray KConfigDialogManager::getUserPropertyChangedSignal(const QWidget *widget) const
432{
433 const QString className = QLatin1String(widget->metaObject()->className());
434 auto changedIt = s_changedMap()->constFind(key: className);
435
436 if (changedIt == s_changedMap()->constEnd()) {
437 // If the class name of the widget wasn't in the monitored widgets map, then look for
438 // it again using the super class name. This fixes a problem with using QtRuby/Korundum
439 // widgets with KConfigXT where 'Qt::Widget' wasn't being seen a the real deal, even
440 // though it was a 'QWidget'.
441 if (widget->metaObject()->superClass()) {
442 const QString parentClassName = QLatin1String(widget->metaObject()->superClass()->className());
443 changedIt = s_changedMap()->constFind(key: parentClassName);
444 }
445 }
446
447 return (changedIt == s_changedMap()->constEnd()) ? QByteArray() : *changedIt;
448}
449
450QByteArray KConfigDialogManager::getCustomPropertyChangedSignal(const QWidget *widget) const
451{
452 QVariant prop(widget->property(name: "kcfg_propertyNotify"));
453 if (prop.isValid()) {
454 if (!prop.canConvert<QByteArray>()) {
455 qCWarning(KCONFIG_WIDGETS_LOG) << "kcfg_propertyNotify on" << widget->metaObject()->className() << "is not of type ByteArray";
456 } else {
457 return prop.toByteArray();
458 }
459 }
460 return QByteArray();
461}
462
463void KConfigDialogManager::setProperty(QWidget *w, const QVariant &v)
464{
465 if (d->allExclusiveGroupBoxes.contains(value: w)) {
466 const QList<QAbstractButton *> buttons = w->findChildren<QAbstractButton *>();
467 if (v.toInt() < buttons.count()) {
468 buttons[v.toInt()]->setChecked(true);
469 }
470 return;
471 }
472
473 QByteArray userproperty = getCustomProperty(widget: w);
474 if (userproperty.isEmpty()) {
475 userproperty = getUserProperty(widget: w);
476 }
477 if (userproperty.isEmpty()) {
478 QComboBox *cb = qobject_cast<QComboBox *>(object: w);
479 if (cb) {
480 if (cb->isEditable()) {
481 int i = cb->findText(text: v.toString());
482 if (i != -1) {
483 cb->setCurrentIndex(i);
484 } else {
485 cb->setEditText(v.toString());
486 }
487 } else {
488 cb->setCurrentIndex(v.toInt());
489 }
490 return;
491 }
492 }
493 if (userproperty.isEmpty()) {
494 qCWarning(KCONFIG_WIDGETS_LOG) << w->metaObject()->className() << "widget not handled!";
495 return;
496 }
497
498 w->setProperty(name: userproperty.constData(), value: v);
499}
500
501QVariant KConfigDialogManager::property(QWidget *w) const
502{
503 if (d->allExclusiveGroupBoxes.contains(value: w)) {
504 const QList<QAbstractButton *> buttons = w->findChildren<QAbstractButton *>();
505 for (int i = 0; i < buttons.count(); ++i) {
506 if (buttons[i]->isChecked()) {
507 return i;
508 }
509 }
510 return -1;
511 }
512
513 QByteArray userproperty = getCustomProperty(widget: w);
514 if (userproperty.isEmpty()) {
515 userproperty = getUserProperty(widget: w);
516 }
517 if (userproperty.isEmpty()) {
518 QComboBox *cb = qobject_cast<QComboBox *>(object: w);
519 if (cb) {
520 if (cb->isEditable()) {
521 return QVariant(cb->currentText());
522 } else {
523 return QVariant(cb->currentIndex());
524 }
525 }
526 }
527 if (userproperty.isEmpty()) {
528 qCWarning(KCONFIG_WIDGETS_LOG) << w->metaObject()->className() << "widget not handled!";
529 return QVariant();
530 }
531
532 return w->property(name: userproperty.constData());
533}
534
535bool KConfigDialogManager::hasChanged() const
536{
537 QWidget *widget;
538 QHashIterator<QString, QWidget *> it(d->knownWidget);
539 while (it.hasNext()) {
540 it.next();
541 widget = it.value();
542
543 KConfigSkeletonItem *item = d->m_conf->findItem(name: it.key());
544 if (!item) {
545 qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
546 continue;
547 }
548
549 if (!item->isEqual(p: property(w: widget))) {
550 // qCDebug(KCONFIG_WIDGETS_LOG) << "Widget for '" << it.key() << "' has changed.";
551 return true;
552 }
553 }
554 return false;
555}
556
557bool KConfigDialogManager::isDefault() const
558{
559 QWidget *widget;
560 QHashIterator<QString, QWidget *> it(d->knownWidget);
561 while (it.hasNext()) {
562 it.next();
563 widget = it.value();
564
565 KConfigSkeletonItem *item = d->m_conf->findItem(name: it.key());
566 if (!item) {
567 qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
568 continue;
569 }
570
571 if (property(w: widget) != item->getDefault()) {
572 return false;
573 }
574 }
575 return true;
576}
577
578KConfigDialogManagerPrivate::KConfigDialogManagerPrivate(KConfigDialogManager *qq)
579 : q(qq)
580 , insideGroupBox(false)
581 , defaultsIndicatorsVisible(false)
582{
583}
584
585void KConfigDialogManagerPrivate::setDefaultsIndicatorsVisible(bool enabled)
586{
587 if (defaultsIndicatorsVisible != enabled) {
588 defaultsIndicatorsVisible = enabled;
589 updateAllWidgetIndicators();
590 }
591}
592
593void KConfigDialogManagerPrivate::onWidgetModified()
594{
595 const auto widget = qobject_cast<QWidget *>(o: q->sender());
596 Q_ASSERT(widget);
597
598 const QLatin1String prefix("kcfg_");
599 QString configId = widget->objectName();
600 if (configId.startsWith(s: prefix)) {
601 configId.remove(i: 0, len: prefix.size());
602 updateWidgetIndicator(configId, widget);
603 } else {
604 auto *parent = qobject_cast<QWidget *>(o: widget->parent());
605 Q_ASSERT(parent);
606 configId = parent->objectName();
607 Q_ASSERT(configId.startsWith(prefix));
608 configId.remove(i: 0, len: prefix.size());
609 updateWidgetIndicator(configId, widget: parent);
610 }
611
612 Q_EMIT q->widgetModified();
613}
614
615void KConfigDialogManagerPrivate::updateWidgetIndicator(const QString &configId, QWidget *widget)
616{
617 const auto item = m_conf->findItem(name: configId);
618 Q_ASSERT(item);
619
620 const auto widgetValue = q->property(w: widget);
621 const auto defaultValue = item->getDefault();
622
623 const auto defaulted = widgetValue == defaultValue;
624
625 if (allExclusiveGroupBoxes.contains(value: widget)) {
626 const QList<QAbstractButton *> buttons = widget->findChildren<QAbstractButton *>();
627 for (int i = 0; i < buttons.count(); i++) {
628 const auto value = widgetValue.toInt() == i && !defaulted && defaultsIndicatorsVisible;
629 buttons.at(i)->setProperty(name: "_kde_highlight_neutral", value);
630 buttons.at(i)->update();
631 }
632 } else {
633 widget->setProperty(name: "_kde_highlight_neutral", value: !defaulted && defaultsIndicatorsVisible);
634 widget->update();
635 }
636}
637
638void KConfigDialogManagerPrivate::updateAllWidgetIndicators()
639{
640 QHashIterator<QString, QWidget *> it(knownWidget);
641 while (it.hasNext()) {
642 it.next();
643 updateWidgetIndicator(configId: it.key(), widget: it.value());
644 }
645}
646
647#include "moc_kconfigdialogmanager.cpp"
648

source code of kconfigwidgets/src/kconfigdialogmanager.cpp