File indexing completed on 2024-04-21 03:53:21

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2003 Benjamin C Meyer <ben+kdelibs at meyerhome dot net>
0004     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
0005     SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
0006     SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
0007     SPDX-FileCopyrightText: 2020 Cyril Rossi <cyril.rossi@enioka.com>
0008 
0009     SPDX-License-Identifier: LGPL-2.0-or-later
0010 */
0011 
0012 #include "kconfigdialogmanager.h"
0013 #include "kconfigdialogmanager_p.h"
0014 #include "kconfigwidgets_debug.h"
0015 
0016 #include <QComboBox>
0017 #include <QGroupBox>
0018 #include <QLabel>
0019 #include <QLayout>
0020 #include <QMetaObject>
0021 #include <QMetaProperty>
0022 #include <QRadioButton>
0023 #include <QTimer>
0024 
0025 #include <KConfigSkeleton>
0026 
0027 typedef QHash<QString, QByteArray> MyHash;
0028 Q_GLOBAL_STATIC(MyHash, s_propertyMap)
0029 Q_GLOBAL_STATIC(MyHash, s_changedMap)
0030 
0031 KConfigDialogManager::KConfigDialogManager(QWidget *parent, KCoreConfigSkeleton *conf)
0032     : QObject(parent)
0033     , d(new KConfigDialogManagerPrivate(this))
0034 {
0035     d->m_conf = conf;
0036     d->m_dialog = parent;
0037     init(true);
0038 }
0039 
0040 KConfigDialogManager::~KConfigDialogManager() = default;
0041 
0042 // KF6: Drop this and get signals only from metaObject and/or widget's dynamic properties kcfg_property/kcfg_propertyNotify
0043 void KConfigDialogManager::initMaps()
0044 {
0045     if (s_propertyMap()->isEmpty()) {
0046         s_propertyMap()->insert(QStringLiteral("KButtonGroup"), "current");
0047         s_propertyMap()->insert(QStringLiteral("KColorButton"), "color");
0048         s_propertyMap()->insert(QStringLiteral("KColorCombo"), "color");
0049         s_propertyMap()->insert(QStringLiteral("KKeySequenceWidget"), "keySequence");
0050     }
0051 
0052     if (s_changedMap()->isEmpty()) {
0053         // QT
0054         s_changedMap()->insert(QStringLiteral("QCheckBox"), SIGNAL(stateChanged(int)));
0055         s_changedMap()->insert(QStringLiteral("QPushButton"), SIGNAL(clicked(bool)));
0056         s_changedMap()->insert(QStringLiteral("QRadioButton"), SIGNAL(toggled(bool)));
0057         s_changedMap()->insert(QStringLiteral("QGroupBox"), SIGNAL(toggled(bool)));
0058         s_changedMap()->insert(QStringLiteral("QComboBox"), SIGNAL(activated(int)));
0059         s_changedMap()->insert(QStringLiteral("QDateEdit"), SIGNAL(dateChanged(QDate)));
0060         s_changedMap()->insert(QStringLiteral("QTimeEdit"), SIGNAL(timeChanged(QTime)));
0061         s_changedMap()->insert(QStringLiteral("QDateTimeEdit"), SIGNAL(dateTimeChanged(QDateTime)));
0062         s_changedMap()->insert(QStringLiteral("QDial"), SIGNAL(valueChanged(int)));
0063         s_changedMap()->insert(QStringLiteral("QDoubleSpinBox"), SIGNAL(valueChanged(double)));
0064         s_changedMap()->insert(QStringLiteral("QLineEdit"), SIGNAL(textChanged(QString)));
0065         s_changedMap()->insert(QStringLiteral("QSlider"), SIGNAL(valueChanged(int)));
0066         s_changedMap()->insert(QStringLiteral("QSpinBox"), SIGNAL(valueChanged(int)));
0067         s_changedMap()->insert(QStringLiteral("QTextEdit"), SIGNAL(textChanged()));
0068         s_changedMap()->insert(QStringLiteral("QTextBrowser"), SIGNAL(sourceChanged(QString)));
0069         s_changedMap()->insert(QStringLiteral("QPlainTextEdit"), SIGNAL(textChanged()));
0070         s_changedMap()->insert(QStringLiteral("QTabWidget"), SIGNAL(currentChanged(int)));
0071 
0072         // KDE
0073         s_changedMap()->insert(QStringLiteral("KComboBox"), SIGNAL(activated(int)));
0074         s_changedMap()->insert(QStringLiteral("KFontComboBox"), SIGNAL(activated(int)));
0075         s_changedMap()->insert(QStringLiteral("KFontRequester"), SIGNAL(fontSelected(QFont)));
0076         s_changedMap()->insert(QStringLiteral("KFontChooser"), SIGNAL(fontSelected(QFont)));
0077         s_changedMap()->insert(QStringLiteral("KColorCombo"), SIGNAL(activated(QColor)));
0078         s_changedMap()->insert(QStringLiteral("KColorButton"), SIGNAL(changed(QColor)));
0079         s_changedMap()->insert(QStringLiteral("KDatePicker"), SIGNAL(dateSelected(QDate)));
0080         s_changedMap()->insert(QStringLiteral("KDateWidget"), SIGNAL(changed(QDate)));
0081         s_changedMap()->insert(QStringLiteral("KDateTimeWidget"), SIGNAL(valueChanged(QDateTime)));
0082         s_changedMap()->insert(QStringLiteral("KEditListWidget"), SIGNAL(changed()));
0083         s_changedMap()->insert(QStringLiteral("KListWidget"), SIGNAL(itemSelectionChanged()));
0084         s_changedMap()->insert(QStringLiteral("KLineEdit"), SIGNAL(textChanged(QString)));
0085         s_changedMap()->insert(QStringLiteral("KRestrictedLine"), SIGNAL(textChanged(QString)));
0086         s_changedMap()->insert(QStringLiteral("KTextEdit"), SIGNAL(textChanged()));
0087         s_changedMap()->insert(QStringLiteral("KUrlRequester"), SIGNAL(textChanged(QString)));
0088         s_changedMap()->insert(QStringLiteral("KUrlComboRequester"), SIGNAL(textChanged(QString)));
0089         s_changedMap()->insert(QStringLiteral("KUrlComboBox"), SIGNAL(urlActivated(QUrl)));
0090         s_changedMap()->insert(QStringLiteral("KButtonGroup"), SIGNAL(changed(int)));
0091     }
0092 }
0093 
0094 QHash<QString, QByteArray> *KConfigDialogManager::propertyMap()
0095 {
0096     initMaps();
0097     return s_propertyMap();
0098 }
0099 
0100 void KConfigDialogManager::init(bool trackChanges)
0101 {
0102     initMaps();
0103     d->trackChanges = trackChanges;
0104 
0105     // Go through all of the children of the widgets and find all known widgets
0106     (void)parseChildren(d->m_dialog, trackChanges);
0107 }
0108 
0109 void KConfigDialogManager::addWidget(QWidget *widget)
0110 {
0111     (void)parseChildren(widget, true);
0112 }
0113 
0114 void KConfigDialogManager::setupWidget(QWidget *widget, KConfigSkeletonItem *item)
0115 {
0116     QVariant minValue = item->minValue();
0117     if (minValue.isValid()) {
0118         // KSelector is using this property
0119         if (widget->metaObject()->indexOfProperty("minValue") != -1) {
0120             widget->setProperty("minValue", minValue);
0121         }
0122         if (widget->metaObject()->indexOfProperty("minimum") != -1) {
0123             widget->setProperty("minimum", minValue);
0124         }
0125     }
0126     QVariant maxValue = item->maxValue();
0127     if (maxValue.isValid()) {
0128         // KSelector is using this property
0129         if (widget->metaObject()->indexOfProperty("maxValue") != -1) {
0130             widget->setProperty("maxValue", maxValue);
0131         }
0132         if (widget->metaObject()->indexOfProperty("maximum") != -1) {
0133             widget->setProperty("maximum", maxValue);
0134         }
0135     }
0136 
0137     if (widget->whatsThis().isEmpty()) {
0138         QString whatsThis = item->whatsThis();
0139         if (!whatsThis.isEmpty()) {
0140             widget->setWhatsThis(whatsThis);
0141         }
0142     }
0143 
0144     if (widget->toolTip().isEmpty()) {
0145         QString toolTip = item->toolTip();
0146         if (!toolTip.isEmpty()) {
0147             widget->setToolTip(toolTip);
0148         }
0149     }
0150 
0151     // If it is a QGroupBox with only autoExclusive buttons
0152     // and has no custom property and the config item type
0153     // is an integer, assume we want to save the index like we did with
0154     // KButtonGroup instead of if it is checked or not
0155     QGroupBox *gb = qobject_cast<QGroupBox *>(widget);
0156     if (gb && getCustomProperty(gb).isEmpty()) {
0157         const KConfigSkeletonItem *item = d->m_conf->findItem(widget->objectName().mid(5));
0158         if (item->property().userType() == QMetaType::Int) {
0159             QObjectList children = gb->children();
0160             children.removeAll(gb->layout());
0161             const QList<QAbstractButton *> buttons = gb->findChildren<QAbstractButton *>();
0162             bool allAutoExclusiveDirectChildren = true;
0163             for (QAbstractButton *button : buttons) {
0164                 allAutoExclusiveDirectChildren = allAutoExclusiveDirectChildren && button->autoExclusive() && button->parent() == gb;
0165             }
0166             if (allAutoExclusiveDirectChildren) {
0167                 d->allExclusiveGroupBoxes << widget;
0168             }
0169         }
0170     }
0171 
0172     if (!item->isEqual(property(widget))) {
0173         setProperty(widget, item->property());
0174     }
0175 
0176     d->updateWidgetIndicator(item->name(), widget);
0177 }
0178 
0179 bool KConfigDialogManager::parseChildren(const QWidget *widget, bool trackChanges)
0180 {
0181     bool valueChanged = false;
0182     const QList<QObject *> listOfChildren = widget->children();
0183     if (listOfChildren.isEmpty()) { //?? XXX
0184         return valueChanged;
0185     }
0186 
0187     const QMetaMethod onWidgetModifiedSlot = metaObject()->method(metaObject()->indexOfSlot("onWidgetModified()"));
0188     Q_ASSERT(onWidgetModifiedSlot.isValid() && metaObject()->indexOfSlot("onWidgetModified()") >= 0);
0189 
0190     for (QObject *object : listOfChildren) {
0191         if (!object->isWidgetType()) {
0192             continue; // Skip non-widgets
0193         }
0194 
0195         QWidget *childWidget = static_cast<QWidget *>(object);
0196 
0197         QString widgetName = childWidget->objectName();
0198         bool bParseChildren = true;
0199         bool bSaveInsideGroupBox = d->insideGroupBox;
0200 
0201         if (widgetName.startsWith(QLatin1String("kcfg_"))) {
0202             // This is one of our widgets!
0203             QString configId = widgetName.mid(5);
0204             KConfigSkeletonItem *item = d->m_conf->findItem(configId);
0205             if (item) {
0206                 d->knownWidget.insert(configId, childWidget);
0207 
0208                 setupWidget(childWidget, item);
0209 
0210                 if (trackChanges) {
0211                     bool changeSignalFound = false;
0212 
0213                     if (d->allExclusiveGroupBoxes.contains(childWidget)) {
0214                         const QList<QAbstractButton *> buttons = childWidget->findChildren<QAbstractButton *>();
0215                         for (QAbstractButton *button : buttons) {
0216                             connect(button, &QAbstractButton::toggled, this, [this] {
0217                                 d->onWidgetModified();
0218                             });
0219                         }
0220                     }
0221 
0222                     QByteArray propertyChangeSignal = getCustomPropertyChangedSignal(childWidget);
0223                     if (propertyChangeSignal.isEmpty()) {
0224                         propertyChangeSignal = getUserPropertyChangedSignal(childWidget);
0225                     }
0226 
0227                     if (propertyChangeSignal.isEmpty()) {
0228                         // get the change signal from the meta object
0229                         const QMetaObject *metaObject = childWidget->metaObject();
0230                         QByteArray userproperty = getCustomProperty(childWidget);
0231                         if (userproperty.isEmpty()) {
0232                             userproperty = getUserProperty(childWidget);
0233                         }
0234                         if (!userproperty.isEmpty()) {
0235                             const int indexOfProperty = metaObject->indexOfProperty(userproperty.constData());
0236                             if (indexOfProperty != -1) {
0237                                 const QMetaProperty property = metaObject->property(indexOfProperty);
0238                                 const QMetaMethod notifySignal = property.notifySignal();
0239                                 if (notifySignal.isValid()) {
0240                                     connect(childWidget, notifySignal, this, onWidgetModifiedSlot);
0241                                     changeSignalFound = true;
0242                                 }
0243                             }
0244                         } else {
0245                             qCWarning(KCONFIG_WIDGETS_LOG) << "Don't know how to monitor widget" << childWidget->metaObject()->className() << "for changes!";
0246                         }
0247                     } else {
0248                         connect(childWidget, propertyChangeSignal.constData(), this, SLOT(onWidgetModified()));
0249                         changeSignalFound = true;
0250                     }
0251 
0252                     if (changeSignalFound) {
0253                         QComboBox *cb = qobject_cast<QComboBox *>(childWidget);
0254                         if (cb && cb->isEditable()) {
0255                             connect(cb, &QComboBox::editTextChanged, this, &KConfigDialogManager::widgetModified);
0256                         }
0257                     }
0258                 }
0259                 QGroupBox *gb = qobject_cast<QGroupBox *>(childWidget);
0260                 if (!gb) {
0261                     bParseChildren = false;
0262                 } else {
0263                     d->insideGroupBox = true;
0264                 }
0265             } else {
0266                 qCWarning(KCONFIG_WIDGETS_LOG) << "A widget named" << widgetName << "was found but there is no setting named" << configId;
0267             }
0268         } else if (QLabel *label = qobject_cast<QLabel *>(childWidget)) {
0269             QWidget *buddy = label->buddy();
0270             if (!buddy) {
0271                 continue;
0272             }
0273             QString buddyName = buddy->objectName();
0274             if (buddyName.startsWith(QLatin1String("kcfg_"))) {
0275                 // This is one of our widgets!
0276                 QString configId = buddyName.mid(5);
0277                 d->buddyWidget.insert(configId, childWidget);
0278             }
0279         }
0280         // kf5: commented out to reduce debug output
0281         // #ifndef NDEBUG
0282         //     else if (!widgetName.isEmpty() && trackChanges)
0283         //     {
0284         //       QHash<QString, QByteArray>::const_iterator changedIt = s_changedMap()->constFind(childWidget->metaObject()->className());
0285         //       if (changedIt != s_changedMap()->constEnd())
0286         //       {
0287         //         if ((!d->insideGroupBox || !qobject_cast<QRadioButton*>(childWidget)) &&
0288         //             !qobject_cast<QGroupBox*>(childWidget) &&!qobject_cast<QTabWidget*>(childWidget) )
0289         //           qCDebug(KCONFIG_WIDGETS_LOG) << "Widget '" << widgetName << "' (" << childWidget->metaObject()->className() << ") remains unmanaged.";
0290         //       }
0291         //     }
0292         // #endif
0293 
0294         if (bParseChildren) {
0295             // this widget is not known as something we can store.
0296             // Maybe we can store one of its children.
0297             valueChanged |= parseChildren(childWidget, trackChanges);
0298         }
0299         d->insideGroupBox = bSaveInsideGroupBox;
0300     }
0301     return valueChanged;
0302 }
0303 
0304 void KConfigDialogManager::updateWidgets()
0305 {
0306     bool changed = false;
0307     bool bSignalsBlocked = signalsBlocked();
0308     blockSignals(true);
0309 
0310     QWidget *widget;
0311     QHashIterator<QString, QWidget *> it(d->knownWidget);
0312     while (it.hasNext()) {
0313         it.next();
0314         widget = it.value();
0315 
0316         KConfigSkeletonItem *item = d->m_conf->findItem(it.key());
0317         if (!item) {
0318             qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
0319             continue;
0320         }
0321 
0322         if (!item->isEqual(property(widget))) {
0323             setProperty(widget, item->property());
0324             // qCDebug(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "[" << widget->className() << "] has changed";
0325             changed = true;
0326         }
0327         if (item->isImmutable()) {
0328             widget->setEnabled(false);
0329             QWidget *buddy = d->buddyWidget.value(it.key(), nullptr);
0330             if (buddy) {
0331                 buddy->setEnabled(false);
0332             }
0333         }
0334     }
0335     blockSignals(bSignalsBlocked);
0336 
0337     if (changed) {
0338         QTimer::singleShot(0, this, &KConfigDialogManager::widgetModified);
0339         d->updateAllWidgetIndicators();
0340     }
0341 }
0342 
0343 void KConfigDialogManager::updateWidgetsDefault()
0344 {
0345     bool bUseDefaults = d->m_conf->useDefaults(true);
0346     updateWidgets();
0347     d->m_conf->useDefaults(bUseDefaults);
0348     d->updateAllWidgetIndicators();
0349 }
0350 
0351 void KConfigDialogManager::setDefaultsIndicatorsVisible(bool enabled)
0352 {
0353     d->setDefaultsIndicatorsVisible(enabled);
0354 }
0355 
0356 void KConfigDialogManager::updateSettings()
0357 {
0358     bool changed = false;
0359 
0360     QWidget *widget;
0361     QHashIterator<QString, QWidget *> it(d->knownWidget);
0362     while (it.hasNext()) {
0363         it.next();
0364         widget = it.value();
0365 
0366         KConfigSkeletonItem *item = d->m_conf->findItem(it.key());
0367         if (!item) {
0368             qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
0369             continue;
0370         }
0371 
0372         QVariant fromWidget = property(widget);
0373         if (!item->isEqual(fromWidget)) {
0374             item->setProperty(fromWidget);
0375             changed = true;
0376         }
0377     }
0378     if (changed) {
0379         d->m_conf->save();
0380         Q_EMIT settingsChanged();
0381         d->updateAllWidgetIndicators();
0382     }
0383 }
0384 
0385 QByteArray KConfigDialogManager::getUserProperty(const QWidget *widget) const
0386 {
0387     MyHash *map = s_propertyMap();
0388     const QMetaObject *metaObject = widget->metaObject();
0389     const QString className(QLatin1String(metaObject->className()));
0390     auto it = map->find(className);
0391     if (it == map->end()) {
0392         const QMetaProperty userProp = metaObject->userProperty();
0393         if (userProp.isValid()) {
0394             it = map->insert(className, userProp.name());
0395             // qCDebug(KCONFIG_WIDGETS_LOG) << "class name: '" << className
0396             //<< " 's USER property: " << metaProperty.name() << endl;
0397         } else {
0398             return QByteArray(); // no USER property
0399         }
0400     }
0401 
0402     const QComboBox *cb = qobject_cast<const QComboBox *>(widget);
0403     if (cb) {
0404         const char *qcomboUserPropertyName = cb->QComboBox::metaObject()->userProperty().name();
0405         const int qcomboUserPropertyIndex = qcomboUserPropertyName ? cb->QComboBox::metaObject()->indexOfProperty(qcomboUserPropertyName) : -1;
0406         const char *widgetUserPropertyName = metaObject->userProperty().name();
0407         const int widgetUserPropertyIndex = widgetUserPropertyName ? cb->metaObject()->indexOfProperty(widgetUserPropertyName) : -1;
0408 
0409         // no custom user property set on subclass of QComboBox?
0410         if (qcomboUserPropertyIndex == widgetUserPropertyIndex) {
0411             return QByteArray(); // use the q/kcombobox special code
0412         }
0413     }
0414 
0415     return it != map->end() ? it.value() : QByteArray{};
0416 }
0417 
0418 QByteArray KConfigDialogManager::getCustomProperty(const QWidget *widget) const
0419 {
0420     QVariant prop(widget->property("kcfg_property"));
0421     if (prop.isValid()) {
0422         if (!prop.canConvert<QByteArray>()) {
0423             qCWarning(KCONFIG_WIDGETS_LOG) << "kcfg_property on" << widget->metaObject()->className() << "is not of type ByteArray";
0424         } else {
0425             return prop.toByteArray();
0426         }
0427     }
0428     return QByteArray();
0429 }
0430 
0431 QByteArray KConfigDialogManager::getUserPropertyChangedSignal(const QWidget *widget) const
0432 {
0433     const QString className = QLatin1String(widget->metaObject()->className());
0434     auto changedIt = s_changedMap()->constFind(className);
0435 
0436     if (changedIt == s_changedMap()->constEnd()) {
0437         // If the class name of the widget wasn't in the monitored widgets map, then look for
0438         // it again using the super class name. This fixes a problem with using QtRuby/Korundum
0439         // widgets with KConfigXT where 'Qt::Widget' wasn't being seen a the real deal, even
0440         // though it was a 'QWidget'.
0441         if (widget->metaObject()->superClass()) {
0442             const QString parentClassName = QLatin1String(widget->metaObject()->superClass()->className());
0443             changedIt = s_changedMap()->constFind(parentClassName);
0444         }
0445     }
0446 
0447     return (changedIt == s_changedMap()->constEnd()) ? QByteArray() : *changedIt;
0448 }
0449 
0450 QByteArray KConfigDialogManager::getCustomPropertyChangedSignal(const QWidget *widget) const
0451 {
0452     QVariant prop(widget->property("kcfg_propertyNotify"));
0453     if (prop.isValid()) {
0454         if (!prop.canConvert<QByteArray>()) {
0455             qCWarning(KCONFIG_WIDGETS_LOG) << "kcfg_propertyNotify on" << widget->metaObject()->className() << "is not of type ByteArray";
0456         } else {
0457             return prop.toByteArray();
0458         }
0459     }
0460     return QByteArray();
0461 }
0462 
0463 void KConfigDialogManager::setProperty(QWidget *w, const QVariant &v)
0464 {
0465     if (d->allExclusiveGroupBoxes.contains(w)) {
0466         const QList<QAbstractButton *> buttons = w->findChildren<QAbstractButton *>();
0467         if (v.toInt() < buttons.count()) {
0468             buttons[v.toInt()]->setChecked(true);
0469         }
0470         return;
0471     }
0472 
0473     QByteArray userproperty = getCustomProperty(w);
0474     if (userproperty.isEmpty()) {
0475         userproperty = getUserProperty(w);
0476     }
0477     if (userproperty.isEmpty()) {
0478         QComboBox *cb = qobject_cast<QComboBox *>(w);
0479         if (cb) {
0480             if (cb->isEditable()) {
0481                 int i = cb->findText(v.toString());
0482                 if (i != -1) {
0483                     cb->setCurrentIndex(i);
0484                 } else {
0485                     cb->setEditText(v.toString());
0486                 }
0487             } else {
0488                 cb->setCurrentIndex(v.toInt());
0489             }
0490             return;
0491         }
0492     }
0493     if (userproperty.isEmpty()) {
0494         qCWarning(KCONFIG_WIDGETS_LOG) << w->metaObject()->className() << "widget not handled!";
0495         return;
0496     }
0497 
0498     w->setProperty(userproperty.constData(), v);
0499 }
0500 
0501 QVariant KConfigDialogManager::property(QWidget *w) const
0502 {
0503     if (d->allExclusiveGroupBoxes.contains(w)) {
0504         const QList<QAbstractButton *> buttons = w->findChildren<QAbstractButton *>();
0505         for (int i = 0; i < buttons.count(); ++i) {
0506             if (buttons[i]->isChecked()) {
0507                 return i;
0508             }
0509         }
0510         return -1;
0511     }
0512 
0513     QByteArray userproperty = getCustomProperty(w);
0514     if (userproperty.isEmpty()) {
0515         userproperty = getUserProperty(w);
0516     }
0517     if (userproperty.isEmpty()) {
0518         QComboBox *cb = qobject_cast<QComboBox *>(w);
0519         if (cb) {
0520             if (cb->isEditable()) {
0521                 return QVariant(cb->currentText());
0522             } else {
0523                 return QVariant(cb->currentIndex());
0524             }
0525         }
0526     }
0527     if (userproperty.isEmpty()) {
0528         qCWarning(KCONFIG_WIDGETS_LOG) << w->metaObject()->className() << "widget not handled!";
0529         return QVariant();
0530     }
0531 
0532     return w->property(userproperty.constData());
0533 }
0534 
0535 bool KConfigDialogManager::hasChanged() const
0536 {
0537     QWidget *widget;
0538     QHashIterator<QString, QWidget *> it(d->knownWidget);
0539     while (it.hasNext()) {
0540         it.next();
0541         widget = it.value();
0542 
0543         KConfigSkeletonItem *item = d->m_conf->findItem(it.key());
0544         if (!item) {
0545             qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
0546             continue;
0547         }
0548 
0549         if (!item->isEqual(property(widget))) {
0550             // qCDebug(KCONFIG_WIDGETS_LOG) << "Widget for '" << it.key() << "' has changed.";
0551             return true;
0552         }
0553     }
0554     return false;
0555 }
0556 
0557 bool KConfigDialogManager::isDefault() const
0558 {
0559     QWidget *widget;
0560     QHashIterator<QString, QWidget *> it(d->knownWidget);
0561     while (it.hasNext()) {
0562         it.next();
0563         widget = it.value();
0564 
0565         KConfigSkeletonItem *item = d->m_conf->findItem(it.key());
0566         if (!item) {
0567             qCWarning(KCONFIG_WIDGETS_LOG) << "The setting" << it.key() << "has disappeared!";
0568             continue;
0569         }
0570 
0571         if (property(widget) != item->getDefault()) {
0572             return false;
0573         }
0574     }
0575     return true;
0576 }
0577 
0578 KConfigDialogManagerPrivate::KConfigDialogManagerPrivate(KConfigDialogManager *qq)
0579     : q(qq)
0580     , insideGroupBox(false)
0581     , defaultsIndicatorsVisible(false)
0582 {
0583 }
0584 
0585 void KConfigDialogManagerPrivate::setDefaultsIndicatorsVisible(bool enabled)
0586 {
0587     if (defaultsIndicatorsVisible != enabled) {
0588         defaultsIndicatorsVisible = enabled;
0589         updateAllWidgetIndicators();
0590     }
0591 }
0592 
0593 void KConfigDialogManagerPrivate::onWidgetModified()
0594 {
0595     const auto widget = qobject_cast<QWidget *>(q->sender());
0596     Q_ASSERT(widget);
0597 
0598     const QLatin1String prefix("kcfg_");
0599     QString configId = widget->objectName();
0600     if (configId.startsWith(prefix)) {
0601         configId.remove(0, prefix.size());
0602         updateWidgetIndicator(configId, widget);
0603     } else {
0604         auto *parent = qobject_cast<QWidget *>(widget->parent());
0605         Q_ASSERT(parent);
0606         configId = parent->objectName();
0607         Q_ASSERT(configId.startsWith(prefix));
0608         configId.remove(0, prefix.size());
0609         updateWidgetIndicator(configId, parent);
0610     }
0611 
0612     Q_EMIT q->widgetModified();
0613 }
0614 
0615 void KConfigDialogManagerPrivate::updateWidgetIndicator(const QString &configId, QWidget *widget)
0616 {
0617     const auto item = m_conf->findItem(configId);
0618     Q_ASSERT(item);
0619 
0620     const auto widgetValue = q->property(widget);
0621     const auto defaultValue = item->getDefault();
0622 
0623     const auto defaulted = widgetValue == defaultValue;
0624 
0625     if (allExclusiveGroupBoxes.contains(widget)) {
0626         const QList<QAbstractButton *> buttons = widget->findChildren<QAbstractButton *>();
0627         for (int i = 0; i < buttons.count(); i++) {
0628             const auto value = widgetValue.toInt() == i && !defaulted && defaultsIndicatorsVisible;
0629             buttons.at(i)->setProperty("_kde_highlight_neutral", value);
0630             buttons.at(i)->update();
0631         }
0632     } else {
0633         widget->setProperty("_kde_highlight_neutral", !defaulted && defaultsIndicatorsVisible);
0634         widget->update();
0635     }
0636 }
0637 
0638 void KConfigDialogManagerPrivate::updateAllWidgetIndicators()
0639 {
0640     QHashIterator<QString, QWidget *> it(knownWidget);
0641     while (it.hasNext()) {
0642         it.next();
0643         updateWidgetIndicator(it.key(), it.value());
0644     }
0645 }
0646 
0647 #include "moc_kconfigdialogmanager.cpp"