| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <qdebug.h> |
| #include "qplatformdefs.h" |
| #include "qsettings.h" |
| |
| #include "qsettings_p.h" |
| #include "qcache.h" |
| #include "qfile.h" |
| #include "qdir.h" |
| #include "qfileinfo.h" |
| #include "qmutex.h" |
| #include "private/qlocking_p.h" |
| #include "qlibraryinfo.h" |
| #include "qtemporaryfile.h" |
| #include "qstandardpaths.h" |
| #include <qdatastream.h> |
| |
| #if QT_CONFIG(textcodec) |
| # include "qtextcodec.h" |
| #endif |
| |
| #ifndef QT_NO_GEOM_VARIANT |
| #include "qsize.h" |
| #include "qpoint.h" |
| #include "qrect.h" |
| #endif // !QT_NO_GEOM_VARIANT |
| |
| #ifndef QT_BUILD_QMAKE |
| # include "qcoreapplication.h" |
| #endif |
| |
| #ifndef QT_BOOTSTRAPPED |
| #include "qsavefile.h" |
| #include "qlockfile.h" |
| #endif |
| |
| #ifdef Q_OS_VXWORKS |
| # include <ioLib.h> |
| #endif |
| |
| #include <algorithm> |
| #include <stdlib.h> |
| |
| #ifdef Q_OS_WIN // for homedirpath reading from registry |
| # include <qt_windows.h> |
| # ifndef Q_OS_WINRT |
| # include <shlobj.h> |
| # endif |
| #endif |
| |
| #ifdef Q_OS_WINRT |
| #include <wrl.h> |
| #include <windows.foundation.h> |
| #include <windows.storage.h> |
| using namespace Microsoft::WRL; |
| using namespace Microsoft::WRL::Wrappers; |
| using namespace ABI::Windows::Foundation; |
| using namespace ABI::Windows::Storage; |
| #endif |
| |
| #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) |
| #define Q_XDG_PLATFORM |
| #endif |
| |
| #if !defined(QT_NO_STANDARDPATHS) && (defined(Q_XDG_PLATFORM) || defined(QT_PLATFORM_UIKIT)) |
| #define QSETTINGS_USE_QSTANDARDPATHS |
| #endif |
| |
| // ************************************************************************ |
| // QConfFile |
| |
| /* |
| QConfFile objects are explicitly shared within the application. |
| This ensures that modification to the settings done through one |
| QSettings object are immediately reflected in other setting |
| objects of the same application. |
| */ |
| |
| QT_BEGIN_NAMESPACE |
| |
| struct QConfFileCustomFormat |
| { |
| QString extension; |
| QSettings::ReadFunc readFunc; |
| QSettings::WriteFunc writeFunc; |
| Qt::CaseSensitivity caseSensitivity; |
| }; |
| Q_DECLARE_TYPEINFO(QConfFileCustomFormat, Q_MOVABLE_TYPE); |
| |
| typedef QHash<QString, QConfFile *> ConfFileHash; |
| typedef QCache<QString, QConfFile> ConfFileCache; |
| namespace { |
| struct Path |
| { |
| // Note: Defining constructors explicitly because of buggy C++11 |
| // implementation in MSVC (uniform initialization). |
| Path() {} |
| Path(const QString & p, bool ud) : path(p), userDefined(ud) {} |
| QString path; |
| bool userDefined; //!< true - user defined, overridden by setPath |
| }; |
| } |
| typedef QHash<int, Path> PathHash; |
| typedef QVector<QConfFileCustomFormat> CustomFormatVector; |
| |
| Q_GLOBAL_STATIC(ConfFileHash, usedHashFunc) |
| Q_GLOBAL_STATIC(ConfFileCache, unusedCacheFunc) |
| Q_GLOBAL_STATIC(PathHash, pathHashFunc) |
| Q_GLOBAL_STATIC(CustomFormatVector, customFormatVectorFunc) |
| |
| static QBasicMutex settingsGlobalMutex; |
| |
| static QSettings::Format globalDefaultFormat = QSettings::NativeFormat; |
| |
| QConfFile::QConfFile(const QString &fileName, bool _userPerms) |
| : name(fileName), size(0), ref(1), userPerms(_userPerms) |
| { |
| usedHashFunc()->insert(name, this); |
| } |
| |
| QConfFile::~QConfFile() |
| { |
| if (usedHashFunc()) |
| usedHashFunc()->remove(name); |
| } |
| |
| ParsedSettingsMap QConfFile::mergedKeyMap() const |
| { |
| ParsedSettingsMap result = originalKeys; |
| ParsedSettingsMap::const_iterator i; |
| |
| for (i = removedKeys.begin(); i != removedKeys.end(); ++i) |
| result.remove(i.key()); |
| for (i = addedKeys.begin(); i != addedKeys.end(); ++i) |
| result.insert(i.key(), i.value()); |
| return result; |
| } |
| |
| bool QConfFile::isWritable() const |
| { |
| QFileInfo fileInfo(name); |
| |
| #ifndef QT_NO_TEMPORARYFILE |
| if (fileInfo.exists()) { |
| #endif |
| QFile file(name); |
| return file.open(QFile::ReadWrite); |
| #ifndef QT_NO_TEMPORARYFILE |
| } else { |
| // Create the directories to the file. |
| QDir dir(fileInfo.absolutePath()); |
| if (!dir.exists()) { |
| if (!dir.mkpath(dir.absolutePath())) |
| return false; |
| } |
| |
| // we use a temporary file to avoid race conditions |
| QTemporaryFile file(name); |
| return file.open(); |
| } |
| #endif |
| } |
| |
| QConfFile *QConfFile::fromName(const QString &fileName, bool _userPerms) |
| { |
| QString absPath = QFileInfo(fileName).absoluteFilePath(); |
| |
| ConfFileHash *usedHash = usedHashFunc(); |
| ConfFileCache *unusedCache = unusedCacheFunc(); |
| |
| QConfFile *confFile = nullptr; |
| const auto locker = qt_scoped_lock(settingsGlobalMutex); |
| |
| if (!(confFile = usedHash->value(absPath))) { |
| if ((confFile = unusedCache->take(absPath))) |
| usedHash->insert(absPath, confFile); |
| } |
| if (confFile) { |
| confFile->ref.ref(); |
| return confFile; |
| } |
| return new QConfFile(absPath, _userPerms); |
| } |
| |
| void QConfFile::clearCache() |
| { |
| const auto locker = qt_scoped_lock(settingsGlobalMutex); |
| unusedCacheFunc()->clear(); |
| } |
| |
| // ************************************************************************ |
| // QSettingsPrivate |
| |
| QSettingsPrivate::QSettingsPrivate(QSettings::Format format) |
| : format(format), scope(QSettings::UserScope /* nothing better to put */), iniCodec(nullptr), fallbacks(true), |
| pendingChanges(false), status(QSettings::NoError) |
| { |
| } |
| |
| QSettingsPrivate::QSettingsPrivate(QSettings::Format format, QSettings::Scope scope, |
| const QString &organization, const QString &application) |
| : format(format), scope(scope), organizationName(organization), applicationName(application), |
| iniCodec(nullptr), fallbacks(true), pendingChanges(false), status(QSettings::NoError) |
| { |
| } |
| |
| QSettingsPrivate::~QSettingsPrivate() |
| { |
| } |
| |
| QString QSettingsPrivate::actualKey(const QString &key) const |
| { |
| QString n = normalizedKey(key); |
| Q_ASSERT_X(!n.isEmpty(), "QSettings", "empty key"); |
| return groupPrefix + n; |
| } |
| |
| /* |
| Returns a string that never starts nor ends with a slash (or an |
| empty string). Examples: |
| |
| "foo" becomes "foo" |
| "/foo//bar///" becomes "foo/bar" |
| "///" becomes "" |
| |
| This function is optimized to avoid a QString deep copy in the |
| common case where the key is already normalized. |
| */ |
| QString QSettingsPrivate::normalizedKey(const QString &key) |
| { |
| QString result = key; |
| |
| int i = 0; |
| while (i < result.size()) { |
| while (result.at(i) == QLatin1Char('/')) { |
| result.remove(i, 1); |
| if (i == result.size()) |
| goto after_loop; |
| } |
| while (result.at(i) != QLatin1Char('/')) { |
| ++i; |
| if (i == result.size()) |
| return result; |
| } |
| ++i; // leave the slash alone |
| } |
| |
| after_loop: |
| if (!result.isEmpty()) |
| result.truncate(i - 1); // remove the trailing slash |
| return result; |
| } |
| |
| // see also qsettings_win.cpp, qsettings_winrt.cpp and qsettings_mac.cpp |
| |
| #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_WASM) |
| QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope, |
| const QString &organization, const QString &application) |
| { |
| return new QConfFileSettingsPrivate(format, scope, organization, application); |
| } |
| #endif |
| |
| #if !defined(Q_OS_WIN) |
| QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format) |
| { |
| return new QConfFileSettingsPrivate(fileName, format); |
| } |
| #endif |
| |
| void QSettingsPrivate::processChild(QStringRef key, ChildSpec spec, QStringList &result) |
| { |
| if (spec != AllKeys) { |
| int slashPos = key.indexOf(QLatin1Char('/')); |
| if (slashPos == -1) { |
| if (spec != ChildKeys) |
| return; |
| } else { |
| if (spec != ChildGroups) |
| return; |
| key.truncate(slashPos); |
| } |
| } |
| result.append(key.toString()); |
| } |
| |
| void QSettingsPrivate::beginGroupOrArray(const QSettingsGroup &group) |
| { |
| groupStack.push(group); |
| const QString name = group.name(); |
| if (!name.isEmpty()) |
| groupPrefix += name + QLatin1Char('/'); |
| } |
| |
| /* |
| We only set an error if there isn't one set already. This way the user always gets the |
| first error that occurred. We always allow clearing errors. |
| */ |
| |
| void QSettingsPrivate::setStatus(QSettings::Status status) const |
| { |
| if (status == QSettings::NoError || this->status == QSettings::NoError) |
| this->status = status; |
| } |
| |
| void QSettingsPrivate::update() |
| { |
| flush(); |
| pendingChanges = false; |
| } |
| |
| void QSettingsPrivate::requestUpdate() |
| { |
| if (!pendingChanges) { |
| pendingChanges = true; |
| #ifndef QT_NO_QOBJECT |
| Q_Q(QSettings); |
| QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); |
| #else |
| update(); |
| #endif |
| } |
| } |
| |
| QStringList QSettingsPrivate::variantListToStringList(const QVariantList &l) |
| { |
| QStringList result; |
| result.reserve(l.count()); |
| QVariantList::const_iterator it = l.constBegin(); |
| for (; it != l.constEnd(); ++it) |
| result.append(variantToString(*it)); |
| return result; |
| } |
| |
| QVariant QSettingsPrivate::stringListToVariantList(const QStringList &l) |
| { |
| QStringList outStringList = l; |
| for (int i = 0; i < outStringList.count(); ++i) { |
| const QString &str = outStringList.at(i); |
| |
| if (str.startsWith(QLatin1Char('@'))) { |
| if (str.length() >= 2 && str.at(1) == QLatin1Char('@')) { |
| outStringList[i].remove(0, 1); |
| } else { |
| QVariantList variantList; |
| const int stringCount = l.count(); |
| variantList.reserve(stringCount); |
| for (int j = 0; j < stringCount; ++j) |
| variantList.append(stringToVariant(l.at(j))); |
| return variantList; |
| } |
| } |
| } |
| return outStringList; |
| } |
| |
| QString QSettingsPrivate::variantToString(const QVariant &v) |
| { |
| QString result; |
| |
| switch (v.userType()) { |
| case QMetaType::UnknownType: |
| result = QLatin1String("@Invalid()"); |
| break; |
| |
| case QMetaType::QByteArray: { |
| QByteArray a = v.toByteArray(); |
| result = QLatin1String("@ByteArray(") |
| + QLatin1String(a.constData(), a.size()) |
| + QLatin1Char(')'); |
| break; |
| } |
| |
| case QMetaType::QString: |
| case QMetaType::LongLong: |
| case QMetaType::ULongLong: |
| case QMetaType::Int: |
| case QMetaType::UInt: |
| case QMetaType::Bool: |
| case QMetaType::Double: |
| case QMetaType::QKeySequence: { |
| result = v.toString(); |
| if (result.contains(QChar::Null)) |
| result = QLatin1String("@String(") + result + QLatin1Char(')'); |
| else if (result.startsWith(QLatin1Char('@'))) |
| result.prepend(QLatin1Char('@')); |
| break; |
| } |
| #ifndef QT_NO_GEOM_VARIANT |
| case QMetaType::QRect: { |
| QRect r = qvariant_cast<QRect>(v); |
| result = QString::asprintf("@Rect(%d %d %d %d)", r.x(), r.y(), r.width(), r.height()); |
| break; |
| } |
| case QMetaType::QSize: { |
| QSize s = qvariant_cast<QSize>(v); |
| result = QString::asprintf("@Size(%d %d)", s.width(), s.height()); |
| break; |
| } |
| case QMetaType::QPoint: { |
| QPoint p = qvariant_cast<QPoint>(v); |
| result = QString::asprintf("@Point(%d %d)", p.x(), p.y()); |
| break; |
| } |
| #endif // !QT_NO_GEOM_VARIANT |
| |
| default: { |
| #ifndef QT_NO_DATASTREAM |
| QDataStream::Version version; |
| const char *typeSpec; |
| if (v.userType() == QMetaType::QDateTime) { |
| version = QDataStream::Qt_5_6; |
| typeSpec = "@DateTime("; |
| } else { |
| version = QDataStream::Qt_4_0; |
| typeSpec = "@Variant("; |
| } |
| QByteArray a; |
| { |
| QDataStream s(&a, QIODevice::WriteOnly); |
| s.setVersion(version); |
| s << v; |
| } |
| |
| result = QLatin1String(typeSpec) |
| + QLatin1String(a.constData(), a.size()) |
| + QLatin1Char(')'); |
| #else |
| Q_ASSERT(!"QSettings: Cannot save custom types without QDataStream support"); |
| #endif |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| QVariant QSettingsPrivate::stringToVariant(const QString &s) |
| { |
| if (s.startsWith(QLatin1Char('@'))) { |
| if (s.endsWith(QLatin1Char(')'))) { |
| if (s.startsWith(QLatin1String("@ByteArray("))) { |
| return QVariant(s.midRef(11, s.size() - 12).toLatin1()); |
| } else if (s.startsWith(QLatin1String("@String("))) { |
| return QVariant(s.midRef(8, s.size() - 9).toString()); |
| } else if (s.startsWith(QLatin1String("@Variant(")) |
| || s.startsWith(QLatin1String("@DateTime("))) { |
| #ifndef QT_NO_DATASTREAM |
| QDataStream::Version version; |
| int offset; |
| if (s.at(1) == QLatin1Char('D')) { |
| version = QDataStream::Qt_5_6; |
| offset = 10; |
| } else { |
| version = QDataStream::Qt_4_0; |
| offset = 9; |
| } |
| QByteArray a = s.midRef(offset).toLatin1(); |
| QDataStream stream(&a, QIODevice::ReadOnly); |
| stream.setVersion(version); |
| QVariant result; |
| stream >> result; |
| return result; |
| #else |
| Q_ASSERT(!"QSettings: Cannot load custom types without QDataStream support"); |
| #endif |
| #ifndef QT_NO_GEOM_VARIANT |
| } else if (s.startsWith(QLatin1String("@Rect("))) { |
| QStringList args = QSettingsPrivate::splitArgs(s, 5); |
| if (args.size() == 4) |
| return QVariant(QRect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt())); |
| } else if (s.startsWith(QLatin1String("@Size("))) { |
| QStringList args = QSettingsPrivate::splitArgs(s, 5); |
| if (args.size() == 2) |
| return QVariant(QSize(args[0].toInt(), args[1].toInt())); |
| } else if (s.startsWith(QLatin1String("@Point("))) { |
| QStringList args = QSettingsPrivate::splitArgs(s, 6); |
| if (args.size() == 2) |
| return QVariant(QPoint(args[0].toInt(), args[1].toInt())); |
| #endif |
| } else if (s == QLatin1String("@Invalid()")) { |
| return QVariant(); |
| } |
| |
| } |
| if (s.startsWith(QLatin1String("@@"))) |
| return QVariant(s.mid(1)); |
| } |
| |
| return QVariant(s); |
| } |
| |
| static const char hexDigits[] = "0123456789ABCDEF"; |
| |
| void QSettingsPrivate::iniEscapedKey(const QString &key, QByteArray &result) |
| { |
| result.reserve(result.length() + key.length() * 3 / 2); |
| for (int i = 0; i < key.size(); ++i) { |
| uint ch = key.at(i).unicode(); |
| |
| if (ch == '/') { |
| result += '\\'; |
| } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') |
| || ch == '_' || ch == '-' || ch == '.') { |
| result += (char)ch; |
| } else if (ch <= 0xFF) { |
| result += '%'; |
| result += hexDigits[ch / 16]; |
| result += hexDigits[ch % 16]; |
| } else { |
| result += "%U"; |
| QByteArray hexCode; |
| for (int i = 0; i < 4; ++i) { |
| hexCode.prepend(hexDigits[ch % 16]); |
| ch >>= 4; |
| } |
| result += hexCode; |
| } |
| } |
| } |
| |
| bool QSettingsPrivate::iniUnescapedKey(const QByteArray &key, int from, int to, QString &result) |
| { |
| bool lowercaseOnly = true; |
| int i = from; |
| result.reserve(result.length() + (to - from)); |
| while (i < to) { |
| int ch = (uchar)key.at(i); |
| |
| if (ch == '\\') { |
| result += QLatin1Char('/'); |
| ++i; |
| continue; |
| } |
| |
| if (ch != '%' || i == to - 1) { |
| if (uint(ch - 'A') <= 'Z' - 'A') // only for ASCII |
| lowercaseOnly = false; |
| result += QLatin1Char(ch); |
| ++i; |
| continue; |
| } |
| |
| int numDigits = 2; |
| int firstDigitPos = i + 1; |
| |
| ch = key.at(i + 1); |
| if (ch == 'U') { |
| ++firstDigitPos; |
| numDigits = 4; |
| } |
| |
| if (firstDigitPos + numDigits > to) { |
| result += QLatin1Char('%'); |
| // ### missing U |
| ++i; |
| continue; |
| } |
| |
| bool ok; |
| ch = key.mid(firstDigitPos, numDigits).toInt(&ok, 16); |
| if (!ok) { |
| result += QLatin1Char('%'); |
| // ### missing U |
| ++i; |
| continue; |
| } |
| |
| QChar qch(ch); |
| if (qch.isUpper()) |
| lowercaseOnly = false; |
| result += qch; |
| i = firstDigitPos + numDigits; |
| } |
| return lowercaseOnly; |
| } |
| |
| void QSettingsPrivate::iniEscapedString(const QString &str, QByteArray &result, QTextCodec *codec) |
| { |
| bool needsQuotes = false; |
| bool escapeNextIfDigit = false; |
| bool useCodec = codec && !str.startsWith(QLatin1String("@ByteArray(")) |
| && !str.startsWith(QLatin1String("@Variant(")); |
| |
| int i; |
| int startPos = result.size(); |
| |
| result.reserve(startPos + str.size() * 3 / 2); |
| const QChar *unicode = str.unicode(); |
| for (i = 0; i < str.size(); ++i) { |
| uint ch = unicode[i].unicode(); |
| if (ch == ';' || ch == ',' || ch == '=') |
| needsQuotes = true; |
| |
| if (escapeNextIfDigit |
| && ((ch >= '0' && ch <= '9') |
| || (ch >= 'a' && ch <= 'f') |
| || (ch >= 'A' && ch <= 'F'))) { |
| result += "\\x" + QByteArray::number(ch, 16); |
| continue; |
| } |
| |
| escapeNextIfDigit = false; |
| |
| switch (ch) { |
| case '\0': |
| result += "\\0"; |
| escapeNextIfDigit = true; |
| break; |
| case '\a': |
| result += "\\a"; |
| break; |
| case '\b': |
| result += "\\b"; |
| break; |
| case '\f': |
| result += "\\f"; |
| break; |
| case '\n': |
| result += "\\n"; |
| break; |
| case '\r': |
| result += "\\r"; |
| break; |
| case '\t': |
| result += "\\t"; |
| break; |
| case '\v': |
| result += "\\v"; |
| break; |
| case '"': |
| case '\\': |
| result += '\\'; |
| result += (char)ch; |
| break; |
| default: |
| if (ch <= 0x1F || (ch >= 0x7F && !useCodec)) { |
| result += "\\x" + QByteArray::number(ch, 16); |
| escapeNextIfDigit = true; |
| #if QT_CONFIG(textcodec) |
| } else if (useCodec) { |
| // slow |
| result += codec->fromUnicode(&unicode[i], 1); |
| #endif |
| } else { |
| result += (char)ch; |
| } |
| } |
| } |
| |
| if (needsQuotes |
| || (startPos < result.size() && (result.at(startPos) == ' ' |
| || result.at(result.size() - 1) == ' '))) { |
| result.insert(startPos, '"'); |
| result += '"'; |
| } |
| } |
| |
| inline static void iniChopTrailingSpaces(QString &str, int limit) |
| { |
| int n = str.size() - 1; |
| QChar ch; |
| while (n >= limit && ((ch = str.at(n)) == QLatin1Char(' ') || ch == QLatin1Char('\t'))) |
| str.truncate(n--); |
| } |
| |
| void QSettingsPrivate::iniEscapedStringList(const QStringList &strs, QByteArray &result, QTextCodec *codec) |
| { |
| if (strs.isEmpty()) { |
| /* |
| We need to distinguish between empty lists and one-item |
| lists that contain an empty string. Ideally, we'd have a |
| @EmptyList() symbol but that would break compatibility |
| with Qt 4.0. @Invalid() stands for QVariant(), and |
| QVariant().toStringList() returns an empty QStringList, |
| so we're in good shape. |
| */ |
| result += "@Invalid()"; |
| } else { |
| for (int i = 0; i < strs.size(); ++i) { |
| if (i != 0) |
| result += ", "; |
| iniEscapedString(strs.at(i), result, codec); |
| } |
| } |
| } |
| |
| bool QSettingsPrivate::iniUnescapedStringList(const QByteArray &str, int from, int to, |
| QString &stringResult, QStringList &stringListResult, |
| QTextCodec *codec) |
| { |
| static const char escapeCodes[][2] = |
| { |
| { 'a', '\a' }, |
| { 'b', '\b' }, |
| { 'f', '\f' }, |
| { 'n', '\n' }, |
| { 'r', '\r' }, |
| { 't', '\t' }, |
| { 'v', '\v' }, |
| { '"', '"' }, |
| { '?', '?' }, |
| { '\'', '\'' }, |
| { '\\', '\\' } |
| }; |
| |
| bool isStringList = false; |
| bool inQuotedString = false; |
| bool currentValueIsQuoted = false; |
| char16_t escapeVal = 0; |
| int i = from; |
| char ch; |
| |
| StSkipSpaces: |
| while (i < to && ((ch = str.at(i)) == ' ' || ch == '\t')) |
| ++i; |
| // fallthrough |
| |
| StNormal: |
| int chopLimit = stringResult.length(); |
| while (i < to) { |
| switch (str.at(i)) { |
| case '\\': |
| ++i; |
| if (i >= to) |
| goto end; |
| |
| ch = str.at(i++); |
| for (const auto &escapeCode : escapeCodes) { |
| if (ch == escapeCode[0]) { |
| stringResult += QLatin1Char(escapeCode[1]); |
| goto StNormal; |
| } |
| } |
| |
| if (ch == 'x') { |
| escapeVal = 0; |
| |
| if (i >= to) |
| goto end; |
| |
| ch = str.at(i); |
| if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) |
| goto StHexEscape; |
| } else if (ch >= '0' && ch <= '7') { |
| escapeVal = ch - '0'; |
| goto StOctEscape; |
| } else if (ch == '\n' || ch == '\r') { |
| if (i < to) { |
| char ch2 = str.at(i); |
| // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files |
| if ((ch2 == '\n' || ch2 == '\r') && ch2 != ch) |
| ++i; |
| } |
| } else { |
| // the character is skipped |
| } |
| chopLimit = stringResult.length(); |
| break; |
| case '"': |
| ++i; |
| currentValueIsQuoted = true; |
| inQuotedString = !inQuotedString; |
| if (!inQuotedString) |
| goto StSkipSpaces; |
| break; |
| case ',': |
| if (!inQuotedString) { |
| if (!currentValueIsQuoted) |
| iniChopTrailingSpaces(stringResult, chopLimit); |
| if (!isStringList) { |
| isStringList = true; |
| stringListResult.clear(); |
| stringResult.squeeze(); |
| } |
| stringListResult.append(stringResult); |
| stringResult.clear(); |
| currentValueIsQuoted = false; |
| ++i; |
| goto StSkipSpaces; |
| } |
| Q_FALLTHROUGH(); |
| default: { |
| int j = i + 1; |
| while (j < to) { |
| ch = str.at(j); |
| if (ch == '\\' || ch == '"' || ch == ',') |
| break; |
| ++j; |
| } |
| |
| #if !QT_CONFIG(textcodec) |
| Q_UNUSED(codec) |
| #else |
| if (codec) { |
| stringResult += codec->toUnicode(str.constData() + i, j - i); |
| } else |
| #endif |
| { |
| int n = stringResult.size(); |
| stringResult.resize(n + (j - i)); |
| QChar *resultData = stringResult.data() + n; |
| for (int k = i; k < j; ++k) |
| *resultData++ = QLatin1Char(str.at(k)); |
| } |
| i = j; |
| } |
| } |
| } |
| if (!currentValueIsQuoted) |
| iniChopTrailingSpaces(stringResult, chopLimit); |
| goto end; |
| |
| StHexEscape: |
| if (i >= to) { |
| stringResult += QChar(escapeVal); |
| goto end; |
| } |
| |
| ch = str.at(i); |
| if (ch >= 'a') |
| ch -= 'a' - 'A'; |
| if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) { |
| escapeVal <<= 4; |
| escapeVal += strchr(hexDigits, ch) - hexDigits; |
| ++i; |
| goto StHexEscape; |
| } else { |
| stringResult += QChar(escapeVal); |
| goto StNormal; |
| } |
| |
| StOctEscape: |
| if (i >= to) { |
| stringResult += QChar(escapeVal); |
| goto end; |
| } |
| |
| ch = str.at(i); |
| if (ch >= '0' && ch <= '7') { |
| escapeVal <<= 3; |
| escapeVal += ch - '0'; |
| ++i; |
| goto StOctEscape; |
| } else { |
| stringResult += QChar(escapeVal); |
| goto StNormal; |
| } |
| |
| end: |
| if (isStringList) |
| stringListResult.append(stringResult); |
| return isStringList; |
| } |
| |
| QStringList QSettingsPrivate::splitArgs(const QString &s, int idx) |
| { |
| int l = s.length(); |
| Q_ASSERT(l > 0); |
| Q_ASSERT(s.at(idx) == QLatin1Char('(')); |
| Q_ASSERT(s.at(l - 1) == QLatin1Char(')')); |
| |
| QStringList result; |
| QString item; |
| |
| for (++idx; idx < l; ++idx) { |
| QChar c = s.at(idx); |
| if (c == QLatin1Char(')')) { |
| Q_ASSERT(idx == l - 1); |
| result.append(item); |
| } else if (c == QLatin1Char(' ')) { |
| result.append(item); |
| item.clear(); |
| } else { |
| item.append(c); |
| } |
| } |
| |
| return result; |
| } |
| |
| // ************************************************************************ |
| // QConfFileSettingsPrivate |
| |
| void QConfFileSettingsPrivate::initFormat() |
| { |
| extension = (format == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"); |
| readFunc = nullptr; |
| writeFunc = nullptr; |
| #if defined(Q_OS_MAC) |
| caseSensitivity = (format == QSettings::NativeFormat) ? Qt::CaseSensitive : IniCaseSensitivity; |
| #else |
| caseSensitivity = IniCaseSensitivity; |
| #endif |
| |
| if (format > QSettings::IniFormat) { |
| const auto locker = qt_scoped_lock(settingsGlobalMutex); |
| const CustomFormatVector *customFormatVector = customFormatVectorFunc(); |
| |
| int i = (int)format - (int)QSettings::CustomFormat1; |
| if (i >= 0 && i < customFormatVector->size()) { |
| QConfFileCustomFormat info = customFormatVector->at(i); |
| extension = info.extension; |
| readFunc = info.readFunc; |
| writeFunc = info.writeFunc; |
| caseSensitivity = info.caseSensitivity; |
| } |
| } |
| } |
| |
| void QConfFileSettingsPrivate::initAccess() |
| { |
| if (!confFiles.isEmpty()) { |
| if (format > QSettings::IniFormat) { |
| if (!readFunc) |
| setStatus(QSettings::AccessError); |
| } |
| } |
| |
| sync(); // loads the files the first time |
| } |
| |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| static QString windowsConfigPath(const KNOWNFOLDERID &type) |
| { |
| QString result; |
| |
| PWSTR path = nullptr; |
| if (SHGetKnownFolderPath(type, KF_FLAG_DONT_VERIFY, NULL, &path) == S_OK) { |
| result = QString::fromWCharArray(path); |
| CoTaskMemFree(path); |
| } |
| |
| if (result.isEmpty()) { |
| if (type == FOLDERID_ProgramData) { |
| result = QLatin1String("C:\\temp\\qt-common"); |
| } else if (type == FOLDERID_RoamingAppData) { |
| result = QLatin1String("C:\\temp\\qt-user"); |
| } |
| } |
| |
| return result; |
| } |
| #elif defined(Q_OS_WINRT) // Q_OS_WIN && !Q_OS_WINRT |
| |
| enum ConfigPathType { |
| ConfigPath_CommonAppData, |
| ConfigPath_UserAppData |
| }; |
| |
| static QString windowsConfigPath(ConfigPathType type) |
| { |
| static QString result; |
| while (result.isEmpty()) { |
| ComPtr<IApplicationDataStatics> applicationDataStatics; |
| if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics))) |
| return result; |
| ComPtr<IApplicationData> applicationData; |
| if (FAILED(applicationDataStatics->get_Current(&applicationData))) |
| return result; |
| ComPtr<IStorageFolder> localFolder; |
| if (FAILED(applicationData->get_LocalFolder(&localFolder))) |
| return result; |
| ComPtr<IStorageItem> localFolderItem; |
| if (FAILED(localFolder.As(&localFolderItem))) |
| return result; |
| HString path; |
| if (FAILED(localFolderItem->get_Path(path.GetAddressOf()))) |
| return result; |
| result = QString::fromWCharArray(path.GetRawBuffer(nullptr)); |
| } |
| |
| switch (type) { |
| case ConfigPath_CommonAppData: |
| return result + QLatin1String("\\qt-common"); |
| case ConfigPath_UserAppData: |
| return result + QLatin1String("\\qt-user"); |
| } |
| return result; |
| } |
| #endif // Q_OS_WINRT |
| |
| static inline int pathHashKey(QSettings::Format format, QSettings::Scope scope) |
| { |
| return int((uint(format) << 1) | uint(scope == QSettings::SystemScope)); |
| } |
| |
| #ifndef Q_OS_WIN |
| static QString make_user_path() |
| { |
| static Q_CONSTEXPR QChar sep = QLatin1Char('/'); |
| #ifndef QSETTINGS_USE_QSTANDARDPATHS |
| // Non XDG platforms (OS X, iOS, Android...) have used this code path erroneously |
| // for some time now. Moving away from that would require migrating existing settings. |
| QByteArray env = qgetenv("XDG_CONFIG_HOME"); |
| if (env.isEmpty()) { |
| return QDir::homePath() + QLatin1String("/.config/"); |
| } else if (env.startsWith('/')) { |
| return QFile::decodeName(env) + sep; |
| } else { |
| return QDir::homePath() + sep + QFile::decodeName(env) + sep; |
| } |
| #else |
| // When using a proper XDG platform, use QStandardPaths rather than the above hand-written code; |
| // it makes the use of test mode from unit tests possible. |
| // Ideally all platforms should use this, but see above for the migration issue. |
| return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + sep; |
| #endif |
| } |
| #endif // !Q_OS_WIN |
| |
| static std::unique_lock<QBasicMutex> initDefaultPaths(std::unique_lock<QBasicMutex> locker) |
| { |
| PathHash *pathHash = pathHashFunc(); |
| |
| locker.unlock(); |
| |
| /* |
| QLibraryInfo::location() uses QSettings, so in order to |
| avoid a dead-lock, we can't hold the global mutex while |
| calling it. |
| */ |
| QString systemPath = QLibraryInfo::location(QLibraryInfo::SettingsPath) + QLatin1Char('/'); |
| |
| locker.lock(); |
| if (pathHash->isEmpty()) { |
| /* |
| Lazy initialization of pathHash. We initialize the |
| IniFormat paths and (on Unix) the NativeFormat paths. |
| (The NativeFormat paths are not configurable for the |
| Windows registry and the Mac CFPreferences.) |
| */ |
| #ifdef Q_OS_WIN |
| |
| # ifdef Q_OS_WINRT |
| const QString roamingAppDataFolder = windowsConfigPath(ConfigPath_UserAppData); |
| const QString programDataFolder = windowsConfigPath(ConfigPath_CommonAppData); |
| # else |
| const QString roamingAppDataFolder = windowsConfigPath(FOLDERID_RoamingAppData); |
| const QString programDataFolder = windowsConfigPath(FOLDERID_ProgramData); |
| # endif |
| pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), |
| Path(roamingAppDataFolder + QDir::separator(), false)); |
| pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), |
| Path(programDataFolder + QDir::separator(), false)); |
| #else |
| const QString userPath = make_user_path(); |
| pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), Path(userPath, false)); |
| pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), Path(systemPath, false)); |
| #ifndef Q_OS_MAC |
| pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), Path(userPath, false)); |
| pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), Path(systemPath, false)); |
| #endif |
| #endif // Q_OS_WIN |
| } |
| |
| return locker; |
| } |
| |
| static Path getPath(QSettings::Format format, QSettings::Scope scope) |
| { |
| Q_ASSERT((int)QSettings::NativeFormat == 0); |
| Q_ASSERT((int)QSettings::IniFormat == 1); |
| |
| auto locker = qt_unique_lock(settingsGlobalMutex); |
| PathHash *pathHash = pathHashFunc(); |
| if (pathHash->isEmpty()) |
| locker = initDefaultPaths(std::move(locker)); |
| |
| Path result = pathHash->value(pathHashKey(format, scope)); |
| if (!result.path.isEmpty()) |
| return result; |
| |
| // fall back on INI path |
| return pathHash->value(pathHashKey(QSettings::IniFormat, scope)); |
| } |
| |
| #if defined(QT_BUILD_INTERNAL) && defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS) |
| // Note: Suitable only for autotests. |
| void Q_AUTOTEST_EXPORT clearDefaultPaths() |
| { |
| const auto locker = qt_scoped_lock(settingsGlobalMutex); |
| pathHashFunc()->clear(); |
| } |
| #endif // QT_BUILD_INTERNAL && Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS |
| |
| QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, |
| QSettings::Scope scope, |
| const QString &organization, |
| const QString &application) |
| : QSettingsPrivate(format, scope, organization, application), |
| nextPosition(0x40000000) // big positive number |
| { |
| initFormat(); |
| |
| QString org = organization; |
| if (org.isEmpty()) { |
| setStatus(QSettings::AccessError); |
| org = QLatin1String("Unknown Organization"); |
| } |
| |
| QString appFile = org + QDir::separator() + application + extension; |
| QString orgFile = org + extension; |
| |
| if (scope == QSettings::UserScope) { |
| Path userPath = getPath(format, QSettings::UserScope); |
| if (!application.isEmpty()) |
| confFiles.append(QConfFile::fromName(userPath.path + appFile, true)); |
| confFiles.append(QConfFile::fromName(userPath.path + orgFile, true)); |
| } |
| |
| Path systemPath = getPath(format, QSettings::SystemScope); |
| #if defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS) |
| // check if the systemPath wasn't overridden by QSettings::setPath() |
| if (!systemPath.userDefined) { |
| // Note: We can't use QStandardPaths::locateAll() as we need all the |
| // possible files (not just the existing ones) and there is no way |
| // to exclude user specific (XDG_CONFIG_HOME) directory from the search. |
| QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); |
| // remove the QStandardLocation::writableLocation() (XDG_CONFIG_HOME) |
| if (!dirs.isEmpty()) |
| dirs.takeFirst(); |
| QStringList paths; |
| if (!application.isEmpty()) { |
| paths.reserve(dirs.size() * 2); |
| for (const auto &dir : qAsConst(dirs)) |
| paths.append(dir + QLatin1Char('/') + appFile); |
| } else { |
| paths.reserve(dirs.size()); |
| } |
| for (const auto &dir : qAsConst(dirs)) |
| paths.append(dir + QLatin1Char('/') + orgFile); |
| |
| // Note: No check for existence of files is done intentionaly. |
| for (const auto &path : qAsConst(paths)) |
| confFiles.append(QConfFile::fromName(path, false)); |
| } else |
| #endif // Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS |
| { |
| if (!application.isEmpty()) |
| confFiles.append(QConfFile::fromName(systemPath.path + appFile, false)); |
| confFiles.append(QConfFile::fromName(systemPath.path + orgFile, false)); |
| } |
| |
| #ifndef Q_OS_WASM // wasm needs to delay access until after file sync |
| initAccess(); |
| #endif |
| } |
| |
| QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName, |
| QSettings::Format format) |
| : QSettingsPrivate(format), |
| nextPosition(0x40000000) // big positive number |
| { |
| initFormat(); |
| |
| confFiles.append(QConfFile::fromName(fileName, true)); |
| |
| initAccess(); |
| } |
| |
| QConfFileSettingsPrivate::~QConfFileSettingsPrivate() |
| { |
| const auto locker = qt_scoped_lock(settingsGlobalMutex); |
| ConfFileHash *usedHash = usedHashFunc(); |
| ConfFileCache *unusedCache = unusedCacheFunc(); |
| |
| for (auto conf_file : qAsConst(confFiles)) { |
| if (!conf_file->ref.deref()) { |
| if (conf_file->size == 0) { |
| delete conf_file; |
| } else { |
| if (usedHash) |
| usedHash->remove(conf_file->name); |
| if (unusedCache) { |
| QT_TRY { |
| // compute a better size? |
| unusedCache->insert(conf_file->name, conf_file, |
| 10 + (conf_file->originalKeys.size() / 4)); |
| } QT_CATCH(...) { |
| // out of memory. Do not cache the file. |
| delete conf_file; |
| } |
| } else { |
| // unusedCache is gone - delete the entry to prevent a memory leak |
| delete conf_file; |
| } |
| } |
| } |
| } |
| } |
| |
| void QConfFileSettingsPrivate::remove(const QString &key) |
| { |
| if (confFiles.isEmpty()) |
| return; |
| |
| // Note: First config file is always the most specific. |
| QConfFile *confFile = confFiles.at(0); |
| |
| QSettingsKey theKey(key, caseSensitivity); |
| QSettingsKey prefix(key + QLatin1Char('/'), caseSensitivity); |
| const auto locker = qt_scoped_lock(confFile->mutex); |
| |
| ensureSectionParsed(confFile, theKey); |
| ensureSectionParsed(confFile, prefix); |
| |
| ParsedSettingsMap::iterator i = confFile->addedKeys.lowerBound(prefix); |
| while (i != confFile->addedKeys.end() && i.key().startsWith(prefix)) |
| i = confFile->addedKeys.erase(i); |
| confFile->addedKeys.remove(theKey); |
| |
| ParsedSettingsMap::const_iterator j = const_cast<const ParsedSettingsMap *>(&confFile->originalKeys)->lowerBound(prefix); |
| while (j != confFile->originalKeys.constEnd() && j.key().startsWith(prefix)) { |
| confFile->removedKeys.insert(j.key(), QVariant()); |
| ++j; |
| } |
| if (confFile->originalKeys.contains(theKey)) |
| confFile->removedKeys.insert(theKey, QVariant()); |
| } |
| |
| void QConfFileSettingsPrivate::set(const QString &key, const QVariant &value) |
| { |
| if (confFiles.isEmpty()) |
| return; |
| |
| // Note: First config file is always the most specific. |
| QConfFile *confFile = confFiles.at(0); |
| |
| QSettingsKey theKey(key, caseSensitivity, nextPosition++); |
| const auto locker = qt_scoped_lock(confFile->mutex); |
| confFile->removedKeys.remove(theKey); |
| confFile->addedKeys.insert(theKey, value); |
| } |
| |
| bool QConfFileSettingsPrivate::get(const QString &key, QVariant *value) const |
| { |
| QSettingsKey theKey(key, caseSensitivity); |
| ParsedSettingsMap::const_iterator j; |
| bool found = false; |
| |
| for (auto confFile : qAsConst(confFiles)) { |
| const auto locker = qt_scoped_lock(confFile->mutex); |
| |
| if (!confFile->addedKeys.isEmpty()) { |
| j = confFile->addedKeys.constFind(theKey); |
| found = (j != confFile->addedKeys.constEnd()); |
| } |
| if (!found) { |
| ensureSectionParsed(confFile, theKey); |
| j = confFile->originalKeys.constFind(theKey); |
| found = (j != confFile->originalKeys.constEnd() |
| && !confFile->removedKeys.contains(theKey)); |
| } |
| |
| if (found && value) |
| *value = *j; |
| |
| if (found) |
| return true; |
| if (!fallbacks) |
| break; |
| } |
| return false; |
| } |
| |
| QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec spec) const |
| { |
| QStringList result; |
| ParsedSettingsMap::const_iterator j; |
| |
| QSettingsKey thePrefix(prefix, caseSensitivity); |
| int startPos = prefix.size(); |
| |
| for (auto confFile : qAsConst(confFiles)) { |
| const auto locker = qt_scoped_lock(confFile->mutex); |
| |
| if (thePrefix.isEmpty()) |
| ensureAllSectionsParsed(confFile); |
| else |
| ensureSectionParsed(confFile, thePrefix); |
| |
| j = const_cast<const ParsedSettingsMap *>( |
| &confFile->originalKeys)->lowerBound( thePrefix); |
| while (j != confFile->originalKeys.constEnd() && j.key().startsWith(thePrefix)) { |
| if (!confFile->removedKeys.contains(j.key())) |
| processChild(j.key().originalCaseKey().midRef(startPos), spec, result); |
| ++j; |
| } |
| |
| j = const_cast<const ParsedSettingsMap *>( |
| &confFile->addedKeys)->lowerBound(thePrefix); |
| while (j != confFile->addedKeys.constEnd() && j.key().startsWith(thePrefix)) { |
| processChild(j.key().originalCaseKey().midRef(startPos), spec, result); |
| ++j; |
| } |
| |
| if (!fallbacks) |
| break; |
| } |
| std::sort(result.begin(), result.end()); |
| result.erase(std::unique(result.begin(), result.end()), |
| result.end()); |
| return result; |
| } |
| |
| void QConfFileSettingsPrivate::clear() |
| { |
| if (confFiles.isEmpty()) |
| return; |
| |
| // Note: First config file is always the most specific. |
| QConfFile *confFile = confFiles.at(0); |
| |
| const auto locker = qt_scoped_lock(confFile->mutex); |
| ensureAllSectionsParsed(confFile); |
| confFile->addedKeys.clear(); |
| confFile->removedKeys = confFile->originalKeys; |
| } |
| |
| void QConfFileSettingsPrivate::sync() |
| { |
| // people probably won't be checking the status a whole lot, so in case of |
| // error we just try to go on and make the best of it |
| |
| for (auto confFile : qAsConst(confFiles)) { |
| const auto locker = qt_scoped_lock(confFile->mutex); |
| syncConfFile(confFile); |
| } |
| } |
| |
| void QConfFileSettingsPrivate::flush() |
| { |
| sync(); |
| } |
| |
| QString QConfFileSettingsPrivate::fileName() const |
| { |
| if (confFiles.isEmpty()) |
| return QString(); |
| |
| // Note: First config file is always the most specific. |
| return confFiles.at(0)->name; |
| } |
| |
| bool QConfFileSettingsPrivate::isWritable() const |
| { |
| if (format > QSettings::IniFormat && !writeFunc) |
| return false; |
| |
| if (confFiles.isEmpty()) |
| return false; |
| |
| return confFiles.at(0)->isWritable(); |
| } |
| |
| void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) |
| { |
| bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty(); |
| |
| /* |
| We can often optimize the read-only case, if the file on disk |
| hasn't changed. |
| */ |
| if (readOnly && confFile->size > 0) { |
| QFileInfo fileInfo(confFile->name); |
| if (confFile->size == fileInfo.size() && confFile->timeStamp == fileInfo.lastModified()) |
| return; |
| } |
| |
| if (!readOnly && !confFile->isWritable()) { |
| setStatus(QSettings::AccessError); |
| return; |
| } |
| |
| #ifndef QT_BOOTSTRAPPED |
| /* |
| Use a lockfile in order to protect us against other QSettings instances |
| trying to write the same settings at the same time. |
| |
| We only need to lock if we are actually writing as only concurrent writes are a problem. |
| Concurrent read and write are not a problem because the writing operation is atomic. |
| */ |
| QLockFile lockFile(confFile->name + QLatin1String(".lock")); |
| if (!readOnly && !lockFile.lock() && atomicSyncOnly) { |
| setStatus(QSettings::AccessError); |
| return; |
| } |
| #endif |
| |
| /* |
| We hold the lock. Let's reread the file if it has changed |
| since last time we read it. |
| */ |
| QFileInfo fileInfo(confFile->name); |
| bool mustReadFile = true; |
| bool createFile = !fileInfo.exists(); |
| |
| if (!readOnly) |
| mustReadFile = (confFile->size != fileInfo.size() |
| || (confFile->size != 0 && confFile->timeStamp != fileInfo.lastModified())); |
| |
| if (mustReadFile) { |
| confFile->unparsedIniSections.clear(); |
| confFile->originalKeys.clear(); |
| |
| QFile file(confFile->name); |
| if (!createFile && !file.open(QFile::ReadOnly)) { |
| setStatus(QSettings::AccessError); |
| return; |
| } |
| |
| /* |
| Files that we can't read (because of permissions or |
| because they don't exist) are treated as empty files. |
| */ |
| if (file.isReadable() && file.size() != 0) { |
| bool ok = false; |
| #ifdef Q_OS_MAC |
| if (format == QSettings::NativeFormat) { |
| QByteArray data = file.readAll(); |
| ok = readPlistFile(data, &confFile->originalKeys); |
| } else |
| #endif |
| if (format <= QSettings::IniFormat) { |
| QByteArray data = file.readAll(); |
| ok = readIniFile(data, &confFile->unparsedIniSections); |
| } else if (readFunc) { |
| QSettings::SettingsMap tempNewKeys; |
| ok = readFunc(file, tempNewKeys); |
| |
| if (ok) { |
| QSettings::SettingsMap::const_iterator i = tempNewKeys.constBegin(); |
| while (i != tempNewKeys.constEnd()) { |
| confFile->originalKeys.insert(QSettingsKey(i.key(), caseSensitivity), |
| i.value()); |
| ++i; |
| } |
| } |
| } |
| |
| if (!ok) |
| setStatus(QSettings::FormatError); |
| } |
| |
| confFile->size = fileInfo.size(); |
| confFile->timeStamp = fileInfo.lastModified(); |
| } |
| |
| /* |
| We also need to save the file. We still hold the file lock, |
| so everything is under control. |
| */ |
| if (!readOnly) { |
| bool ok = false; |
| ensureAllSectionsParsed(confFile); |
| ParsedSettingsMap mergedKeys = confFile->mergedKeyMap(); |
| |
| #if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) |
| QSaveFile sf(confFile->name); |
| sf.setDirectWriteFallback(!atomicSyncOnly); |
| #else |
| QFile sf(confFile->name); |
| #endif |
| if (!sf.open(QIODevice::WriteOnly)) { |
| setStatus(QSettings::AccessError); |
| return; |
| } |
| |
| #ifdef Q_OS_MAC |
| if (format == QSettings::NativeFormat) { |
| ok = writePlistFile(sf, mergedKeys); |
| } else |
| #endif |
| if (format <= QSettings::IniFormat) { |
| ok = writeIniFile(sf, mergedKeys); |
| } else if (writeFunc) { |
| QSettings::SettingsMap tempOriginalKeys; |
| |
| ParsedSettingsMap::const_iterator i = mergedKeys.constBegin(); |
| while (i != mergedKeys.constEnd()) { |
| tempOriginalKeys.insert(i.key(), i.value()); |
| ++i; |
| } |
| ok = writeFunc(sf, tempOriginalKeys); |
| } |
| |
| #if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) |
| if (ok) |
| ok = sf.commit(); |
| #endif |
| |
| if (ok) { |
| confFile->unparsedIniSections.clear(); |
| confFile->originalKeys = mergedKeys; |
| confFile->addedKeys.clear(); |
| confFile->removedKeys.clear(); |
| |
| QFileInfo fileInfo(confFile->name); |
| confFile->size = fileInfo.size(); |
| confFile->timeStamp = fileInfo.lastModified(); |
| |
| // If we have created the file, apply the file perms |
| if (createFile) { |
| QFile::Permissions perms = fileInfo.permissions() | QFile::ReadOwner | QFile::WriteOwner; |
| if (!confFile->userPerms) |
| perms |= QFile::ReadGroup | QFile::ReadOther; |
| QFile(confFile->name).setPermissions(perms); |
| } |
| } else { |
| setStatus(QSettings::AccessError); |
| } |
| } |
| } |
| |
| enum { Space = 0x1, Special = 0x2 }; |
| |
| static const char charTraits[256] = |
| { |
| // Space: '\t', '\n', '\r', ' ' |
| // Special: '\n', '\r', '"', ';', '=', '\\' |
| |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| Space, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, Special, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| }; |
| |
| bool QConfFileSettingsPrivate::readIniLine(const QByteArray &data, int &dataPos, |
| int &lineStart, int &lineLen, int &equalsPos) |
| { |
| int dataLen = data.length(); |
| bool inQuotes = false; |
| |
| equalsPos = -1; |
| |
| lineStart = dataPos; |
| while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space)) |
| ++lineStart; |
| |
| int i = lineStart; |
| while (i < dataLen) { |
| char ch = data.at(i); |
| while (!(charTraits[uchar(ch)] & Special)) { |
| if (++i == dataLen) |
| goto break_out_of_outer_loop; |
| ch = data.at(i); |
| } |
| |
| ++i; |
| if (ch == '=') { |
| if (!inQuotes && equalsPos == -1) |
| equalsPos = i - 1; |
| } else if (ch == '\n' || ch == '\r') { |
| if (i == lineStart + 1) { |
| ++lineStart; |
| } else if (!inQuotes) { |
| --i; |
| goto break_out_of_outer_loop; |
| } |
| } else if (ch == '\\') { |
| if (i < dataLen) { |
| char ch = data.at(i++); |
| if (i < dataLen) { |
| char ch2 = data.at(i); |
| // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files |
| if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n')) |
| ++i; |
| } |
| } |
| } else if (ch == '"') { |
| inQuotes = !inQuotes; |
| } else { |
| Q_ASSERT(ch == ';'); |
| |
| if (i == lineStart + 1) { |
| while (i < dataLen && (((ch = data.at(i)) != '\n') && ch != '\r')) |
| ++i; |
| while (i < dataLen && charTraits[uchar(data.at(i))] & Space) |
| ++i; |
| lineStart = i; |
| } else if (!inQuotes) { |
| --i; |
| goto break_out_of_outer_loop; |
| } |
| } |
| } |
| |
| break_out_of_outer_loop: |
| dataPos = i; |
| lineLen = i - lineStart; |
| return lineLen > 0; |
| } |
| |
| /* |
| Returns \c false on parse error. However, as many keys are read as |
| possible, so if the user doesn't check the status he will get the |
| most out of the file anyway. |
| */ |
| bool QConfFileSettingsPrivate::readIniFile(const QByteArray &data, |
| UnparsedSettingsMap *unparsedIniSections) |
| { |
| #define FLUSH_CURRENT_SECTION() \ |
| { \ |
| QByteArray §ionData = (*unparsedIniSections)[QSettingsKey(currentSection, \ |
| IniCaseSensitivity, \ |
| sectionPosition)]; \ |
| if (!sectionData.isEmpty()) \ |
| sectionData.append('\n'); \ |
| sectionData += data.mid(currentSectionStart, lineStart - currentSectionStart); \ |
| sectionPosition = ++position; \ |
| } |
| |
| QString currentSection; |
| int currentSectionStart = 0; |
| int dataPos = 0; |
| int lineStart; |
| int lineLen; |
| int equalsPos; |
| int position = 0; |
| int sectionPosition = 0; |
| bool ok = true; |
| |
| // detect utf8 BOM |
| const uchar *dd = (const uchar *)data.constData(); |
| if (data.size() >= 3 && dd[0] == 0xef && dd[1] == 0xbb && dd[2] == 0xbf) { |
| #if QT_CONFIG(textcodec) |
| iniCodec = QTextCodec::codecForName("UTF-8"); |
| #else |
| ok = false; |
| #endif |
| dataPos = 3; |
| } |
| |
| while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) { |
| char ch = data.at(lineStart); |
| if (ch == '[') { |
| FLUSH_CURRENT_SECTION(); |
| |
| // this is a section |
| QByteArray iniSection; |
| int idx = data.indexOf(']', lineStart); |
| if (idx == -1 || idx >= lineStart + lineLen) { |
| ok = false; |
| iniSection = data.mid(lineStart + 1, lineLen - 1); |
| } else { |
| iniSection = data.mid(lineStart + 1, idx - lineStart - 1); |
| } |
| |
| iniSection = iniSection.trimmed(); |
| |
| if (iniSection.compare("general", Qt::CaseInsensitive) == 0) { |
| currentSection.clear(); |
| } else { |
| if (iniSection.compare("%general", Qt::CaseInsensitive) == 0) { |
| currentSection = QLatin1String(iniSection.constData() + 1); |
| } else { |
| currentSection.clear(); |
| iniUnescapedKey(iniSection, 0, iniSection.size(), currentSection); |
| } |
| currentSection += QLatin1Char('/'); |
| } |
| currentSectionStart = dataPos; |
| } |
| ++position; |
| } |
| |
| Q_ASSERT(lineStart == data.length()); |
| FLUSH_CURRENT_SECTION(); |
| |
| return ok; |
| |
| #undef FLUSH_CURRENT_SECTION |
| } |
| |
| bool QConfFileSettingsPrivate::readIniSection(const QSettingsKey §ion, const QByteArray &data, |
| ParsedSettingsMap *settingsMap, QTextCodec *codec) |
| { |
| QStringList strListValue; |
| bool sectionIsLowercase = (section == section.originalCaseKey()); |
| int equalsPos; |
| |
| bool ok = true; |
| int dataPos = 0; |
| int lineStart; |
| int lineLen; |
| int position = section.originalKeyPosition(); |
| |
| while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) { |
| char ch = data.at(lineStart); |
| Q_ASSERT(ch != '['); |
| |
| if (equalsPos == -1) { |
| if (ch != ';') |
| ok = false; |
| continue; |
| } |
| |
| int keyEnd = equalsPos; |
| while (keyEnd > lineStart && ((ch = data.at(keyEnd - 1)) == ' ' || ch == '\t')) |
| --keyEnd; |
| int valueStart = equalsPos + 1; |
| |
| QString key = section.originalCaseKey(); |
| bool keyIsLowercase = (iniUnescapedKey(data, lineStart, keyEnd, key) && sectionIsLowercase); |
| |
| QString strValue; |
| strValue.reserve(lineLen - (valueStart - lineStart)); |
| bool isStringList = iniUnescapedStringList(data, valueStart, lineStart + lineLen, |
| strValue, strListValue, codec); |
| QVariant variant; |
| if (isStringList) { |
| variant = stringListToVariantList(strListValue); |
| } else { |
| variant = stringToVariant(strValue); |
| } |
| |
| /* |
| We try to avoid the expensive toLower() call in |
| QSettingsKey by passing Qt::CaseSensitive when the |
| key is already in lowercase. |
| */ |
| settingsMap->insert(QSettingsKey(key, keyIsLowercase ? Qt::CaseSensitive |
| : IniCaseSensitivity, |
| position), |
| variant); |
| ++position; |
| } |
| |
| return ok; |
| } |
| |
| class QSettingsIniKey : public QString |
| { |
| public: |
| inline QSettingsIniKey() : position(-1) {} |
| inline QSettingsIniKey(const QString &str, int pos = -1) : QString(str), position(pos) {} |
| |
| int position; |
| }; |
| Q_DECLARE_TYPEINFO(QSettingsIniKey, Q_MOVABLE_TYPE); |
| |
| static bool operator<(const QSettingsIniKey &k1, const QSettingsIniKey &k2) |
| { |
| if (k1.position != k2.position) |
| return k1.position < k2.position; |
| return static_cast<const QString &>(k1) < static_cast<const QString &>(k2); |
| } |
| |
| typedef QMap<QSettingsIniKey, QVariant> IniKeyMap; |
| |
| struct QSettingsIniSection |
| { |
| int position; |
| IniKeyMap keyMap; |
| |
| inline QSettingsIniSection() : position(-1) {} |
| }; |
| |
| Q_DECLARE_TYPEINFO(QSettingsIniSection, Q_MOVABLE_TYPE); |
| |
| typedef QMap<QString, QSettingsIniSection> IniMap; |
| |
| /* |
| This would be more straightforward if we didn't try to remember the original |
| key order in the .ini file, but we do. |
| */ |
| bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSettingsMap &map) |
| { |
| IniMap iniMap; |
| IniMap::const_iterator i; |
| |
| #ifdef Q_OS_WIN |
| const char * const eol = "\r\n"; |
| #else |
| const char eol = '\n'; |
| #endif |
| |
| for (ParsedSettingsMap::const_iterator j = map.constBegin(); j != map.constEnd(); ++j) { |
| QString section; |
| QSettingsIniKey key(j.key().originalCaseKey(), j.key().originalKeyPosition()); |
| int slashPos; |
| |
| if ((slashPos = key.indexOf(QLatin1Char('/'))) != -1) { |
| section = key.left(slashPos); |
| key.remove(0, slashPos + 1); |
| } |
| |
| QSettingsIniSection &iniSection = iniMap[section]; |
| |
| // -1 means infinity |
| if (uint(key.position) < uint(iniSection.position)) |
| iniSection.position = key.position; |
| iniSection.keyMap[key] = j.value(); |
| } |
| |
| const int sectionCount = iniMap.size(); |
| QVector<QSettingsIniKey> sections; |
| sections.reserve(sectionCount); |
| for (i = iniMap.constBegin(); i != iniMap.constEnd(); ++i) |
| sections.append(QSettingsIniKey(i.key(), i.value().position)); |
| std::sort(sections.begin(), sections.end()); |
| |
| bool writeError = false; |
| for (int j = 0; !writeError && j < sectionCount; ++j) { |
| i = iniMap.constFind(sections.at(j)); |
| Q_ASSERT(i != iniMap.constEnd()); |
| |
| QByteArray realSection; |
| |
| iniEscapedKey(i.key(), realSection); |
| |
| if (realSection.isEmpty()) { |
| realSection = "[General]"; |
| } else if (realSection.compare("general", Qt::CaseInsensitive) == 0) { |
| realSection = "[%General]"; |
| } else { |
| realSection.prepend('['); |
| realSection.append(']'); |
| } |
| |
| if (j != 0) |
| realSection.prepend(eol); |
| realSection += eol; |
| |
| device.write(realSection); |
| |
| const IniKeyMap &ents = i.value().keyMap; |
| for (IniKeyMap::const_iterator j = ents.constBegin(); j != ents.constEnd(); ++j) { |
| QByteArray block; |
| iniEscapedKey(j.key(), block); |
| block += '='; |
| |
| const QVariant &value = j.value(); |
| |
| /* |
| The size() != 1 trick is necessary because |
| QVariant(QString("foo")).toList() returns an empty |
| list, not a list containing "foo". |
| */ |
| if (value.userType() == QMetaType::QStringList |
| || (value.userType() == QMetaType::QVariantList && value.toList().size() != 1)) { |
| iniEscapedStringList(variantListToStringList(value.toList()), block, iniCodec); |
| } else { |
| iniEscapedString(variantToString(value), block, iniCodec); |
| } |
| block += eol; |
| if (device.write(block) == -1) { |
| writeError = true; |
| break; |
| } |
| } |
| } |
| return !writeError; |
| } |
| |
| void QConfFileSettingsPrivate::ensureAllSectionsParsed(QConfFile *confFile) const |
| { |
| UnparsedSettingsMap::const_iterator i = confFile->unparsedIniSections.constBegin(); |
| const UnparsedSettingsMap::const_iterator end = confFile->unparsedIniSections.constEnd(); |
| |
| for (; i != end; ++i) { |
| if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys, iniCodec)) |
| setStatus(QSettings::FormatError); |
| } |
| confFile->unparsedIniSections.clear(); |
| } |
| |
| void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, |
| const QSettingsKey &key) const |
| { |
| if (confFile->unparsedIniSections.isEmpty()) |
| return; |
| |
| UnparsedSettingsMap::iterator i; |
| |
| int indexOfSlash = key.indexOf(QLatin1Char('/')); |
| if (indexOfSlash != -1) { |
| i = confFile->unparsedIniSections.upperBound(key); |
| if (i == confFile->unparsedIniSections.begin()) |
| return; |
| --i; |
| if (i.key().isEmpty() || !key.startsWith(i.key())) |
| return; |
| } else { |
| i = confFile->unparsedIniSections.begin(); |
| if (i == confFile->unparsedIniSections.end() || !i.key().isEmpty()) |
| return; |
| } |
| |
| if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys, iniCodec)) |
| setStatus(QSettings::FormatError); |
| confFile->unparsedIniSections.erase(i); |
| } |
| |
| /*! |
| \class QSettings |
| \inmodule QtCore |
| \brief The QSettings class provides persistent platform-independent application settings. |
| |
| \ingroup io |
| |
| \reentrant |
| |
| Users normally expect an application to remember its settings |
| (window sizes and positions, options, etc.) across sessions. This |
| information is often stored in the system registry on Windows, |
| and in property list files on \macos and iOS. On Unix systems, in the |
| absence of a standard, many applications (including the KDE |
| applications) use INI text files. |
| |
| QSettings is an abstraction around these technologies, enabling |
| you to save and restore application settings in a portable |
| manner. It also supports \l{registerFormat()}{custom storage |
| formats}. |
| |
| QSettings's API is based on QVariant, allowing you to save |
| most value-based types, such as QString, QRect, and QImage, |
| with the minimum of effort. |
| |
| If all you need is a non-persistent memory-based structure, |
| consider using QMap<QString, QVariant> instead. |
| |
| \tableofcontents section1 |
| |
| \section1 Basic Usage |
| |
| When creating a QSettings object, you must pass the name of your |
| company or organization as well as the name of your application. |
| For example, if your product is called Star Runner and your |
| company is called MySoft, you would construct the QSettings |
| object as follows: |
| |
| \snippet settings/settings.cpp 0 |
| |
| QSettings objects can be created either on the stack or on |
| the heap (i.e. using \c new). Constructing and destroying a |
| QSettings object is very fast. |
| |
| If you use QSettings from many places in your application, you |
| might want to specify the organization name and the application |
| name using QCoreApplication::setOrganizationName() and |
| QCoreApplication::setApplicationName(), and then use the default |
| QSettings constructor: |
| |
| \snippet settings/settings.cpp 1 |
| \snippet settings/settings.cpp 2 |
| \snippet settings/settings.cpp 3 |
| \dots |
| \snippet settings/settings.cpp 4 |
| |
| (Here, we also specify the organization's Internet domain. When |
| the Internet domain is set, it is used on \macos and iOS instead of the |
| organization name, since \macos and iOS applications conventionally use |
| Internet domains to identify themselves. If no domain is set, a |
| fake domain is derived from the organization name. See the |
| \l{Platform-Specific Notes} below for details.) |
| |
| QSettings stores settings. Each setting consists of a QString |
| that specifies the setting's name (the \e key) and a QVariant |
| that stores the data associated with the key. To write a setting, |
| use setValue(). For example: |
| |
| \snippet settings/settings.cpp 5 |
| |
| If there already exists a setting with the same key, the existing |
| value is overwritten by the new value. For efficiency, the |
| changes may not be saved to permanent storage immediately. (You |
| can always call sync() to commit your changes.) |
| |
| You can get a setting's value back using value(): |
| |
| \snippet settings/settings.cpp 6 |
| |
| If there is no setting with the specified name, QSettings |
| returns a null QVariant (which can be converted to the integer 0). |
| You can specify another default value by passing a second |
| argument to value(): |
| |
| \snippet settings/settings.cpp 7 |
| |
| To test whether a given key exists, call contains(). To remove |
| the setting associated with a key, call remove(). To obtain the |
| list of all keys, call allKeys(). To remove all keys, call |
| clear(). |
| |
| \section1 QVariant and GUI Types |
| |
| Because QVariant is part of the Qt Core module, it cannot provide |
| conversion functions to data types such as QColor, QImage, and |
| QPixmap, which are part of Qt GUI. In other words, there is no |
| \c toColor(), \c toImage(), or \c toPixmap() functions in QVariant. |
| |
| Instead, you can use the QVariant::value() template function. |
| For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 0 |
| |
| The inverse conversion (e.g., from QColor to QVariant) is |
| automatic for all data types supported by QVariant, including |
| GUI-related types: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 1 |
| |
| Custom types registered using qRegisterMetaType() and |
| qRegisterMetaTypeStreamOperators() can be stored using QSettings. |
| |
| \section1 Section and Key Syntax |
| |
| Setting keys can contain any Unicode characters. The Windows |
| registry and INI files use case-insensitive keys, whereas the |
| CFPreferences API on \macos and iOS uses case-sensitive keys. To |
| avoid portability problems, follow these simple rules: |
| |
| \list 1 |
| \li Always refer to the same key using the same case. For example, |
| if you refer to a key as "text fonts" in one place in your |
| code, don't refer to it as "Text Fonts" somewhere else. |
| |
| \li Avoid key names that are identical except for the case. For |
| example, if you have a key called "MainWindow", don't try to |
| save another key as "mainwindow". |
| |
| \li Do not use slashes ('/' and '\\') in section or key names; the |
| backslash character is used to separate sub keys (see below). On |
| windows '\\' are converted by QSettings to '/', which makes |
| them identical. |
| \endlist |
| |
| You can form hierarchical keys using the '/' character as a |
| separator, similar to Unix file paths. For example: |
| |
| \snippet settings/settings.cpp 8 |
| \snippet settings/settings.cpp 9 |
| \snippet settings/settings.cpp 10 |
| |
| If you want to save or restore many settings with the same |
| prefix, you can specify the prefix using beginGroup() and call |
| endGroup() at the end. Here's the same example again, but this |
| time using the group mechanism: |
| |
| \snippet settings/settings.cpp 11 |
| \codeline |
| \snippet settings/settings.cpp 12 |
| |
| If a group is set using beginGroup(), the behavior of most |
| functions changes consequently. Groups can be set recursively. |
| |
| In addition to groups, QSettings also supports an "array" |
| concept. See beginReadArray() and beginWriteArray() for details. |
| |
| \section1 Fallback Mechanism |
| |
| Let's assume that you have created a QSettings object with the |
| organization name MySoft and the application name Star Runner. |
| When you look up a value, up to four locations are searched in |
| that order: |
| |
| \list 1 |
| \li a user-specific location for the Star Runner application |
| \li a user-specific location for all applications by MySoft |
| \li a system-wide location for the Star Runner application |
| \li a system-wide location for all applications by MySoft |
| \endlist |
| |
| (See \l{Platform-Specific Notes} below for information on what |
| these locations are on the different platforms supported by Qt.) |
| |
| If a key cannot be found in the first location, the search goes |
| on in the second location, and so on. This enables you to store |
| system-wide or organization-wide settings and to override them on |
| a per-user or per-application basis. To turn off this mechanism, |
| call setFallbacksEnabled(false). |
| |
| Although keys from all four locations are available for reading, |
| only the first file (the user-specific location for the |
| application at hand) is accessible for writing. To write to any |
| of the other files, omit the application name and/or specify |
| QSettings::SystemScope (as opposed to QSettings::UserScope, the |
| default). |
| |
| Let's see with an example: |
| |
| \snippet settings/settings.cpp 13 |
| \snippet settings/settings.cpp 14 |
| |
| The table below summarizes which QSettings objects access |
| which location. "\b{X}" means that the location is the main |
| location associated to the QSettings object and is used both |
| for reading and for writing; "o" means that the location is used |
| as a fallback when reading. |
| |
| \table |
| \header \li Locations \li \c{obj1} \li \c{obj2} \li \c{obj3} \li \c{obj4} |
| \row \li 1. User, Application \li \b{X} \li \li \li |
| \row \li 2. User, Organization \li o \li \b{X} \li \li |
| \row \li 3. System, Application \li o \li \li \b{X} \li |
| \row \li 4. System, Organization \li o \li o \li o \li \b{X} |
| \endtable |
| |
| The beauty of this mechanism is that it works on all platforms |
| supported by Qt and that it still gives you a lot of flexibility, |
| without requiring you to specify any file names or registry |
| paths. |
| |
| If you want to use INI files on all platforms instead of the |
| native API, you can pass QSettings::IniFormat as the first |
| argument to the QSettings constructor, followed by the scope, the |
| organization name, and the application name: |
| |
| \snippet settings/settings.cpp 15 |
| |
| Note that type information is not preserved when reading settings from INI |
| files; all values will be returned as QString. |
| |
| The \l{tools/settingseditor}{Settings Editor} example lets you |
| experiment with different settings location and with fallbacks |
| turned on or off. |
| |
| \section1 Restoring the State of a GUI Application |
| |
| QSettings is often used to store the state of a GUI |
| application. The following example illustrates how to use QSettings |
| to save and restore the geometry of an application's main window. |
| |
| \snippet settings/settings.cpp 16 |
| \codeline |
| \snippet settings/settings.cpp 17 |
| |
| See \l{Window Geometry} for a discussion on why it is better to |
| call QWidget::resize() and QWidget::move() rather than QWidget::setGeometry() |
| to restore a window's geometry. |
| |
| The \c readSettings() and \c writeSettings() functions must be |
| called from the main window's constructor and close event handler |
| as follows: |
| |
| \snippet settings/settings.cpp 18 |
| \dots |
| \snippet settings/settings.cpp 19 |
| \snippet settings/settings.cpp 20 |
| \codeline |
| \snippet settings/settings.cpp 21 |
| |
| See the \l{mainwindows/application}{Application} example for a |
| self-contained example that uses QSettings. |
| |
| \section1 Accessing Settings from Multiple Threads or Processes Simultaneously |
| |
| QSettings is \l{reentrant}. This means that you can use |
| distinct QSettings object in different threads |
| simultaneously. This guarantee stands even when the QSettings |
| objects refer to the same files on disk (or to the same entries |
| in the system registry). If a setting is modified through one |
| QSettings object, the change will immediately be visible in |
| any other QSettings objects that operate on the same location |
| and that live in the same process. |
| |
| QSettings can safely be used from different processes (which can |
| be different instances of your application running at the same |
| time or different applications altogether) to read and write to |
| the same system locations, provided certain conditions are met. For |
| QSettings::IniFormat, it uses advisory file locking and a smart merging |
| algorithm to ensure data integrity. The condition for that to work is that |
| the writeable configuration file must be a regular file and must reside in |
| a directory that the current user can create new, temporary files in. If |
| that is not the case, then one must use setAtomicSyncRequired() to turn the |
| safety off. |
| |
| Note that sync() imports changes made by other processes (in addition to |
| writing the changes from this QSettings). |
| |
| \section1 Platform-Specific Notes |
| |
| \section2 Locations Where Application Settings Are Stored |
| |
| As mentioned in the \l{Fallback Mechanism} section, QSettings |
| stores settings for an application in up to four locations, |
| depending on whether the settings are user-specific or |
| system-wide and whether the settings are application-specific |
| or organization-wide. For simplicity, we're assuming the |
| organization is called MySoft and the application is called Star |
| Runner. |
| |
| On Unix systems, if the file format is NativeFormat, the |
| following files are used by default: |
| |
| \list 1 |
| \li \c{$HOME/.config/MySoft/Star Runner.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.conf}) |
| \li \c{$HOME/.config/MySoft.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.conf}) |
| \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.conf} |
| \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.conf} |
| \endlist |
| \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used. |
| |
| On \macos versions 10.2 and 10.3, these files are used by |
| default: |
| |
| \list 1 |
| \li \c{$HOME/Library/Preferences/com.MySoft.Star Runner.plist} |
| \li \c{$HOME/Library/Preferences/com.MySoft.plist} |
| \li \c{/Library/Preferences/com.MySoft.Star Runner.plist} |
| \li \c{/Library/Preferences/com.MySoft.plist} |
| \endlist |
| |
| On Windows, NativeFormat settings are stored in the following |
| registry paths: |
| |
| \list 1 |
| \li \c{HKEY_CURRENT_USER\Software\MySoft\Star Runner} |
| \li \c{HKEY_CURRENT_USER\Software\MySoft\OrganizationDefaults} |
| \li \c{HKEY_LOCAL_MACHINE\Software\MySoft\Star Runner} |
| \li \c{HKEY_LOCAL_MACHINE\Software\MySoft\OrganizationDefaults} |
| \endlist |
| |
| \note On Windows, for 32-bit programs running in WOW64 mode, settings are |
| stored in the following registry path: |
| \c{HKEY_LOCAL_MACHINE\Software\WOW6432node}. |
| |
| If the file format is NativeFormat, this is "Settings/MySoft/Star Runner.conf" |
| in the application's home directory. |
| |
| If the file format is IniFormat, the following files are |
| used on Unix, \macos, and iOS: |
| |
| \list 1 |
| \li \c{$HOME/.config/MySoft/Star Runner.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.ini}) |
| \li \c{$HOME/.config/MySoft.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.ini}) |
| \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.ini} |
| \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.ini} |
| \endlist |
| \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used. |
| |
| On Windows, the following files are used: |
| |
| \list 1 |
| \li \c{FOLDERID_RoamingAppData\MySoft\Star Runner.ini} |
| \li \c{FOLDERID_RoamingAppData\MySoft.ini} |
| \li \c{FOLDERID_ProgramData\MySoft\Star Runner.ini} |
| \li \c{FOLDERID_ProgramData\MySoft.ini} |
| \endlist |
| |
| The identifiers prefixed by \c{FOLDERID_} are special item ID lists to be passed |
| to the Win32 API function \c{SHGetKnownFolderPath()} to obtain the |
| corresponding path. |
| |
| \c{FOLDERID_RoamingAppData} usually points to \tt{C:\\Users\\\e{User Name}\\AppData\\Roaming}, |
| also shown by the environment variable \c{%APPDATA%}. |
| |
| \c{FOLDERID_ProgramData} usually points to \tt{C:\\ProgramData}. |
| |
| If the file format is IniFormat, this is "Settings/MySoft/Star Runner.ini" |
| in the application's home directory. |
| |
| The paths for the \c .ini and \c .conf files can be changed using |
| setPath(). On Unix, \macos, and iOS the user can override them by |
| setting the \c XDG_CONFIG_HOME environment variable; see |
| setPath() for details. |
| |
| \section2 Accessing INI and .plist Files Directly |
| |
| Sometimes you do want to access settings stored in a specific |
| file or registry path. On all platforms, if you want to read an |
| INI file directly, you can use the QSettings constructor that |
| takes a file name as first argument and pass QSettings::IniFormat |
| as second argument. For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 2 |
| |
| You can then use the QSettings object to read and write settings |
| in the file. |
| |
| On \macos and iOS, you can access property list \c .plist files by passing |
| QSettings::NativeFormat as second argument. For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 3 |
| |
| \section2 Accessing the Windows Registry Directly |
| |
| On Windows, QSettings lets you access settings that have been |
| written with QSettings (or settings in a supported format, e.g., string |
| data) in the system registry. This is done by constructing a QSettings |
| object with a path in the registry and QSettings::NativeFormat. |
| |
| For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 4 |
| |
| All the registry entries that appear under the specified path can |
| be read or written through the QSettings object as usual (using |
| forward slashes instead of backslashes). For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 5 |
| |
| Note that the backslash character is, as mentioned, used by |
| QSettings to separate subkeys. As a result, you cannot read or |
| write windows registry entries that contain slashes or |
| backslashes; you should use a native windows API if you need to do |
| so. |
| |
| \section2 Accessing Common Registry Settings on Windows |
| |
| On Windows, it is possible for a key to have both a value and subkeys. |
| Its default value is accessed by using "Default" or "." in |
| place of a subkey: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 6 |
| |
| On other platforms than Windows, "Default" and "." would be |
| treated as regular subkeys. |
| |
| \section2 Platform Limitations |
| |
| While QSettings attempts to smooth over the differences between |
| the different supported platforms, there are still a few |
| differences that you should be aware of when porting your |
| application: |
| |
| \list |
| \li The Windows system registry has the following limitations: A |
| subkey may not exceed 255 characters, an entry's value may |
| not exceed 16,383 characters, and all the values of a key may |
| not exceed 65,535 characters. One way to work around these |
| limitations is to store the settings using the IniFormat |
| instead of the NativeFormat. |
| |
| \li On Windows, when the Windows system registry is used, QSettings |
| does not preserve the original type of the value. Therefore, |
| the type of the value might change when a new value is set. For |
| example, a value with type \c REG_EXPAND_SZ will change to \c REG_SZ. |
| |
| \li On \macos and iOS, allKeys() will return some extra keys for global |
| settings that apply to all applications. These keys can be |
| read using value() but cannot be changed, only shadowed. |
| Calling setFallbacksEnabled(false) will hide these global |
| settings. |
| |
| \li On \macos and iOS, the CFPreferences API used by QSettings expects |
| Internet domain names rather than organization names. To |
| provide a uniform API, QSettings derives a fake domain name |
| from the organization name (unless the organization name |
| already is a domain name, e.g. OpenOffice.org). The algorithm |
| appends ".com" to the company name and replaces spaces and |
| other illegal characters with hyphens. If you want to specify |
| a different domain name, call |
| QCoreApplication::setOrganizationDomain(), |
| QCoreApplication::setOrganizationName(), and |
| QCoreApplication::setApplicationName() in your \c main() |
| function and then use the default QSettings constructor. |
| Another solution is to use preprocessor directives, for |
| example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 7 |
| |
| \li On \macos, permissions to access settings not belonging to the |
| current user (i.e. SystemScope) have changed with 10.7 (Lion). Prior to |
| that version, users having admin rights could access these. For 10.7 and |
| 10.8 (Mountain Lion), only root can. However, 10.9 (Mavericks) changes |
| that rule again but only for the native format (plist files). |
| |
| \endlist |
| |
| \sa QVariant, QSessionManager, {Settings Editor Example}, {Application Example} |
| */ |
| |
| /*! \enum QSettings::Status |
| |
| The following status values are possible: |
| |
| \value NoError No error occurred. |
| \value AccessError An access error occurred (e.g. trying to write to a read-only file). |
| \value FormatError A format error occurred (e.g. loading a malformed INI file). |
| |
| \sa status() |
| */ |
| |
| /*! \enum QSettings::Format |
| |
| This enum type specifies the storage format used by QSettings. |
| |
| \value NativeFormat Store the settings using the most |
| appropriate storage format for the platform. |
| On Windows, this means the system registry; |
| on \macos and iOS, this means the CFPreferences |
| API; on Unix, this means textual |
| configuration files in INI format. |
| \value Registry32Format Windows only: Explicitly access the 32-bit system registry |
| from a 64-bit application running on 64-bit Windows. |
| On 32-bit Windows or from a 32-bit application on 64-bit Windows, |
| this works the same as specifying NativeFormat. |
| This enum value was added in Qt 5.7. |
| \value Registry64Format Windows only: Explicitly access the 64-bit system registry |
| from a 32-bit application running on 64-bit Windows. |
| On 32-bit Windows or from a 64-bit application on 64-bit Windows, |
| this works the same as specifying NativeFormat. |
| This enum value was added in Qt 5.7. |
| \value IniFormat Store the settings in INI files. Note that type information |
| is not preserved when reading settings from INI files; |
| all values will be returned as QString. |
| |
| \value InvalidFormat Special value returned by registerFormat(). |
| \omitvalue CustomFormat1 |
| \omitvalue CustomFormat2 |
| \omitvalue CustomFormat3 |
| \omitvalue CustomFormat4 |
| \omitvalue CustomFormat5 |
| \omitvalue CustomFormat6 |
| \omitvalue CustomFormat7 |
| \omitvalue CustomFormat8 |
| \omitvalue CustomFormat9 |
| \omitvalue CustomFormat10 |
| \omitvalue CustomFormat11 |
| \omitvalue CustomFormat12 |
| \omitvalue CustomFormat13 |
| \omitvalue CustomFormat14 |
| \omitvalue CustomFormat15 |
| \omitvalue CustomFormat16 |
| |
| On Unix, NativeFormat and IniFormat mean the same thing, except |
| that the file extension is different (\c .conf for NativeFormat, |
| \c .ini for IniFormat). |
| |
| The INI file format is a Windows file format that Qt supports on |
| all platforms. In the absence of an INI standard, we try to |
| follow what Microsoft does, with the following exceptions: |
| |
| \list |
| \li If you store types that QVariant can't convert to QString |
| (e.g., QPoint, QRect, and QSize), Qt uses an \c{@}-based |
| syntax to encode the type. For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 8 |
| |
| To minimize compatibility issues, any \c @ that doesn't |
| appear at the first position in the value or that isn't |
| followed by a Qt type (\c Point, \c Rect, \c Size, etc.) is |
| treated as a normal character. |
| |
| \li Although backslash is a special character in INI files, most |
| Windows applications don't escape backslashes (\c{\}) in file |
| paths: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 9 |
| |
| QSettings always treats backslash as a special character and |
| provides no API for reading or writing such entries. |
| |
| \li The INI file format has severe restrictions on the syntax of |
| a key. Qt works around this by using \c % as an escape |
| character in keys. In addition, if you save a top-level |
| setting (a key with no slashes in it, e.g., "someKey"), it |
| will appear in the INI file's "General" section. To avoid |
| overwriting other keys, if you save something using a key |
| such as "General/someKey", the key will be located in the |
| "%General" section, \e not in the "General" section. |
| |
| \li Following the philosophy that we should be liberal in what |
| we accept and conservative in what we generate, QSettings |
| will accept Latin-1 encoded INI files, but generate pure |
| ASCII files, where non-ASCII values are encoded using standard |
| INI escape sequences. To make the INI files more readable (but |
| potentially less compatible), call setIniCodec(). |
| \endlist |
| |
| \sa registerFormat(), setPath() |
| */ |
| |
| /*! \enum QSettings::Scope |
| |
| This enum specifies whether settings are user-specific or shared |
| by all users of the same system. |
| |
| \value UserScope Store settings in a location specific to the |
| current user (e.g., in the user's home |
| directory). |
| \value SystemScope Store settings in a global location, so that |
| all users on the same machine access the same |
| set of settings. |
| |
| \sa setPath() |
| */ |
| |
| #ifndef QT_NO_QOBJECT |
| /*! |
| Constructs a QSettings object for accessing settings of the |
| application called \a application from the organization called \a |
| organization, and with parent \a parent. |
| |
| Example: |
| \snippet code/src_corelib_io_qsettings.cpp 10 |
| |
| The scope is set to QSettings::UserScope, and the format is |
| set to QSettings::NativeFormat (i.e. calling setDefaultFormat() |
| before calling this constructor has no effect). |
| |
| \sa setDefaultFormat(), {Fallback Mechanism} |
| */ |
| QSettings::QSettings(const QString &organization, const QString &application, QObject *parent) |
| : QObject(*QSettingsPrivate::create(NativeFormat, UserScope, organization, application), |
| parent) |
| { |
| } |
| |
| /*! |
| Constructs a QSettings object for accessing settings of the |
| application called \a application from the organization called \a |
| organization, and with parent \a parent. |
| |
| If \a scope is QSettings::UserScope, the QSettings object searches |
| user-specific settings first, before it searches system-wide |
| settings as a fallback. If \a scope is QSettings::SystemScope, the |
| QSettings object ignores user-specific settings and provides |
| access to system-wide settings. |
| |
| The storage format is set to QSettings::NativeFormat (i.e. calling |
| setDefaultFormat() before calling this constructor has no effect). |
| |
| If no application name is given, the QSettings object will |
| only access the organization-wide \l{Fallback Mechanism}{locations}. |
| |
| \sa setDefaultFormat() |
| */ |
| QSettings::QSettings(Scope scope, const QString &organization, const QString &application, |
| QObject *parent) |
| : QObject(*QSettingsPrivate::create(NativeFormat, scope, organization, application), parent) |
| { |
| } |
| |
| /*! |
| Constructs a QSettings object for accessing settings of the |
| application called \a application from the organization called |
| \a organization, and with parent \a parent. |
| |
| If \a scope is QSettings::UserScope, the QSettings object searches |
| user-specific settings first, before it searches system-wide |
| settings as a fallback. If \a scope is |
| QSettings::SystemScope, the QSettings object ignores user-specific |
| settings and provides access to system-wide settings. |
| |
| If \a format is QSettings::NativeFormat, the native API is used for |
| storing settings. If \a format is QSettings::IniFormat, the INI format |
| is used. |
| |
| If no application name is given, the QSettings object will |
| only access the organization-wide \l{Fallback Mechanism}{locations}. |
| */ |
| QSettings::QSettings(Format format, Scope scope, const QString &organization, |
| const QString &application, QObject *parent) |
| : QObject(*QSettingsPrivate::create(format, scope, organization, application), parent) |
| { |
| } |
| |
| /*! |
| Constructs a QSettings object for accessing the settings |
| stored in the file called \a fileName, with parent \a parent. If |
| the file doesn't already exist, it is created. |
| |
| If \a format is QSettings::NativeFormat, the meaning of \a |
| fileName depends on the platform. On Unix, \a fileName is the |
| name of an INI file. On \macos and iOS, \a fileName is the name of a |
| \c .plist file. On Windows, \a fileName is a path in the system |
| registry. |
| |
| If \a format is QSettings::IniFormat, \a fileName is the name of an INI |
| file. |
| |
| \warning This function is provided for convenience. It works well for |
| accessing INI or \c .plist files generated by Qt, but might fail on some |
| syntaxes found in such files originated by other programs. In particular, |
| be aware of the following limitations: |
| |
| \list |
| \li QSettings provides no way of reading INI "path" entries, i.e., entries |
| with unescaped slash characters. (This is because these entries are |
| ambiguous and cannot be resolved automatically.) |
| \li In INI files, QSettings uses the \c @ character as a metacharacter in some |
| contexts, to encode Qt-specific data types (e.g., \c @Rect), and might |
| therefore misinterpret it when it occurs in pure INI files. |
| \endlist |
| |
| \sa fileName() |
| */ |
| QSettings::QSettings(const QString &fileName, Format format, QObject *parent) |
| : QObject(*QSettingsPrivate::create(fileName, format), parent) |
| { |
| } |
| |
| /*! |
| Constructs a QSettings object for accessing settings of the |
| application and organization set previously with a call to |
| QCoreApplication::setOrganizationName(), |
| QCoreApplication::setOrganizationDomain(), and |
| QCoreApplication::setApplicationName(). |
| |
| The scope is QSettings::UserScope and the format is |
| defaultFormat() (QSettings::NativeFormat by default). |
| Use setDefaultFormat() before calling this constructor |
| to change the default format used by this constructor. |
| |
| The code |
| |
| \snippet code/src_corelib_io_qsettings.cpp 11 |
| |
| is equivalent to |
| |
| \snippet code/src_corelib_io_qsettings.cpp 12 |
| |
| If QCoreApplication::setOrganizationName() and |
| QCoreApplication::setApplicationName() has not been previously |
| called, the QSettings object will not be able to read or write |
| any settings, and status() will return AccessError. |
| |
| You should supply both the domain (used by default on \macos and iOS) and |
| the name (used by default elsewhere), although the code will cope if you |
| supply only one, which will then be used (on all platforms), at odds with |
| the usual naming of the file on platforms for which it isn't the default. |
| |
| \sa QCoreApplication::setOrganizationName(), |
| QCoreApplication::setOrganizationDomain(), |
| QCoreApplication::setApplicationName(), |
| setDefaultFormat() |
| */ |
| QSettings::QSettings(QObject *parent) |
| : QSettings(UserScope, parent) |
| { |
| } |
| |
| /*! |
| \since 5.13 |
| |
| Constructs a QSettings object in the same way as |
| QSettings(QObject *parent) but with the given \a scope. |
| |
| \sa QSettings(QObject *parent) |
| */ |
| QSettings::QSettings(Scope scope, QObject *parent) |
| : QObject(*QSettingsPrivate::create(globalDefaultFormat, scope, |
| #ifdef Q_OS_DARWIN |
| QCoreApplication::organizationDomain().isEmpty() |
| ? QCoreApplication::organizationName() |
| : QCoreApplication::organizationDomain() |
| #else |
| QCoreApplication::organizationName().isEmpty() |
| ? QCoreApplication::organizationDomain() |
| : QCoreApplication::organizationName() |
| #endif |
| , QCoreApplication::applicationName()), |
| parent) |
| { |
| } |
| |
| #else |
| QSettings::QSettings(const QString &organization, const QString &application) |
| : d_ptr(QSettingsPrivate::create(globalDefaultFormat, QSettings::UserScope, organization, application)) |
| { |
| d_ptr->q_ptr = this; |
| } |
| |
| QSettings::QSettings(Scope scope, const QString &organization, const QString &application) |
| : d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope, organization, application)) |
| { |
| d_ptr->q_ptr = this; |
| } |
| |
| QSettings::QSettings(Format format, Scope scope, const QString &organization, |
| const QString &application) |
| : d_ptr(QSettingsPrivate::create(format, scope, organization, application)) |
| { |
| d_ptr->q_ptr = this; |
| } |
| |
| QSettings::QSettings(const QString &fileName, Format format) |
| : d_ptr(QSettingsPrivate::create(fileName, format)) |
| { |
| d_ptr->q_ptr = this; |
| } |
| |
| # ifndef QT_BUILD_QMAKE |
| QSettings::QSettings(Scope scope) |
| : d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope, |
| # ifdef Q_OS_DARWIN |
| QCoreApplication::organizationDomain().isEmpty() |
| ? QCoreApplication::organizationName() |
| : QCoreApplication::organizationDomain() |
| # else |
| QCoreApplication::organizationName().isEmpty() |
| ? QCoreApplication::organizationDomain() |
| : QCoreApplication::organizationName() |
| # endif |
| , QCoreApplication::applicationName()) |
| ) |
| { |
| d_ptr->q_ptr = this; |
| } |
| # endif |
| #endif |
| |
| /*! |
| Destroys the QSettings object. |
| |
| Any unsaved changes will eventually be written to permanent |
| storage. |
| |
| \sa sync() |
| */ |
| QSettings::~QSettings() |
| { |
| Q_D(QSettings); |
| if (d->pendingChanges) { |
| QT_TRY { |
| d->flush(); |
| } QT_CATCH(...) { |
| ; // ok. then don't flush but at least don't throw in the destructor |
| } |
| } |
| } |
| |
| /*! |
| Removes all entries in the primary location associated to this |
| QSettings object. |
| |
| Entries in fallback locations are not removed. |
| |
| If you only want to remove the entries in the current group(), |
| use remove("") instead. |
| |
| \sa remove(), setFallbacksEnabled() |
| */ |
| void QSettings::clear() |
| { |
| Q_D(QSettings); |
| d->clear(); |
| d->requestUpdate(); |
| } |
| |
| /*! |
| Writes any unsaved changes to permanent storage, and reloads any |
| settings that have been changed in the meantime by another |
| application. |
| |
| This function is called automatically from QSettings's destructor and |
| by the event loop at regular intervals, so you normally don't need to |
| call it yourself. |
| |
| \sa status() |
| */ |
| void QSettings::sync() |
| { |
| Q_D(QSettings); |
| d->sync(); |
| d->pendingChanges = false; |
| } |
| |
| /*! |
| Returns the path where settings written using this QSettings |
| object are stored. |
| |
| On Windows, if the format is QSettings::NativeFormat, the return value |
| is a system registry path, not a file path. |
| |
| \sa isWritable(), format() |
| */ |
| QString QSettings::fileName() const |
| { |
| Q_D(const QSettings); |
| return d->fileName(); |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Returns the format used for storing the settings. |
| |
| \sa defaultFormat(), fileName(), scope(), organizationName(), applicationName() |
| */ |
| QSettings::Format QSettings::format() const |
| { |
| Q_D(const QSettings); |
| return d->format; |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Returns the scope used for storing the settings. |
| |
| \sa format(), organizationName(), applicationName() |
| */ |
| QSettings::Scope QSettings::scope() const |
| { |
| Q_D(const QSettings); |
| return d->scope; |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Returns the organization name used for storing the settings. |
| |
| \sa QCoreApplication::organizationName(), format(), scope(), applicationName() |
| */ |
| QString QSettings::organizationName() const |
| { |
| Q_D(const QSettings); |
| return d->organizationName; |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Returns the application name used for storing the settings. |
| |
| \sa QCoreApplication::applicationName(), format(), scope(), organizationName() |
| */ |
| QString QSettings::applicationName() const |
| { |
| Q_D(const QSettings); |
| return d->applicationName; |
| } |
| |
| #if QT_CONFIG(textcodec) |
| |
| /*! |
| \since 4.5 |
| |
| Sets the codec for accessing INI files (including \c .conf files on Unix) |
| to \a codec. The codec is used for decoding any data that is read from |
| the INI file, and for encoding any data that is written to the file. By |
| default, no codec is used, and non-ASCII characters are encoded using |
| standard INI escape sequences. |
| |
| \warning The codec must be set immediately after creating the QSettings |
| object, before accessing any data. |
| |
| \sa iniCodec() |
| */ |
| void QSettings::setIniCodec(QTextCodec *codec) |
| { |
| Q_D(QSettings); |
| d->iniCodec = codec; |
| } |
| |
| /*! |
| \since 4.5 |
| \overload |
| |
| Sets the codec for accessing INI files (including \c .conf files on Unix) |
| to the QTextCodec for the encoding specified by \a codecName. Common |
| values for \c codecName include "ISO 8859-1", "UTF-8", and "UTF-16". |
| If the encoding isn't recognized, nothing happens. |
| |
| \sa QTextCodec::codecForName() |
| */ |
| void QSettings::setIniCodec(const char *codecName) |
| { |
| Q_D(QSettings); |
| if (QTextCodec *codec = QTextCodec::codecForName(codecName)) |
| d->iniCodec = codec; |
| } |
| |
| /*! |
| \since 4.5 |
| |
| Returns the codec that is used for accessing INI files. By default, |
| no codec is used, so \nullptr is returned. |
| */ |
| |
| QTextCodec *QSettings::iniCodec() const |
| { |
| Q_D(const QSettings); |
| return d->iniCodec; |
| } |
| |
| #endif // textcodec |
| |
| /*! |
| Returns a status code indicating the first error that was met by |
| QSettings, or QSettings::NoError if no error occurred. |
| |
| Be aware that QSettings delays performing some operations. For this |
| reason, you might want to call sync() to ensure that the data stored |
| in QSettings is written to disk before calling status(). |
| |
| \sa sync() |
| */ |
| QSettings::Status QSettings::status() const |
| { |
| Q_D(const QSettings); |
| return d->status; |
| } |
| |
| /*! |
| \since 5.10 |
| |
| Returns \c true if QSettings is only allowed to perform atomic saving and |
| reloading (synchronization) of the settings. Returns \c false if it is |
| allowed to save the settings contents directly to the configuration file. |
| |
| The default is \c true. |
| |
| \sa setAtomicSyncRequired(), QSaveFile |
| */ |
| bool QSettings::isAtomicSyncRequired() const |
| { |
| Q_D(const QSettings); |
| return d->atomicSyncOnly; |
| } |
| |
| /*! |
| \since 5.10 |
| |
| Configures whether QSettings is required to perform atomic saving and |
| reloading (synchronization) of the settings. If the \a enable argument is |
| \c true (the default), sync() will only perform synchronization operations |
| that are atomic. If this is not possible, sync() will fail and status() |
| will be an error condition. |
| |
| Setting this property to \c false will allow QSettings to write directly to |
| the configuration file and ignore any errors trying to lock it against |
| other processes trying to write at the same time. Because of the potential |
| for corruption, this option should be used with care, but is required in |
| certain conditions, like a QSettings::IniFormat configuration file that |
| exists in an otherwise non-writeable directory or NTFS Alternate Data |
| Streams. |
| |
| See \l QSaveFile for more information on the feature. |
| |
| \sa isAtomicSyncRequired(), QSaveFile |
| */ |
| void QSettings::setAtomicSyncRequired(bool enable) |
| { |
| Q_D(QSettings); |
| d->atomicSyncOnly = enable; |
| } |
| |
| /*! |
| Appends \a prefix to the current group. |
| |
| The current group is automatically prepended to all keys |
| specified to QSettings. In addition, query functions such as |
| childGroups(), childKeys(), and allKeys() are based on the group. |
| By default, no group is set. |
| |
| Groups are useful to avoid typing in the same setting paths over |
| and over. For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 13 |
| |
| This will set the value of three settings: |
| |
| \list |
| \li \c mainwindow/size |
| \li \c mainwindow/fullScreen |
| \li \c outputpanel/visible |
| \endlist |
| |
| Call endGroup() to reset the current group to what it was before |
| the corresponding beginGroup() call. Groups can be nested. |
| |
| \sa endGroup(), group() |
| */ |
| void QSettings::beginGroup(const QString &prefix) |
| { |
| Q_D(QSettings); |
| d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix))); |
| } |
| |
| /*! |
| Resets the group to what it was before the corresponding |
| beginGroup() call. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 14 |
| |
| \sa beginGroup(), group() |
| */ |
| void QSettings::endGroup() |
| { |
| Q_D(QSettings); |
| if (d->groupStack.isEmpty()) { |
| qWarning("QSettings::endGroup: No matching beginGroup()"); |
| return; |
| } |
| |
| QSettingsGroup group = d->groupStack.pop(); |
| int len = group.toString().size(); |
| if (len > 0) |
| d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1)); |
| |
| if (group.isArray()) |
| qWarning("QSettings::endGroup: Expected endArray() instead"); |
| } |
| |
| /*! |
| Returns the current group. |
| |
| \sa beginGroup(), endGroup() |
| */ |
| QString QSettings::group() const |
| { |
| Q_D(const QSettings); |
| return d->groupPrefix.left(d->groupPrefix.size() - 1); |
| } |
| |
| /*! |
| Adds \a prefix to the current group and starts reading from an |
| array. Returns the size of the array. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 15 |
| |
| Use beginWriteArray() to write the array in the first place. |
| |
| \sa beginWriteArray(), endArray(), setArrayIndex() |
| */ |
| int QSettings::beginReadArray(const QString &prefix) |
| { |
| Q_D(QSettings); |
| d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), false)); |
| return value(QLatin1String("size")).toInt(); |
| } |
| |
| /*! |
| Adds \a prefix to the current group and starts writing an array |
| of size \a size. If \a size is -1 (the default), it is automatically |
| determined based on the indexes of the entries written. |
| |
| If you have many occurrences of a certain set of keys, you can |
| use arrays to make your life easier. For example, let's suppose |
| that you want to save a variable-length list of user names and |
| passwords. You could then write: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 16 |
| |
| The generated keys will have the form |
| |
| \list |
| \li \c logins/size |
| \li \c logins/1/userName |
| \li \c logins/1/password |
| \li \c logins/2/userName |
| \li \c logins/2/password |
| \li \c logins/3/userName |
| \li \c logins/3/password |
| \li ... |
| \endlist |
| |
| To read back an array, use beginReadArray(). |
| |
| \sa beginReadArray(), endArray(), setArrayIndex() |
| */ |
| void QSettings::beginWriteArray(const QString &prefix, int size) |
| { |
| Q_D(QSettings); |
| d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), size < 0)); |
| |
| if (size < 0) |
| remove(QLatin1String("size")); |
| else |
| setValue(QLatin1String("size"), size); |
| } |
| |
| /*! |
| Closes the array that was started using beginReadArray() or |
| beginWriteArray(). |
| |
| \sa beginReadArray(), beginWriteArray() |
| */ |
| void QSettings::endArray() |
| { |
| Q_D(QSettings); |
| if (d->groupStack.isEmpty()) { |
| qWarning("QSettings::endArray: No matching beginArray()"); |
| return; |
| } |
| |
| QSettingsGroup group = d->groupStack.top(); |
| int len = group.toString().size(); |
| d->groupStack.pop(); |
| if (len > 0) |
| d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1)); |
| |
| if (group.arraySizeGuess() != -1) |
| setValue(group.name() + QLatin1String("/size"), group.arraySizeGuess()); |
| |
| if (!group.isArray()) |
| qWarning("QSettings::endArray: Expected endGroup() instead"); |
| } |
| |
| /*! |
| Sets the current array index to \a i. Calls to functions such as |
| setValue(), value(), remove(), and contains() will operate on the |
| array entry at that index. |
| |
| You must call beginReadArray() or beginWriteArray() before you |
| can call this function. |
| */ |
| void QSettings::setArrayIndex(int i) |
| { |
| Q_D(QSettings); |
| if (d->groupStack.isEmpty() || !d->groupStack.top().isArray()) { |
| qWarning("QSettings::setArrayIndex: Missing beginArray()"); |
| return; |
| } |
| |
| QSettingsGroup &top = d->groupStack.top(); |
| int len = top.toString().size(); |
| top.setArrayIndex(qMax(i, 0)); |
| d->groupPrefix.replace(d->groupPrefix.size() - len - 1, len, top.toString()); |
| } |
| |
| /*! |
| Returns a list of all keys, including subkeys, that can be read |
| using the QSettings object. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 17 |
| |
| If a group is set using beginGroup(), only the keys in the group |
| are returned, without the group prefix: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 18 |
| |
| \sa childGroups(), childKeys() |
| */ |
| QStringList QSettings::allKeys() const |
| { |
| Q_D(const QSettings); |
| return d->children(d->groupPrefix, QSettingsPrivate::AllKeys); |
| } |
| |
| /*! |
| Returns a list of all top-level keys that can be read using the |
| QSettings object. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 19 |
| |
| If a group is set using beginGroup(), the top-level keys in that |
| group are returned, without the group prefix: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 20 |
| |
| You can navigate through the entire setting hierarchy using |
| childKeys() and childGroups() recursively. |
| |
| \sa childGroups(), allKeys() |
| */ |
| QStringList QSettings::childKeys() const |
| { |
| Q_D(const QSettings); |
| return d->children(d->groupPrefix, QSettingsPrivate::ChildKeys); |
| } |
| |
| /*! |
| Returns a list of all key top-level groups that contain keys that |
| can be read using the QSettings object. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 21 |
| |
| If a group is set using beginGroup(), the first-level keys in |
| that group are returned, without the group prefix. |
| |
| \snippet code/src_corelib_io_qsettings.cpp 22 |
| |
| You can navigate through the entire setting hierarchy using |
| childKeys() and childGroups() recursively. |
| |
| \sa childKeys(), allKeys() |
| */ |
| QStringList QSettings::childGroups() const |
| { |
| Q_D(const QSettings); |
| return d->children(d->groupPrefix, QSettingsPrivate::ChildGroups); |
| } |
| |
| /*! |
| Returns \c true if settings can be written using this QSettings |
| object; returns \c false otherwise. |
| |
| One reason why isWritable() might return false is if |
| QSettings operates on a read-only file. |
| |
| \warning This function is not perfectly reliable, because the |
| file permissions can change at any time. |
| |
| \sa fileName(), status(), sync() |
| */ |
| bool QSettings::isWritable() const |
| { |
| Q_D(const QSettings); |
| return d->isWritable(); |
| } |
| |
| /*! |
| |
| Sets the value of setting \a key to \a value. If the \a key already |
| exists, the previous value is overwritten. |
| |
| Note that the Windows registry and INI files use case-insensitive |
| keys, whereas the CFPreferences API on \macos and iOS uses |
| case-sensitive keys. To avoid portability problems, see the |
| \l{Section and Key Syntax} rules. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 23 |
| |
| \sa value(), remove(), contains() |
| */ |
| void QSettings::setValue(const QString &key, const QVariant &value) |
| { |
| Q_D(QSettings); |
| if (key.isEmpty()) { |
| qWarning("QSettings::setValue: Empty key passed"); |
| return; |
| } |
| QString k = d->actualKey(key); |
| d->set(k, value); |
| d->requestUpdate(); |
| } |
| |
| /*! |
| Removes the setting \a key and any sub-settings of \a key. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 24 |
| |
| Be aware that if one of the fallback locations contains a setting |
| with the same key, that setting will be visible after calling |
| remove(). |
| |
| If \a key is an empty string, all keys in the current group() are |
| removed. For example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 25 |
| |
| Note that the Windows registry and INI files use case-insensitive |
| keys, whereas the CFPreferences API on \macos and iOS uses |
| case-sensitive keys. To avoid portability problems, see the |
| \l{Section and Key Syntax} rules. |
| |
| \sa setValue(), value(), contains() |
| */ |
| void QSettings::remove(const QString &key) |
| { |
| Q_D(QSettings); |
| /* |
| We cannot use actualKey(), because remove() supports empty |
| keys. The code is also tricky because of slash handling. |
| */ |
| QString theKey = d->normalizedKey(key); |
| if (theKey.isEmpty()) |
| theKey = group(); |
| else |
| theKey.prepend(d->groupPrefix); |
| |
| if (theKey.isEmpty()) { |
| d->clear(); |
| } else { |
| d->remove(theKey); |
| } |
| d->requestUpdate(); |
| } |
| |
| /*! |
| Returns \c true if there exists a setting called \a key; returns |
| false otherwise. |
| |
| If a group is set using beginGroup(), \a key is taken to be |
| relative to that group. |
| |
| Note that the Windows registry and INI files use case-insensitive |
| keys, whereas the CFPreferences API on \macos and iOS uses |
| case-sensitive keys. To avoid portability problems, see the |
| \l{Section and Key Syntax} rules. |
| |
| \sa value(), setValue() |
| */ |
| bool QSettings::contains(const QString &key) const |
| { |
| Q_D(const QSettings); |
| QString k = d->actualKey(key); |
| return d->get(k, nullptr); |
| } |
| |
| /*! |
| Sets whether fallbacks are enabled to \a b. |
| |
| By default, fallbacks are enabled. |
| |
| \sa fallbacksEnabled() |
| */ |
| void QSettings::setFallbacksEnabled(bool b) |
| { |
| Q_D(QSettings); |
| d->fallbacks = !!b; |
| } |
| |
| /*! |
| Returns \c true if fallbacks are enabled; returns \c false otherwise. |
| |
| By default, fallbacks are enabled. |
| |
| \sa setFallbacksEnabled() |
| */ |
| bool QSettings::fallbacksEnabled() const |
| { |
| Q_D(const QSettings); |
| return d->fallbacks; |
| } |
| |
| #ifndef QT_NO_QOBJECT |
| /*! |
| \reimp |
| */ |
| bool QSettings::event(QEvent *event) |
| { |
| Q_D(QSettings); |
| if (event->type() == QEvent::UpdateRequest) { |
| d->update(); |
| return true; |
| } |
| return QObject::event(event); |
| } |
| #endif |
| |
| /*! |
| Returns the value for setting \a key. If the setting doesn't |
| exist, returns \a defaultValue. |
| |
| If no default value is specified, a default QVariant is |
| returned. |
| |
| Note that the Windows registry and INI files use case-insensitive |
| keys, whereas the CFPreferences API on \macos and iOS uses |
| case-sensitive keys. To avoid portability problems, see the |
| \l{Section and Key Syntax} rules. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 26 |
| |
| \sa setValue(), contains(), remove() |
| */ |
| QVariant QSettings::value(const QString &key, const QVariant &defaultValue) const |
| { |
| Q_D(const QSettings); |
| if (key.isEmpty()) { |
| qWarning("QSettings::value: Empty key passed"); |
| return QVariant(); |
| } |
| QVariant result = defaultValue; |
| QString k = d->actualKey(key); |
| d->get(k, &result); |
| return result; |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Sets the default file format to the given \a format, which is used |
| for storing settings for the QSettings(QObject *) constructor. |
| |
| If no default format is set, QSettings::NativeFormat is used. See |
| the documentation for the QSettings constructor you are using to |
| see if that constructor will ignore this function. |
| |
| \sa format() |
| */ |
| void QSettings::setDefaultFormat(Format format) |
| { |
| globalDefaultFormat = format; |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Returns default file format used for storing settings for the QSettings(QObject *) constructor. |
| If no default format is set, QSettings::NativeFormat is used. |
| |
| \sa format() |
| */ |
| QSettings::Format QSettings::defaultFormat() |
| { |
| return globalDefaultFormat; |
| } |
| |
| #if QT_DEPRECATED_SINCE(5, 13) |
| /*! |
| \obsolete |
| |
| Use setPath() instead. |
| |
| \oldcode |
| setSystemIniPath(path); |
| \newcode |
| setPath(QSettings::NativeFormat, QSettings::SystemScope, path); |
| setPath(QSettings::IniFormat, QSettings::SystemScope, path); |
| \endcode |
| */ |
| void QSettings::setSystemIniPath(const QString &dir) |
| { |
| setPath(IniFormat, SystemScope, dir); |
| #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) |
| setPath(NativeFormat, SystemScope, dir); |
| #endif |
| } |
| |
| /*! |
| \obsolete |
| |
| Use setPath() instead. |
| */ |
| |
| void QSettings::setUserIniPath(const QString &dir) |
| { |
| setPath(IniFormat, UserScope, dir); |
| #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) |
| setPath(NativeFormat, UserScope, dir); |
| #endif |
| } |
| #endif |
| /*! |
| \since 4.1 |
| |
| Sets the path used for storing settings for the given \a format |
| and \a scope, to \a path. The \a format can be a custom format. |
| |
| The table below summarizes the default values: |
| |
| \table |
| \header \li Platform \li Format \li Scope \li Path |
| \row \li{1,2} Windows \li{1,2} IniFormat \li UserScope \li \c FOLDERID_RoamingAppData |
| \row \li SystemScope \li \c FOLDERID_ProgramData |
| \row \li{1,2} Unix \li{1,2} NativeFormat, IniFormat \li UserScope \li \c $HOME/.config |
| \row \li SystemScope \li \c /etc/xdg |
| \row \li{1,2} Qt for Embedded Linux \li{1,2} NativeFormat, IniFormat \li UserScope \li \c $HOME/Settings |
| \row \li SystemScope \li \c /etc/xdg |
| \row \li{1,2} \macos and iOS \li{1,2} IniFormat \li UserScope \li \c $HOME/.config |
| \row \li SystemScope \li \c /etc/xdg |
| \endtable |
| |
| The default UserScope paths on Unix, \macos, and iOS (\c |
| $HOME/.config or $HOME/Settings) can be overridden by the user by setting the |
| \c XDG_CONFIG_HOME environment variable. The default SystemScope |
| paths on Unix, \macos, and iOS (\c /etc/xdg) can be overridden when |
| building the Qt library using the \c configure script's \c |
| -sysconfdir flag (see QLibraryInfo for details). |
| |
| Setting the NativeFormat paths on Windows, \macos, and iOS has no |
| effect. |
| |
| \warning This function doesn't affect existing QSettings objects. |
| |
| \sa registerFormat() |
| */ |
| void QSettings::setPath(Format format, Scope scope, const QString &path) |
| { |
| auto locker = qt_unique_lock(settingsGlobalMutex); |
| PathHash *pathHash = pathHashFunc(); |
| if (pathHash->isEmpty()) |
| locker = initDefaultPaths(std::move(locker)); |
| pathHash->insert(pathHashKey(format, scope), Path(path + QDir::separator(), true)); |
| } |
| |
| /*! |
| \typedef QSettings::SettingsMap |
| |
| Typedef for QMap<QString, QVariant>. |
| |
| \sa registerFormat() |
| */ |
| |
| /*! |
| \typedef QSettings::ReadFunc |
| |
| Typedef for a pointer to a function with the following signature: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 27 |
| |
| \c ReadFunc is used in \c registerFormat() as a pointer to a function |
| that reads a set of key/value pairs. \c ReadFunc should read all the |
| options in one pass, and return all the settings in the \c SettingsMap |
| container, which is initially empty. |
| |
| \sa WriteFunc, registerFormat() |
| */ |
| |
| /*! |
| \typedef QSettings::WriteFunc |
| |
| Typedef for a pointer to a function with the following signature: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 28 |
| |
| \c WriteFunc is used in \c registerFormat() as a pointer to a function |
| that writes a set of key/value pairs. \c WriteFunc is only called once, |
| so you need to output the settings in one go. |
| |
| \sa ReadFunc, registerFormat() |
| */ |
| |
| /*! |
| \since 4.1 |
| \threadsafe |
| |
| Registers a custom storage format. On success, returns a special |
| Format value that can then be passed to the QSettings constructor. |
| On failure, returns InvalidFormat. |
| |
| The \a extension is the file |
| extension associated to the format (without the '.'). |
| |
| The \a readFunc and \a writeFunc parameters are pointers to |
| functions that read and write a set of key/value pairs. The |
| QIODevice parameter to the read and write functions is always |
| opened in binary mode (i.e., without the QIODevice::Text flag). |
| |
| The \a caseSensitivity parameter specifies whether keys are case |
| sensitive or not. This makes a difference when looking up values |
| using QSettings. The default is case sensitive. |
| |
| By default, if you use one of the constructors that work in terms |
| of an organization name and an application name, the file system |
| locations used are the same as for IniFormat. Use setPath() to |
| specify other locations. |
| |
| Example: |
| |
| \snippet code/src_corelib_io_qsettings.cpp 29 |
| |
| \sa setPath() |
| */ |
| QSettings::Format QSettings::registerFormat(const QString &extension, ReadFunc readFunc, |
| WriteFunc writeFunc, |
| Qt::CaseSensitivity caseSensitivity) |
| { |
| #ifdef QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER |
| Q_ASSERT(caseSensitivity == Qt::CaseSensitive); |
| #endif |
| |
| const auto locker = qt_scoped_lock(settingsGlobalMutex); |
| CustomFormatVector *customFormatVector = customFormatVectorFunc(); |
| int index = customFormatVector->size(); |
| if (index == 16) // the QSettings::Format enum has room for 16 custom formats |
| return QSettings::InvalidFormat; |
| |
| QConfFileCustomFormat info; |
| info.extension = QLatin1Char('.') + extension; |
| info.readFunc = readFunc; |
| info.writeFunc = writeFunc; |
| info.caseSensitivity = caseSensitivity; |
| customFormatVector->append(info); |
| |
| return QSettings::Format((int)QSettings::CustomFormat1 + index); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #ifndef QT_BOOTSTRAPPED |
| #include "moc_qsettings.cpp" |
| #endif |