LCOV - code coverage report
Current view: top level - muse/framework/extensions/api/v1 - qqmlsettings.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 146 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /****************************************************************************
       2              : **
       3              : ** Copyright (C) 2016 The Qt Company Ltd.
       4              : ** Contact: https://www.qt.io/licensing/
       5              : **
       6              : ** This file is part of the examples of the Qt Toolkit.
       7              : **
       8              : ** $QT_BEGIN_LICENSE:LGPL$
       9              : ** Commercial License Usage
      10              : ** Licensees holding valid commercial Qt licenses may use this file in
      11              : ** accordance with the commercial license agreement provided with the
      12              : ** Software or, alternatively, in accordance with the terms contained in
      13              : ** a written agreement between you and The Qt Company. For licensing terms
      14              : ** and conditions see https://www.qt.io/terms-conditions. For further
      15              : ** information use the contact form at https://www.qt.io/contact-us.
      16              : **
      17              : ** GNU Lesser General Public License Usage
      18              : ** Alternatively, this file may be used under the terms of the GNU Lesser
      19              : ** General Public License version 3 as published by the Free Software
      20              : ** Foundation and appearing in the file LICENSE.LGPL3 included in the
      21              : ** packaging of this file. Please review the following information to
      22              : ** ensure the GNU Lesser General Public License version 3 requirements
      23              : ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
      24              : **
      25              : ** GNU General Public License Usage
      26              : ** Alternatively, this file may be used under the terms of the GNU
      27              : ** General Public License version 2.0 or (at your option) the GNU General
      28              : ** Public license version 3 or any later version approved by the KDE Free
      29              : ** Qt Foundation. The licenses are as published by the Free Software
      30              : ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
      31              : ** included in the packaging of this file. Please review the following
      32              : ** information to ensure the GNU General Public License requirements will
      33              : ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
      34              : ** https://www.gnu.org/licenses/gpl-3.0.html.
      35              : **
      36              : ** $QT_END_LICENSE$
      37              : **
      38              : ****************************************************************************/
      39              : 
      40              : #include "qqmlsettings_p.h"
      41              : #include <qcoreevent.h>
      42              : #include <qcoreapplication.h>
      43              : #include <qloggingcategory.h>
      44              : #include <qsettings.h>
      45              : #include <qpointer.h>
      46              : #include <qjsvalue.h>
      47              : #include <qqmlinfo.h>
      48              : #include <qdebug.h>
      49              : #include <qhash.h>
      50              : 
      51              : QT_BEGIN_NAMESPACE
      52              : 
      53              : /*!
      54              :     \qmlmodule Qt.labs.settings 1.0
      55              :     \title Qt Labs Settings QML Types
      56              :     \ingroup qmlmodules
      57              :     \brief Provides persistent platform-independent application settings.
      58              : 
      59              :     To use this module, import the module with the following line:
      60              : 
      61              :     \code
      62              :     import Qt.labs.settings
      63              :     \endcode
      64              : */
      65              : 
      66              : /*!
      67              :     \qmltype Settings
      68              :     \instantiates QQmlSettings
      69              :     \inqmlmodule Qt.labs.settings
      70              :     \ingroup settings
      71              :     \brief Provides persistent platform-independent application settings.
      72              : 
      73              :     The Settings type provides persistent platform-independent application settings.
      74              : 
      75              :     \note This type is made available by importing the \b Qt.labs.settings module.
      76              :     \e {Types in the Qt.labs module are not guaranteed to remain compatible
      77              :     in future versions.}
      78              : 
      79              :     Users normally expect an application to remember its settings (window sizes
      80              :     and positions, options, etc.) across sessions. The Settings type enables you
      81              :     to save and restore such application settings with the minimum of effort.
      82              : 
      83              :     Individual setting values are specified by declaring properties within a
      84              :     Settings element. All \l {QML Basic Types}{basic type} properties are
      85              :     supported. The recommended approach is to use property aliases in order
      86              :     to get automatic property updates both ways. The following example shows
      87              :     how to use Settings to store and restore the geometry of a window.
      88              : 
      89              :     \qml
      90              :     import QtQuick.Window
      91              :     import Qt.labs.settings
      92              : 
      93              :     Window {
      94              :         id: window
      95              : 
      96              :         width: 800
      97              :         height: 600
      98              : 
      99              :         Settings {
     100              :             property alias x: window.x
     101              :             property alias y: window.y
     102              :             property alias width: window.width
     103              :             property alias height: window.height
     104              :         }
     105              :     }
     106              :     \endqml
     107              : 
     108              :     At first application startup, the window gets default dimensions specified
     109              :     as 800x600. Notice that no default position is specified - we let the window
     110              :     manager handle that. Later when the window geometry changes, new values will
     111              :     be automatically stored to the persistent settings. The second application
     112              :     run will get initial values from the persistent settings, bringing the window
     113              :     back to the previous position and size.
     114              : 
     115              :     A fully declarative syntax, achieved by using property aliases, comes at the
     116              :     cost of storing persistent settings whenever the values of aliased properties
     117              :     change. Normal properties can be used to gain more fine-grained control over
     118              :     storing the persistent settings. The following example illustrates how to save
     119              :     a setting on component destruction.
     120              : 
     121              :     \qml
     122              :     import QtQuick
     123              :     import Qt.labs.settings
     124              : 
     125              :     Item {
     126              :         id: page
     127              : 
     128              :         state: settings.state
     129              : 
     130              :         states: [
     131              :             State {
     132              :                 name: "active"
     133              :                 // ...
     134              :             },
     135              :             State {
     136              :                 name: "inactive"
     137              :                 // ...
     138              :             }
     139              :         ]
     140              : 
     141              :         Settings {
     142              :             id: settings
     143              :             property string state: "active"
     144              :         }
     145              : 
     146              :         Component.onDestruction: {
     147              :             settings.state = page.state
     148              :         }
     149              :     }
     150              :     \endqml
     151              : 
     152              :     Notice how the default value is now specified in the persistent setting property,
     153              :     and the actual property is bound to the setting in order to get the initial value
     154              :     from the persistent settings.
     155              : 
     156              :     \section1 Application Identifiers
     157              : 
     158              :     Application specific settings are identified by providing application
     159              :     \l {QCoreApplication::applicationName}{name},
     160              :     \l {QCoreApplication::organizationName}{organization} and
     161              :     \l {QCoreApplication::organizationDomain}{domain}, or by specifying
     162              :     \l fileName.
     163              : 
     164              :     \code
     165              :     #include <QGuiApplication>
     166              :     #include <QQmlApplicationEngine>
     167              : 
     168              :     int main(int argc, char *argv[])
     169              :     {
     170              :         QGuiApplication app(argc, argv);
     171              :         app.setOrganizationName("Some Company");
     172              :         app.setOrganizationDomain("somecompany.com");
     173              :         app.setApplicationName("Amazing Application");
     174              : 
     175              :         QQmlApplicationEngine engine("main.qml");
     176              :         return app.exec();
     177              :     }
     178              :     \endcode
     179              : 
     180              :     These are typically specified in C++ in the beginning of \c main(),
     181              :     but can also be controlled in QML via the following properties:
     182              :     \list
     183              :         \li \l {Qt::application}{Qt.application.name},
     184              :         \li \l {Qt::application}{Qt.application.organization} and
     185              :         \li \l {Qt::application}{Qt.application.domain}.
     186              :     \endlist
     187              : 
     188              :     \section1 Categories
     189              : 
     190              :     Application settings may be divided into logical categories by specifying
     191              :     a category name via the \l category property. Using logical categories not
     192              :     only provides a cleaner settings structure, but also prevents possible
     193              :     conflicts between setting keys.
     194              : 
     195              :     If several categories are required, use several Settings objects, each with
     196              :     their own category:
     197              : 
     198              :     \qml
     199              :     Item {
     200              :         id: panel
     201              : 
     202              :         visible: true
     203              : 
     204              :         Settings {
     205              :             category: "OutputPanel"
     206              :             property alias visible: panel.visible
     207              :             // ...
     208              :         }
     209              : 
     210              :         Settings {
     211              :             category: "General"
     212              :             property alias fontSize: fontSizeSpinBox.value
     213              :             // ...
     214              :         }
     215              :     }
     216              :     \endqml
     217              : 
     218              :     Instead of ensuring that all settings in the application have unique names,
     219              :     the settings can be divided into unique categories that may then contain
     220              :     settings using the same names that are used in other categories - without
     221              :     a conflict.
     222              : 
     223              :     \section1 Notes
     224              : 
     225              :     The current implementation is based on \l QSettings. This imposes certain
     226              :     limitations, such as missing change notifications. Writing a setting value
     227              :     using one instance of Settings does not update the value in another Settings
     228              :     instance, even if they are referring to the same setting in the same category.
     229              : 
     230              :     The information is stored in the system registry on Windows, and in XML
     231              :     preferences files on \macos. On other Unix systems, in the absence of a
     232              :     standard, INI text files are used. See \l QSettings documentation for
     233              :     more details.
     234              : 
     235              :     \sa QSettings
     236              : */
     237              : 
     238            0 :     Q_LOGGING_CATEGORY(lcSettings, "qt.labs.settings")
     239              : 
     240              : static const int settingsWriteDelay = 500;
     241              : 
     242              : class QQmlSettingsPrivate
     243              : {
     244            0 :     Q_DECLARE_PUBLIC(QQmlSettings)
     245              : 
     246              : public:
     247              :     QQmlSettingsPrivate();
     248              : 
     249              :     QSettings* instance() const;
     250              : 
     251              :     void init();
     252              :     void reset();
     253              : 
     254              :     void load();
     255              :     void store();
     256              : 
     257              :     void _q_propertyChanged();
     258              :     QVariant readProperty(const QMetaProperty& property) const;
     259              : 
     260              :     QQmlSettings* q_ptr = nullptr;
     261              :     int timerId = 0;
     262              :     bool initialized = false;
     263              :     QString category;
     264              :     QString fileName;
     265              :     mutable QPointer<QSettings> settings;
     266              :     QHash<const char*, QVariant> changedProperties;
     267              : };
     268              : 
     269            0 : QQmlSettingsPrivate::QQmlSettingsPrivate() {}
     270              : 
     271            0 : QSettings* QQmlSettingsPrivate::instance() const
     272              : {
     273            0 :     if (!settings) {
     274            0 :         QQmlSettings* q = const_cast<QQmlSettings*>(q_func());
     275            0 :         settings = fileName.isEmpty() ? new QSettings(q) : new QSettings(fileName, QSettings::IniFormat, q);
     276            0 :         if (settings->status() != QSettings::NoError) {
     277              :             // TODO: can't print out the enum due to the following error:
     278              :             // error: C2666: 'QQmlInfo::operator <<': 15 overloads have similar conversions
     279            0 :             qmlWarning(q) << "Failed to initialize QSettings instance. Status code is: " << int(settings->status());
     280              : 
     281            0 :             if (settings->status() == QSettings::AccessError) {
     282            0 :                 QVector<QString> missingIdentifiers;
     283            0 :                 if (QCoreApplication::organizationName().isEmpty()) {
     284            0 :                     missingIdentifiers.append(QLatin1String("organizationName"));
     285              :                 }
     286            0 :                 if (QCoreApplication::organizationDomain().isEmpty()) {
     287            0 :                     missingIdentifiers.append(QLatin1String("organizationDomain"));
     288              :                 }
     289            0 :                 if (QCoreApplication::applicationName().isEmpty()) {
     290            0 :                     missingIdentifiers.append(QLatin1String("applicationName"));
     291              :                 }
     292              : 
     293            0 :                 if (!missingIdentifiers.isEmpty()) {
     294            0 :                     qmlWarning(q) << "The following application identifiers have not been set: " << missingIdentifiers;
     295              :                 }
     296            0 :             }
     297            0 :             return settings;
     298              :         }
     299              : 
     300            0 :         if (!category.isEmpty()) {
     301            0 :             settings->beginGroup(category);
     302              :         }
     303            0 :         if (initialized) {
     304            0 :             q->d_func()->load();
     305              :         }
     306              :     }
     307            0 :     return settings;
     308              : }
     309              : 
     310            0 : void QQmlSettingsPrivate::init()
     311              : {
     312            0 :     if (!initialized) {
     313            0 :         qCDebug(lcSettings) << "QQmlSettings: stored at" << instance()->fileName();
     314            0 :         load();
     315            0 :         initialized = true;
     316              :     }
     317            0 : }
     318              : 
     319            0 : void QQmlSettingsPrivate::reset()
     320              : {
     321            0 :     if (initialized && settings && !changedProperties.isEmpty()) {
     322            0 :         store();
     323              :     }
     324            0 :     delete settings;
     325            0 : }
     326              : 
     327            0 : void QQmlSettingsPrivate::load()
     328              : {
     329            0 :     Q_Q(QQmlSettings);
     330            0 :     const QMetaObject* mo = q->metaObject();
     331            0 :     const int offset = mo->propertyOffset();
     332            0 :     const int count = mo->propertyCount();
     333              : 
     334              :     // don't save built-in properties if there aren't any qml properties
     335            0 :     if (offset == 1) {
     336            0 :         return;
     337              :     }
     338              : 
     339            0 :     for (int i = offset; i < count; ++i) {
     340            0 :         QMetaProperty property = mo->property(i);
     341              : 
     342            0 :         const QVariant previousValue = readProperty(property);
     343            0 :         const QVariant currentValue = instance()->value(property.name(), previousValue);
     344              : 
     345            0 :         if (!currentValue.isNull() && (!previousValue.isValid()
     346            0 :                                        || (currentValue.canConvert(previousValue.metaType())
     347            0 :                                            && previousValue != currentValue))) {
     348            0 :             property.write(q, currentValue);
     349            0 :             qCDebug(lcSettings) << "QQmlSettings: load" << property.name() << "setting:" << currentValue << "default:" << previousValue;
     350              :         }
     351              : 
     352              :         // ensure that a non-existent setting gets written
     353              :         // even if the property wouldn't change later
     354            0 :         if (!instance()->contains(property.name())) {
     355            0 :             _q_propertyChanged();
     356              :         }
     357              : 
     358              :         // setup change notifications on first load
     359            0 :         if (!initialized && property.hasNotifySignal()) {
     360            0 :             static const int propertyChangedIndex = mo->indexOfSlot("_q_propertyChanged()");
     361            0 :             QMetaObject::connect(q, property.notifySignalIndex(), q, propertyChangedIndex);
     362              :         }
     363            0 :     }
     364              : }
     365              : 
     366            0 : void QQmlSettingsPrivate::store()
     367              : {
     368            0 :     QHash<const char*, QVariant>::const_iterator it = changedProperties.constBegin();
     369            0 :     while (it != changedProperties.constEnd()) {
     370            0 :         instance()->setValue(it.key(), it.value());
     371            0 :         qCDebug(lcSettings) << "QQmlSettings: store" << it.key() << ":" << it.value();
     372            0 :         ++it;
     373              :     }
     374            0 :     changedProperties.clear();
     375            0 : }
     376              : 
     377            0 : void QQmlSettingsPrivate::_q_propertyChanged()
     378              : {
     379            0 :     Q_Q(QQmlSettings);
     380            0 :     const QMetaObject* mo = q->metaObject();
     381            0 :     const int offset = mo->propertyOffset();
     382            0 :     const int count = mo->propertyCount();
     383            0 :     for (int i = offset; i < count; ++i) {
     384            0 :         const QMetaProperty& property = mo->property(i);
     385            0 :         const QVariant value = readProperty(property);
     386            0 :         changedProperties.insert(property.name(), value);
     387            0 :         qCDebug(lcSettings) << "QQmlSettings: cache" << property.name() << ":" << value;
     388            0 :     }
     389            0 :     if (timerId != 0) {
     390            0 :         q->killTimer(timerId);
     391              :     }
     392            0 :     timerId = q->startTimer(settingsWriteDelay);
     393            0 : }
     394              : 
     395            0 : QVariant QQmlSettingsPrivate::readProperty(const QMetaProperty& property) const
     396              : {
     397            0 :     Q_Q(const QQmlSettings);
     398            0 :     QVariant var = property.read(q);
     399            0 :     if (var.userType() == qMetaTypeId<QJSValue>()) {
     400            0 :         var = var.value<QJSValue>().toVariant();
     401              :     }
     402            0 :     return var;
     403            0 : }
     404              : 
     405              : #define Q_PD(Class) Class##Private* const pd = d_func()
     406              : 
     407            0 : QQmlSettings::QQmlSettings(QObject* parent)
     408            0 :     : QObject(parent), d_ptr(new QQmlSettingsPrivate)
     409              : {
     410            0 :     Q_PD(QQmlSettings);
     411            0 :     pd->q_ptr = this;
     412            0 : }
     413              : 
     414            0 : QQmlSettings::~QQmlSettings()
     415              : {
     416            0 :     Q_PD(QQmlSettings);
     417            0 :     pd->reset(); // flush pending changes
     418            0 : }
     419              : 
     420              : /*!
     421              :     \qmlproperty string Settings::category
     422              : 
     423              :     This property holds the name of the settings category.
     424              : 
     425              :     Categories can be used to group related settings together.
     426              : */
     427            0 : QString QQmlSettings::category() const
     428              : {
     429            0 :     Q_PD(const QQmlSettings);
     430            0 :     return pd->category;
     431              : }
     432              : 
     433            0 : void QQmlSettings::setCategory(const QString& category)
     434              : {
     435            0 :     Q_PD(QQmlSettings);
     436            0 :     if (pd->category != category) {
     437            0 :         pd->reset();
     438            0 :         pd->category = category;
     439            0 :         if (pd->initialized) {
     440            0 :             pd->load();
     441              :         }
     442              :     }
     443            0 : }
     444              : 
     445              : /*!
     446              :     \qmlproperty string Settings::fileName
     447              : 
     448              :     This property holds the path to the settings file. If the file doesn't
     449              :     already exist, it is created.
     450              : 
     451              :     \since Qt 5.12
     452              : 
     453              :     \sa QSettings::fileName, QSettings::IniFormat
     454              : */
     455            0 : QString QQmlSettings::fileName() const
     456              : {
     457            0 :     Q_PD(const QQmlSettings);
     458            0 :     return pd->fileName;
     459              : }
     460              : 
     461            0 : void QQmlSettings::setFileName(const QString& fileName)
     462              : {
     463            0 :     Q_PD(QQmlSettings);
     464            0 :     if (pd->fileName != fileName) {
     465            0 :         pd->reset();
     466            0 :         pd->fileName = fileName;
     467            0 :         if (pd->initialized) {
     468            0 :             pd->load();
     469              :         }
     470              :     }
     471            0 : }
     472              : 
     473              : /*!
     474              :    \qmlmethod var Settings::value(string key, var defaultValue)
     475              : 
     476              :    Returns the value for setting \a key. If the setting doesn't exist,
     477              :    returns \a defaultValue.
     478              : 
     479              :    \since Qt 5.12
     480              : 
     481              :    \sa QSettings::value
     482              : */
     483            0 : QVariant QQmlSettings::value(const QString& key, const QVariant& defaultValue) const
     484              : {
     485            0 :     Q_PD(const QQmlSettings);
     486            0 :     return pd->instance()->value(key, defaultValue);
     487              : }
     488              : 
     489              : /*!
     490              :    \qmlmethod Settings::setValue(string key, var value)
     491              : 
     492              :    Sets the value of setting \a key to \a value. If the key already exists,
     493              :    the previous value is overwritten.
     494              : 
     495              :    \since Qt 5.12
     496              : 
     497              :    \sa QSettings::setValue
     498              : */
     499            0 : void QQmlSettings::setValue(const QString& key, const QVariant& value)
     500              : {
     501            0 :     Q_PD(const QQmlSettings);
     502            0 :     pd->instance()->setValue(key, value);
     503            0 :     qCDebug(lcSettings) << "QQmlSettings: setValue" << key << ":" << value;
     504            0 : }
     505              : 
     506              : /*!
     507              :    \qmlmethod Settings::sync()
     508              : 
     509              :     Writes any unsaved changes to permanent storage, and reloads any
     510              :     settings that have been changed in the meantime by another
     511              :     application.
     512              : 
     513              :     This function is called automatically from QSettings's destructor and
     514              :     by the event loop at regular intervals, so you normally don't need to
     515              :     call it yourself.
     516              : 
     517              :    \sa QSettings::sync
     518              : */
     519            0 : void QQmlSettings::sync()
     520              : {
     521            0 :     Q_PD(QQmlSettings);
     522            0 :     pd->instance()->sync();
     523            0 : }
     524              : 
     525            0 : void QQmlSettings::classBegin()
     526              : {
     527            0 : }
     528              : 
     529            0 : void QQmlSettings::componentComplete()
     530              : {
     531            0 :     Q_PD(QQmlSettings);
     532            0 :     pd->init();
     533            0 : }
     534              : 
     535            0 : void QQmlSettings::timerEvent(QTimerEvent* event)
     536              : {
     537            0 :     Q_PD(QQmlSettings);
     538            0 :     if (event->timerId() == pd->timerId) {
     539            0 :         killTimer(pd->timerId);
     540            0 :         pd->timerId = 0;
     541              : 
     542            0 :         pd->store();
     543              :     }
     544            0 :     QObject::timerEvent(event);
     545            0 : }
     546              : 
     547              : QT_END_NAMESPACE
     548              : 
     549              : #include "moc_qqmlsettings_p.cpp"
        

Generated by: LCOV version 2.0-1