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; |
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(trackChanges: true); |
38 | } |
39 | |
40 | KConfigDialogManager::~KConfigDialogManager() = default; |
41 | |
42 | // KF6: Drop this and get signals only from metaObject and/or widget's dynamic properties kcfg_property/kcfg_propertyNotify |
43 | void 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 | |
94 | QHash<QString, QByteArray> *KConfigDialogManager::propertyMap() |
95 | { |
96 | initMaps(); |
97 | return s_propertyMap(); |
98 | } |
99 | |
100 | void 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 | |
109 | void KConfigDialogManager::addWidget(QWidget *widget) |
110 | { |
111 | (void)parseChildren(widget, trackChanges: true); |
112 | } |
113 | |
114 | void 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 | |
179 | bool 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 | |
304 | void 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 | |
343 | void 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 | |
351 | void KConfigDialogManager::setDefaultsIndicatorsVisible(bool enabled) |
352 | { |
353 | d->setDefaultsIndicatorsVisible(enabled); |
354 | } |
355 | |
356 | void 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 | |
385 | QByteArray 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 | |
418 | QByteArray 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 | |
431 | QByteArray 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 | |
450 | QByteArray 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 | |
463 | void 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 | |
501 | QVariant 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 | |
535 | bool 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 | |
557 | bool 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 | |
578 | KConfigDialogManagerPrivate::KConfigDialogManagerPrivate(KConfigDialogManager *qq) |
579 | : q(qq) |
580 | , insideGroupBox(false) |
581 | , defaultsIndicatorsVisible(false) |
582 | { |
583 | } |
584 | |
585 | void KConfigDialogManagerPrivate::setDefaultsIndicatorsVisible(bool enabled) |
586 | { |
587 | if (defaultsIndicatorsVisible != enabled) { |
588 | defaultsIndicatorsVisible = enabled; |
589 | updateAllWidgetIndicators(); |
590 | } |
591 | } |
592 | |
593 | void 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 | |
615 | void 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 | |
638 | void 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 | |