Changeset View
Changeset View
Standalone View
Standalone View
src/kconfigdialogmanager.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * This file is part of the KDE libraries | 2 | * This file is part of the KDE libraries | ||
3 | * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net) | 3 | * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net) | ||
4 | * Copyright (C) 2003 Waldo Bastian <bastian@kde.org> | 4 | * Copyright (C) 2003 Waldo Bastian <bastian@kde.org> | ||
5 | * Copyright (C) 2017 Friedrich W. H. Kossebau <kossebau@kde.org> | 5 | * Copyright (C) 2017 Friedrich W. H. Kossebau <kossebau@kde.org> | ||
6 | * Copyright (C) 2020 Kevin Ottens <kevin.ottens@enioka.com> | ||||
6 | * | 7 | * | ||
7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Library General Public | 9 | * modify it under the terms of the GNU Library General Public | ||
9 | * License as published by the Free Software Foundation; either | 10 | * License as published by the Free Software Foundation; either | ||
10 | * version 2 of the License, or (at your option) any later version. | 11 | * version 2 of the License, or (at your option) any later version. | ||
11 | * | 12 | * | ||
12 | * This library is distributed in the hope that it will be useful, | 13 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Library General Public License for more details. | 16 | * Library General Public License for more details. | ||
16 | * | 17 | * | ||
17 | * You should have received a copy of the GNU Library General Public License | 18 | * You should have received a copy of the GNU Library General Public License | ||
18 | * along with this library; see the file COPYING.LIB. If not, write to | 19 | * along with this library; see the file COPYING.LIB. If not, write to | ||
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
20 | * Boston, MA 02110-1301, USA. | 21 | * Boston, MA 02110-1301, USA. | ||
21 | */ | 22 | */ | ||
22 | 23 | | |||
23 | #include "kconfigdialogmanager.h" | 24 | #include "kconfigdialogmanager.h" | ||
25 | #include "kconfigdialogmanager_p.h" | ||||
24 | #include "kconfigwidgets_debug.h" | 26 | #include "kconfigwidgets_debug.h" | ||
25 | 27 | | |||
28 | #include "settingsstatusindicator_p.h" | ||||
29 | | ||||
26 | #include <QComboBox> | 30 | #include <QComboBox> | ||
27 | #include <QGroupBox> | 31 | #include <QGroupBox> | ||
28 | #include <QLabel> | 32 | #include <QLabel> | ||
29 | #include <QMetaObject> | 33 | #include <QMetaObject> | ||
30 | #include <QMetaProperty> | 34 | #include <QMetaProperty> | ||
31 | #include <QTimer> | 35 | #include <QTimer> | ||
32 | #include <QRadioButton> | 36 | #include <QRadioButton> | ||
33 | #include <QLayout> | 37 | #include <QLayout> | ||
34 | 38 | | |||
35 | #include <kconfigskeleton.h> | 39 | #include <kconfigskeleton.h> | ||
36 | 40 | | |||
37 | typedef QHash<QString, QByteArray> MyHash; | 41 | typedef QHash<QString, QByteArray> MyHash; | ||
38 | Q_GLOBAL_STATIC(MyHash, s_propertyMap) | 42 | Q_GLOBAL_STATIC(MyHash, s_propertyMap) | ||
39 | Q_GLOBAL_STATIC(MyHash, s_changedMap) | 43 | Q_GLOBAL_STATIC(MyHash, s_changedMap) | ||
40 | 44 | | |||
41 | class KConfigDialogManagerPrivate | | |||
42 | { | | |||
43 | | ||||
44 | public: | | |||
45 | KConfigDialogManagerPrivate(KConfigDialogManager *q) : q(q), insideGroupBox(false) { } | | |||
46 | | ||||
47 | public: | | |||
48 | KConfigDialogManager * const q; | | |||
49 | | ||||
50 | /** | | |||
51 | * KConfigSkeleton object used to store settings | | |||
52 | */ | | |||
53 | KCoreConfigSkeleton *m_conf = nullptr; | | |||
54 | | ||||
55 | /** | | |||
56 | * Dialog being managed | | |||
57 | */ | | |||
58 | QWidget *m_dialog = nullptr; | | |||
59 | | ||||
60 | QHash<QString, QWidget *> knownWidget; | | |||
61 | QHash<QString, QWidget *> buddyWidget; | | |||
62 | QSet<QWidget *> allExclusiveGroupBoxes; | | |||
63 | bool insideGroupBox : 1; | | |||
64 | bool trackChanges : 1; | | |||
65 | }; | | |||
66 | | ||||
67 | KConfigDialogManager::KConfigDialogManager(QWidget *parent, KCoreConfigSkeleton *conf) | 45 | KConfigDialogManager::KConfigDialogManager(QWidget *parent, KCoreConfigSkeleton *conf) | ||
68 | : QObject(parent), d(new KConfigDialogManagerPrivate(this)) | 46 | : QObject(parent), d(new KConfigDialogManagerPrivate(this)) | ||
69 | { | 47 | { | ||
70 | d->m_conf = conf; | 48 | d->m_conf = conf; | ||
71 | d->m_dialog = parent; | 49 | d->m_dialog = parent; | ||
72 | init(true); | 50 | init(true); | ||
73 | } | 51 | } | ||
74 | 52 | | |||
▲ Show 20 Lines • Show All 146 Lines • ▼ Show 20 Line(s) | 198 | if (allAutoExclusiveDirectChildren) { | |||
221 | d->allExclusiveGroupBoxes << widget; | 199 | d->allExclusiveGroupBoxes << widget; | ||
222 | } | 200 | } | ||
223 | } | 201 | } | ||
224 | } | 202 | } | ||
225 | 203 | | |||
226 | if (!item->isEqual(property(widget))) { | 204 | if (!item->isEqual(property(widget))) { | ||
227 | setProperty(widget, item->property()); | 205 | setProperty(widget, item->property()); | ||
228 | } | 206 | } | ||
207 | | ||||
208 | d->updateWidgetIndicator(item->name(), widget); | ||||
229 | } | 209 | } | ||
230 | 210 | | |||
231 | bool KConfigDialogManager::parseChildren(const QWidget *widget, bool trackChanges) | 211 | bool KConfigDialogManager::parseChildren(const QWidget *widget, bool trackChanges) | ||
232 | { | 212 | { | ||
233 | bool valueChanged = false; | 213 | bool valueChanged = false; | ||
234 | const QList<QObject *> listOfChildren = widget->children(); | 214 | const QList<QObject *> listOfChildren = widget->children(); | ||
235 | if (listOfChildren.isEmpty()) { //?? XXX | 215 | if (listOfChildren.isEmpty()) { //?? XXX | ||
236 | return valueChanged; | 216 | return valueChanged; | ||
237 | } | 217 | } | ||
238 | 218 | | |||
239 | const QMetaMethod widgetModifiedSignal = metaObject()->method(metaObject()->indexOfSignal("widgetModified()")); | 219 | const QMetaMethod onWidgetModifiedSlot = metaObject()->method(metaObject()->indexOfSlot("onWidgetModified()")); | ||
240 | Q_ASSERT(widgetModifiedSignal.isValid() && metaObject()->indexOfSignal("widgetModified()")>=0); | 220 | Q_ASSERT(onWidgetModifiedSlot.isValid() && metaObject()->indexOfSlot("onWidgetModified()")>=0); | ||
241 | 221 | | |||
242 | for (QObject *object : listOfChildren) { | 222 | for (QObject *object : listOfChildren) { | ||
243 | if (!object->isWidgetType()) { | 223 | if (!object->isWidgetType()) { | ||
244 | continue; // Skip non-widgets | 224 | continue; // Skip non-widgets | ||
245 | } | 225 | } | ||
246 | 226 | | |||
247 | QWidget *childWidget = static_cast<QWidget *>(object); | 227 | QWidget *childWidget = static_cast<QWidget *>(object); | ||
248 | 228 | | |||
Show All 12 Lines | 237 | if (item) { | |||
261 | 241 | | |||
262 | if (trackChanges) { | 242 | if (trackChanges) { | ||
263 | bool changeSignalFound = false; | 243 | bool changeSignalFound = false; | ||
264 | 244 | | |||
265 | if (d->allExclusiveGroupBoxes.contains(childWidget)) { | 245 | if (d->allExclusiveGroupBoxes.contains(childWidget)) { | ||
266 | const QList<QAbstractButton *> buttons = childWidget->findChildren<QAbstractButton *>(); | 246 | const QList<QAbstractButton *> buttons = childWidget->findChildren<QAbstractButton *>(); | ||
267 | for (QAbstractButton *button : buttons) { | 247 | for (QAbstractButton *button : buttons) { | ||
268 | connect(button, &QAbstractButton::toggled, | 248 | connect(button, &QAbstractButton::toggled, | ||
269 | this, &KConfigDialogManager::widgetModified); | 249 | this, [this] { d->onWidgetModified(); }); | ||
270 | } | 250 | } | ||
271 | } | 251 | } | ||
272 | 252 | | |||
273 | QByteArray propertyChangeSignal = getCustomPropertyChangedSignal(childWidget); | 253 | QByteArray propertyChangeSignal = getCustomPropertyChangedSignal(childWidget); | ||
274 | if (propertyChangeSignal.isEmpty()) { | 254 | if (propertyChangeSignal.isEmpty()) { | ||
275 | propertyChangeSignal = getUserPropertyChangedSignal(childWidget); | 255 | propertyChangeSignal = getUserPropertyChangedSignal(childWidget); | ||
276 | } | 256 | } | ||
277 | 257 | | |||
278 | if (propertyChangeSignal.isEmpty()) { | 258 | if (propertyChangeSignal.isEmpty()) { | ||
279 | // get the change signal from the meta object | 259 | // get the change signal from the meta object | ||
280 | const QMetaObject *metaObject = childWidget->metaObject(); | 260 | const QMetaObject *metaObject = childWidget->metaObject(); | ||
281 | QByteArray userproperty = getCustomProperty(childWidget); | 261 | QByteArray userproperty = getCustomProperty(childWidget); | ||
282 | if (userproperty.isEmpty()) { | 262 | if (userproperty.isEmpty()) { | ||
283 | userproperty = getUserProperty(childWidget); | 263 | userproperty = getUserProperty(childWidget); | ||
284 | } | 264 | } | ||
285 | if (!userproperty.isEmpty()) { | 265 | if (!userproperty.isEmpty()) { | ||
286 | const int indexOfProperty = metaObject->indexOfProperty(userproperty); | 266 | const int indexOfProperty = metaObject->indexOfProperty(userproperty); | ||
287 | if (indexOfProperty != -1) { | 267 | if (indexOfProperty != -1) { | ||
288 | const QMetaProperty property = metaObject->property(indexOfProperty); | 268 | const QMetaProperty property = metaObject->property(indexOfProperty); | ||
289 | const QMetaMethod notifySignal = property.notifySignal(); | 269 | const QMetaMethod notifySignal = property.notifySignal(); | ||
290 | if (notifySignal.isValid()) { | 270 | if (notifySignal.isValid()) { | ||
291 | connect(childWidget, notifySignal, this, widgetModifiedSignal); | 271 | connect(childWidget, notifySignal, this, onWidgetModifiedSlot); | ||
292 | changeSignalFound = true; | 272 | changeSignalFound = true; | ||
293 | } | 273 | } | ||
294 | } | 274 | } | ||
295 | } else { | 275 | } else { | ||
296 | qCWarning(KCONFIG_WIDGETS_LOG) << "Don't know how to monitor widget '" << childWidget->metaObject()->className() << "' for changes!"; | 276 | qCWarning(KCONFIG_WIDGETS_LOG) << "Don't know how to monitor widget '" << childWidget->metaObject()->className() << "' for changes!"; | ||
297 | } | 277 | } | ||
298 | } else { | 278 | } else { | ||
299 | connect(childWidget, propertyChangeSignal, | 279 | connect(childWidget, propertyChangeSignal, | ||
300 | this, SIGNAL(widgetModified())); | 280 | this, SLOT(onWidgetModified())); | ||
301 | changeSignalFound = true; | 281 | changeSignalFound = true; | ||
302 | } | 282 | } | ||
303 | 283 | | |||
304 | if (changeSignalFound) { | 284 | if (changeSignalFound) { | ||
305 | QComboBox *cb = qobject_cast<QComboBox *>(childWidget); | 285 | QComboBox *cb = qobject_cast<QComboBox *>(childWidget); | ||
306 | if (cb && cb->isEditable()) | 286 | if (cb && cb->isEditable()) | ||
307 | connect(cb, &QComboBox::editTextChanged, | 287 | connect(cb, &QComboBox::editTextChanged, | ||
308 | this, &KConfigDialogManager::widgetModified); | 288 | this, &KConfigDialogManager::widgetModified); | ||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Line(s) | 362 | if (buddy) { | |||
383 | buddy->setEnabled(false); | 363 | buddy->setEnabled(false); | ||
384 | } | 364 | } | ||
385 | } | 365 | } | ||
386 | } | 366 | } | ||
387 | blockSignals(bSignalsBlocked); | 367 | blockSignals(bSignalsBlocked); | ||
388 | 368 | | |||
389 | if (changed) { | 369 | if (changed) { | ||
390 | QTimer::singleShot(0, this, &KConfigDialogManager::widgetModified); | 370 | QTimer::singleShot(0, this, &KConfigDialogManager::widgetModified); | ||
371 | d->updateAllWidgetIndicators(); | ||||
391 | } | 372 | } | ||
392 | } | 373 | } | ||
393 | 374 | | |||
394 | void KConfigDialogManager::updateWidgetsDefault() | 375 | void KConfigDialogManager::updateWidgetsDefault() | ||
395 | { | 376 | { | ||
396 | bool bUseDefaults = d->m_conf->useDefaults(true); | 377 | bool bUseDefaults = d->m_conf->useDefaults(true); | ||
397 | updateWidgets(); | 378 | updateWidgets(); | ||
398 | d->m_conf->useDefaults(bUseDefaults); | 379 | d->m_conf->useDefaults(bUseDefaults); | ||
380 | d->updateAllWidgetIndicators(); | ||||
399 | } | 381 | } | ||
400 | 382 | | |||
401 | void KConfigDialogManager::updateSettings() | 383 | void KConfigDialogManager::updateSettings() | ||
402 | { | 384 | { | ||
403 | bool changed = false; | 385 | bool changed = false; | ||
404 | 386 | | |||
405 | QWidget *widget; | 387 | QWidget *widget; | ||
406 | QHashIterator<QString, QWidget *> it(d->knownWidget); | 388 | QHashIterator<QString, QWidget *> it(d->knownWidget); | ||
Show All 11 Lines | 389 | while (it.hasNext()) { | |||
418 | if (!item->isEqual(fromWidget)) { | 400 | if (!item->isEqual(fromWidget)) { | ||
419 | item->setProperty(fromWidget); | 401 | item->setProperty(fromWidget); | ||
420 | changed = true; | 402 | changed = true; | ||
421 | } | 403 | } | ||
422 | } | 404 | } | ||
423 | if (changed) { | 405 | if (changed) { | ||
424 | d->m_conf->save(); | 406 | d->m_conf->save(); | ||
425 | emit settingsChanged(); | 407 | emit settingsChanged(); | ||
408 | d->updateAllWidgetIndicators(); | ||||
426 | } | 409 | } | ||
427 | } | 410 | } | ||
428 | 411 | | |||
429 | QByteArray KConfigDialogManager::getUserProperty(const QWidget *widget) const | 412 | QByteArray KConfigDialogManager::getUserProperty(const QWidget *widget) const | ||
430 | { | 413 | { | ||
431 | if (!s_propertyMap()->contains(widget->metaObject()->className())) { | 414 | if (!s_propertyMap()->contains(widget->metaObject()->className())) { | ||
432 | const QMetaObject *metaObject = widget->metaObject(); | 415 | const QMetaObject *metaObject = widget->metaObject(); | ||
433 | const QMetaProperty user = metaObject->userProperty(); | 416 | const QMetaProperty user = metaObject->userProperty(); | ||
▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Line(s) | |||||
596 | bool KConfigDialogManager::isDefault() const | 579 | bool KConfigDialogManager::isDefault() const | ||
597 | { | 580 | { | ||
598 | bool bUseDefaults = d->m_conf->useDefaults(true); | 581 | bool bUseDefaults = d->m_conf->useDefaults(true); | ||
599 | bool result = !hasChanged(); | 582 | bool result = !hasChanged(); | ||
600 | d->m_conf->useDefaults(bUseDefaults); | 583 | d->m_conf->useDefaults(bUseDefaults); | ||
601 | return result; | 584 | return result; | ||
602 | } | 585 | } | ||
603 | 586 | | |||
587 | KConfigDialogManagerPrivate::KConfigDialogManagerPrivate(KConfigDialogManager *q) | ||||
588 | : q(q) | ||||
589 | , insideGroupBox(false) | ||||
590 | { | ||||
591 | } | ||||
592 | | ||||
593 | void KConfigDialogManagerPrivate::onWidgetModified() | ||||
594 | { | ||||
595 | const auto widget = qobject_cast<QWidget*>(q->sender()); | ||||
596 | Q_ASSERT(widget && widget->objectName().startsWith("kcfg_")); | ||||
597 | const auto configId = widget->objectName().mid(5); | ||||
598 | updateWidgetIndicator(configId, widget); | ||||
599 | emit q->widgetModified(); | ||||
600 | } | ||||
601 | | ||||
602 | void KConfigDialogManagerPrivate::updateWidgetIndicator(const QString &configId, QWidget *widget) | ||||
603 | { | ||||
604 | const auto item = m_conf->findItem(configId); | ||||
605 | Q_ASSERT(item); | ||||
606 | | ||||
607 | const auto widgetValue = q->property(widget); | ||||
608 | const auto defaultValue = [item] { | ||||
609 | item->swapDefault(); | ||||
davidedmundson: Why not item->readDefault()? | |||||
Wouldn't do the same thing at all. readDefault() takes a KConfig object and updates the default value stored in the item with what it found in there. Yes, I know... the item API is weird... ervin: Wouldn't do the same thing at all. readDefault() takes a KConfig object and updates the default… | |||||
610 | const auto value = item->property(); | ||||
611 | item->swapDefault(); | ||||
612 | return value; | ||||
613 | }(); | ||||
614 | | ||||
615 | auto indicator = indicatorWidgets.value(configId); | ||||
616 | const auto defaulted = widgetValue == defaultValue; | ||||
617 | if (defaulted) { | ||||
618 | delete indicator; | ||||
619 | indicatorWidgets.remove(configId); | ||||
620 | } else if (!indicator){ | ||||
621 | indicator = new SettingStatusIndicator(widget->parentWidget()); | ||||
622 | indicator->setTrackedWidget(widget); | ||||
623 | QObject::connect(indicator, &SettingStatusIndicator::clicked, widget, [=] { | ||||
624 | q->setProperty(widget, defaultValue); | ||||
625 | emit q->widgetModified(); | ||||
davidedmundson: won't it do it itself when the property changes? | |||||
ervin: Good point, was indeed unnecessary now, I removed the line. | |||||
626 | }); | ||||
627 | indicatorWidgets.insert(configId, indicator); | ||||
628 | } | ||||
629 | } | ||||
630 | | ||||
631 | void KConfigDialogManagerPrivate::updateAllWidgetIndicators() | ||||
632 | { | ||||
633 | QHashIterator<QString, QWidget *> it(knownWidget); | ||||
634 | while (it.hasNext()) { | ||||
635 | it.next(); | ||||
636 | updateWidgetIndicator(it.key(), it.value()); | ||||
637 | } | ||||
638 | } | ||||
639 | | ||||
640 | #include "moc_kconfigdialogmanager.cpp" |
Why not item->readDefault()?