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 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #ifndef KCONFIGDIALOGMANAGER_H
0010 #define KCONFIGDIALOGMANAGER_H
0011 
0012 #include <kconfigwidgets_export.h>
0013 
0014 #include <QHash>
0015 #include <QObject>
0016 #include <memory>
0017 
0018 class KConfigDialogManagerPrivate;
0019 
0020 class KCoreConfigSkeleton;
0021 class KConfigSkeleton;
0022 class KConfigSkeletonItem;
0023 class QWidget;
0024 
0025 /**
0026  * @class KConfigDialogManager kconfigdialogmanager.h KConfigDialogManager
0027  *
0028  * @short Provides a means of automatically retrieving,
0029  * saving and resetting KConfigSkeleton based settings in a dialog.
0030  *
0031  * The KConfigDialogManager class provides a means of automatically
0032  * retrieving, saving and resetting basic settings.
0033  * It also can emit signals when settings have been changed
0034  * (settings were saved) or modified (the user changes a checkbox
0035  * from on to off).
0036  *
0037  * The object names of the widgets to be managed have to correspond to the names of the
0038  * configuration entries in the KConfigSkeleton object plus an additional
0039  * "kcfg_" prefix. For example a widget with the object name "kcfg_MyOption"
0040  * would be associated to the configuration entry "MyOption".
0041  *
0042  * The widget classes of Qt and KDE Frameworks are supported out of the box,
0043  * for other widgets see below:
0044  *
0045  * @par Using Custom Widgets
0046  * @parblock
0047  * Custom widget classes are supported if they have a Q_PROPERTY defined for the
0048  * property representing the value edited by the widget. By default the property
0049  * is used for which "USER true" is set. For using another property, see below.
0050  *
0051  * Example:
0052  *
0053  * A class ColorEditWidget is used in the settings UI to select a color. The
0054  * color value is set and read as type QColor. For that it has a definition of
0055  * the value property similar to this:
0056  * \code
0057  * Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged USER true)
0058  * \endcode
0059  * And of course it has the definition and implementation of the respective
0060  * read & write methods and the notify signal.
0061  * This class then can be used directly with KConfigDialogManager and does not need
0062  * further setup. For supporting also KDE Frameworks versions older than 5.32 see
0063  * below for how to register the property change signal.
0064  * @endparblock
0065  *
0066  * @par Using Other Properties than The USER Property
0067  * @parblock
0068  * To use a widget's property that is not the USER property, the property to use
0069  * can be selected by setting onto the widget instance a property with the key
0070  * "kcfg_property" and as the value the name of the property:
0071  * \code
0072  * ColorEditWidget *myWidget = new ColorEditWidget;
0073  * myWidget->setProperty("kcfg_property", QByteArray("redColorPart"));
0074  * \endcode
0075  * This selection of the property to use is just valid for this widget instance.
0076  * When using a UI file, the "kcfg_property" property can also be set using Qt Designer.
0077  * @endparblock
0078  *
0079  * @par Configuring Classes to use Other Properties Globally
0080  * @parblock
0081  * Alternatively a non-USER property can be defined for a widget class globally
0082  * by registering it for the class in the KConfigDialogManager::propertyMap().
0083  * This global registration has lower priority than any "kcfg_property" property
0084  * set on a class instance though, so the latter overrules this global setting.
0085  * Note: setting the property in the propertyMap affects any instances of that
0086  * widget class in the current application, so use only when needed and prefer
0087  * instead the "kcfg_property" property. Especially with software with many
0088  * libraries and 3rd-party plugins in one process there is a chance of
0089  * conflicting settings.
0090  *
0091  * Example:
0092  *
0093  * If the ColorEditWidget has another property redColor defined by
0094  * \code
0095  * Q_PROPERTY(int redColorPart READ redColorPart WRITE setRedColorPart NOTIFY redColorPartChanged)
0096  * \endcode
0097  * and this one should be used in the settings, call somewhere in the code before
0098  * using the settings:
0099  * \code
0100  * KConfigDialogManager::propertyMap()->insert("ColorEditWidget", QByteArray("redColorPart"));
0101  * \endcode
0102  * @endparblock
0103  *
0104  * @par Using Different Signals than The NOTIFY Signal
0105  * @parblock
0106  * If some non-default signal should be used, e.g. because the property to use does not
0107  * have a NOTIFY setting, for a given widget instance the signal to use can be set
0108  * by a property with the key "kcfg_propertyNotify" and as the value the signal signature.
0109  * This will take priority over the signal noted by NOTIFY for the chosen property
0110  * as well as the content of KConfigDialogManager::changedMap(). Since 5.32.
0111  *
0112  * Example:
0113  *
0114  * If for a class OtherColorEditWidget there was no NOTIFY set on the USER property,
0115  * but some signal colorSelected(QColor) defined which would be good enough to reflect
0116  * the settings change, defined by
0117  * \code
0118  * Q_PROPERTY(QColor color READ color WRITE setColor USER true)
0119  * Q_SIGNALS:
0120  *     void colorSelected(const QColor &color);
0121  * \endcode
0122  * the signal to use would be defined by this:
0123  * \code
0124  * OtherColorEditWidget *myWidget = new OtherColorEditWidget;
0125  * myWidget->setProperty("kcfg_propertyNotify", QByteArray(SIGNAL(colorSelected(QColor))));
0126  * \endcode
0127  * @endparblock
0128  *
0129  * @par Supporting Older Versions of KDE Frameworks
0130  * @parblock
0131  * Before version 5.32 of KDE Frameworks, the signal notifying about a change
0132  * of the property value in the widget had to be manually registered for any
0133  * custom widget, using KConfigDialogManager::changedMap(). The same also had
0134  * to be done for custom signals with widgets from Qt and KDE Frameworks.
0135  * So for code which needs to also work with older versions of the KDE Frameworks,
0136  * this still needs to be done.
0137  * Starting with version 5.32, where the new signal handling is effective, the
0138  * signal registered via KConfigDialogManager::changedMap() will take precedence over
0139  * the one read from the Q_PROPERTY declaration, but is overridden for a given
0140  * widget instance by the "kcfg_propertyNotify" property.
0141  *
0142  * Examples:
0143  *
0144  * For the class ColorEditWidget from the previous example this will register
0145  * the change signal as needed:
0146  * \code
0147  * KConfigDialogManager::changedMap()->insert("ColorEditWidget", QByteArray(SIGNAL(colorChanged(QColor))));
0148  * \endcode
0149  * For KDE Framework versions starting with 5.32 this will override then the signal
0150  * as read from the USER property, but as it is the same signal, nothing will break.
0151  *
0152  * If wants to reduce conflicts and also only add code to the build as needed,
0153  * one would add both a buildtime switch and a runtime switch like
0154  * \code
0155  * #include <kconfigwidgets_version.h>
0156  * #include <kcoreaddons.h>
0157  * // [...]
0158  * #if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5,32,0)
0159  * if (KCoreAddons::version() < QT_VERSION_CHECK(5,32,0)) {
0160  *     KConfigDialogManager::changedMap()->insert("ColorEditWidget", QByteArray(SIGNAL(colorChanged(QColor))));
0161  * }
0162  * #endif
0163  * \endcode
0164  * so support for the old variant would be only used when running against an older
0165  * KDE Frameworks, and this again only built in if also compiled against an older version.
0166  * Note: KCoreAddons::version() needs at least KDE Frameworks 5.20 though.
0167  *
0168  * For the class OtherColorEditWidget from the previous example for the support of
0169  * also older KDE Frameworks versions the change signal would be registered by this:
0170  * \code
0171  * KConfigDialogManager::changedMap()->insert("OtherColorEditWidget", QByteArray(SIGNAL(colorSelected(QColor))));
0172  * OtherColorEditWidget *myWidget = new OtherColorEditWidget;
0173  * myWidget->setProperty("kcfg_propertyNotify", QByteArray(SIGNAL(colorSelected(QColor))));
0174  * \endcode
0175  * Here for KDE Framework versions before 5.32 the "kcfg_propertyNotify" property would
0176  * be ignored and the signal taken from KConfigDialogManager::changedMap(), while
0177  * for newer versions it is taken from that property, which then overrides the latter.
0178  * But as it is the same signal, nothing will break.
0179  *
0180  * Again, using KConfigDialogManager::changedMap could be made to depend on the version,
0181  * so for newer versions any global conflicts are avoided:
0182  * \code
0183  * #include <kconfigwidgets_version.h>
0184  * #include <kcoreaddons.h>
0185  * // [...]
0186  * #if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5,32,0)
0187  * if (KCoreAddons::version() < QT_VERSION_CHECK(5,32,0)) {
0188  *     KConfigDialogManager::changedMap()->insert("OtherColorEditWidget", QByteArray(SIGNAL(colorSelected(QColor))));
0189  * }
0190  * #endif
0191  * OtherColorEditWidget *myWidget = new OtherColorEditWidget;
0192  * myWidget->setProperty("kcfg_propertyNotify", QByteArray(SIGNAL(colorSelected(QColor))));
0193  * \endcode
0194  * @endparblock
0195  *
0196  * @author Benjamin C Meyer <ben+kdelibs at meyerhome dot net>
0197  * @author Waldo Bastian <bastian@kde.org>
0198  */
0199 class KCONFIGWIDGETS_EXPORT KConfigDialogManager : public QObject
0200 {
0201     Q_OBJECT
0202 
0203 Q_SIGNALS:
0204     /**
0205      * One or more of the settings have been saved (such as when the user
0206      * clicks on the Apply button).  This is only emitted by updateSettings()
0207      * whenever one or more setting were changed and consequently saved.
0208      */
0209     void settingsChanged(); // clazy:exclude=overloaded-signal
0210 
0211 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 82)
0212     /**
0213      * TODO: Verify
0214      * One or more of the settings have been changed.
0215      * @param widget - The widget group (pass in via addWidget()) that
0216      * contains the one or more modified setting.
0217      * @see settingsChanged()
0218      *
0219      * @deprecated since 5.82, use the KConfigDialogManager::settingsChanged() signal instead.
0220      */
0221     KCONFIGWIDGETS_DEPRECATED_VERSION(5, 82, "Use the KConfigDialogManager::settingsChanged() signal instead.")
0222     void settingsChanged(QWidget *widget); // clazy:exclude=overloaded-signal
0223 #endif
0224 
0225     /**
0226      * If retrieveSettings() was told to track changes then if
0227      * any known setting was changed this signal will be emitted.  Note
0228      * that a settings can be modified several times and might go back to the
0229      * original saved state. hasChanged() will tell you if anything has
0230      * actually changed from the saved values.
0231      */
0232     void widgetModified();
0233 
0234 public:
0235     /**
0236      * Constructor.
0237      * @param parent  Dialog widget to manage
0238      * @param conf Object that contains settings
0239      */
0240     KConfigDialogManager(QWidget *parent, KCoreConfigSkeleton *conf);
0241 
0242 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 84)
0243     // No deprecation warning by compiler here, as the replacement will be
0244     // automatically picked by the compiler in the future, being the method
0245     // overload using the base-class of the argument type.
0246     // Avoids the need to do extra-casting right now on the caller side.
0247     /**
0248      * Constructor.
0249      * @param parent  Dialog widget to manage
0250      * @param conf Object that contains settings
0251      * @deprecated since 5.84, use KConfigDialogManager(QWidget *parent, KCoreConfigSkeleton *conf)
0252      */
0253     KConfigDialogManager(QWidget *parent, KConfigSkeleton *conf);
0254 #endif
0255 
0256     /**
0257      * Destructor.
0258      */
0259     ~KConfigDialogManager() override;
0260 
0261     /**
0262      * Add additional widgets to manage
0263      * @param widget Additional widget to manage, including all its children
0264      */
0265     void addWidget(QWidget *widget);
0266 
0267     /**
0268      * Returns whether the current state of the known widgets are
0269      * different from the state in the config object.
0270      */
0271     bool hasChanged() const;
0272 
0273     /**
0274      * Returns whether the current state of the known widgets are
0275      * the same as the default state in the config object.
0276      */
0277     bool isDefault() const;
0278 
0279     /**
0280      * Retrieve the map between widgets class names and the
0281      * USER properties used for the configuration values.
0282      */
0283     static QHash<QString, QByteArray> *propertyMap();
0284 
0285 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0286     /**
0287      * Retrieve the map between widgets class names and signals that are listened
0288      * to detect changes in the configuration values.
0289      * @deprecated Since 5.32, rely on the property change signal noted
0290      * by @c NOTIFY of the used property in the class definition
0291      * instead of setting it in this map. Or set the
0292      * "kcfg_propertyNotify" property on the widget instance.
0293      */
0294     KCONFIGWIDGETS_DEPRECATED_VERSION(5, 32, "See API docs")
0295     static QHash<QString, QByteArray> *changedMap();
0296 #endif
0297 
0298 public Q_SLOTS:
0299     /**
0300      * Traverse the specified widgets, saving the settings of all known
0301      * widgets in the settings object.
0302      *
0303      * Example use: User clicks Ok or Apply button in a configure dialog.
0304      */
0305     void updateSettings();
0306 
0307     /**
0308      * Traverse the specified widgets, sets the state of all known
0309      * widgets according to the state in the settings object.
0310      *
0311      * Example use: Initialisation of dialog.
0312      * Example use: User clicks Reset button in a configure dialog.
0313      */
0314     void updateWidgets();
0315 
0316     /**
0317      * Traverse the specified widgets, sets the state of all known
0318      * widgets according to the default state in the settings object.
0319      *
0320      * Example use: User clicks Defaults button in a configure dialog.
0321      */
0322     void updateWidgetsDefault();
0323 
0324     /**
0325      * Show or hide an indicator when settings have changed from their default value.
0326      * Update all widgets to show or hide the indicator accordingly.
0327      *
0328      * @since 5.73
0329      */
0330     void setDefaultsIndicatorsVisible(bool enabled);
0331 
0332 protected:
0333     /**
0334      * @param trackChanges - If any changes by the widgets should be tracked
0335      * set true.  This causes the emitting the modified() signal when
0336      * something changes.
0337      * TODO: @return bool - True if any setting was changed from the default.
0338      */
0339     void init(bool trackChanges);
0340 
0341     /**
0342      * Recursive function that finds all known children.
0343      * Goes through the children of widget and if any are known and not being
0344      * ignored, stores them in currentGroup.  Also checks if the widget
0345      * should be disabled because it is set immutable.
0346      * @param widget - Parent of the children to look at.
0347      * @param trackChanges - If true then tracks any changes to the children of
0348      * widget that are known.
0349      * @return bool - If a widget was set to something other than its default.
0350      */
0351     bool parseChildren(const QWidget *widget, bool trackChanges);
0352 
0353     /**
0354      * Finds the USER property name using Qt's MetaProperty system, and caches
0355      * it in the property map (the cache could be retrieved by propertyMap() ).
0356      */
0357     QByteArray getUserProperty(const QWidget *widget) const;
0358 
0359     /**
0360      * Find the property to use for a widget by querying the "kcfg_property"
0361      * property of the widget. Like a widget can use a property other than the
0362      * USER property.
0363      * @since 4.3
0364      */
0365     QByteArray getCustomProperty(const QWidget *widget) const;
0366 
0367     /**
0368      * Finds the changed signal of the USER property using Qt's MetaProperty system.
0369      * @since 5.32
0370      */
0371     QByteArray getUserPropertyChangedSignal(const QWidget *widget) const;
0372 
0373     /**
0374      * Find the changed signal of the property to use for a widget by querying
0375      * the "kcfg_propertyNotify" property of the widget. Like a widget can use a
0376      * property change signal other than the one for USER property, if there even is one.
0377      * @since 5.32
0378      */
0379     QByteArray getCustomPropertyChangedSignal(const QWidget *widget) const;
0380 
0381     /**
0382      * Set a property
0383      */
0384     void setProperty(QWidget *w, const QVariant &v);
0385 
0386     /**
0387      * Retrieve a property
0388      */
0389     QVariant property(QWidget *w) const;
0390 
0391     /**
0392      * Setup secondary widget properties
0393      */
0394     void setupWidget(QWidget *widget, KConfigSkeletonItem *item);
0395 
0396     /**
0397      * Initializes the property maps
0398      */
0399     static void initMaps();
0400 
0401 private:
0402     /**
0403      * KConfigDialogManager KConfigDialogManagerPrivate class.
0404      */
0405     std::unique_ptr<KConfigDialogManagerPrivate> const d;
0406     friend class KConfigDialogManagerPrivate;
0407 
0408     Q_DISABLE_COPY(KConfigDialogManager)
0409     Q_PRIVATE_SLOT(d, void onWidgetModified())
0410 };
0411 
0412 #endif // KCONFIGDIALOGMANAGER_H