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