File indexing completed on 2024-04-21 14:54:19

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