You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
qt3/src/tools/qsettings.cpp

2106 lines
57 KiB

/****************************************************************************
**
** Implementation of QSettings class
**
** Created : 000626
**
** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved.
**
** This file is part of the tools module of the Qt GUI Toolkit.
**
** This file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free
** Software Foundation and appearing in the files LICENSE.GPL2
** and LICENSE.GPL3 included in the packaging of this file.
** Alternatively you may (at your option) use any later version
** of the GNU General Public License if such license has been
** publicly approved by Trolltech ASA (or its successors, if any)
** and the KDE Free Qt Foundation.
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://trolltech.com/products/qt/licenses/licensing/opensource/.
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
** or contact the sales department at sales@trolltech.com.
**
** This file may be used under the terms of the Q Public License as
** defined by Trolltech ASA and appearing in the file LICENSE.QPL
** included in the packaging of this file. Licensees holding valid Qt
** Commercial licenses may use this file in accordance with the Qt
** Commercial License Agreement provided with the Software.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
** herein.
**
**********************************************************************/
#include "qplatformdefs.h"
// POSIX Large File Support redefines open -> open64
static inline int qt_open( const char *pathname, int flags, mode_t mode )
{ return ::open( pathname, flags, mode ); }
#if defined(open)
# undef open
#endif
// POSIX Large File Support redefines truncate -> truncate64
#if defined(truncate)
# undef truncate
#endif
#include "qsettings.h"
#ifndef QT_NO_SETTINGS
#include "qdir.h"
#include "qfile.h"
#include "qfileinfo.h"
#include "qmap.h"
#include "qtextstream.h"
#include "qregexp.h"
#include <private/qsettings_p.h>
#ifndef NO_ERRNO_H
#include <errno.h>
#endif
/*!
\class QSettings
\brief The QSettings class provides persistent platform-independent application settings.
\ingroup io
\ingroup misc
\mainclass
On Unix systems, QSettings uses text files to store settings. On Windows
systems, QSettings uses the system registry. On Mac OS X, QSettings uses
the Carbon preferences API.
Each setting comprises an identifying key and the data associated with
the key. A key is a unicode string which consists of \e two or more
subkeys. A subkey is a slash, '/', followed by one or more unicode
characters (excluding slashes, newlines, carriage returns and equals,
'=', signs). The associated data, called the entry or value, may be a
boolean, an integer, a double, a string or a list of strings. Entry
strings may contain any unicode characters.
If you want to save and restore the entire desktop's settings, i.e.
which applications are running, use QSettings to save the settings
for each individual application and QSessionManager to save the
desktop's session.
Example settings:
\code
/MyCompany/MyApplication/background color
/MyCompany/MyApplication/foreground color
/MyCompany/MyApplication/geometry/x
/MyCompany/MyApplication/geometry/y
/MyCompany/MyApplication/geometry/width
/MyCompany/MyApplication/geometry/height
/MyCompany/MyApplication/recent files/1
/MyCompany/MyApplication/recent files/2
/MyCompany/MyApplication/recent files/3
\endcode
Each line above is a complete key, made up of subkeys.
A typical usage pattern for reading settings at application
startup:
\code
QSettings settings;
settings.setPath( "MyCompany.com", "MyApplication" );
QString bgColor = settings.readEntry( "/colors/background", "white" );
int width = settings.readNumEntry( "/geometry/width", 640 );
// ...
\endcode
A typical usage pattern for saving settings at application exit or
'save preferences':
\code
QSettings settings;
settings.setPath( "MyCompany.com", "MyApplication" );
settings.writeEntry( "/colors/background", bgColor );
settings.writeEntry( "/geometry/width", width );
// ...
\endcode
A key prefix can be prepended to all keys using beginGroup(). The
application of the prefix is stopped using endGroup(). For
example:
\code
QSettings settings;
settings.beginGroup( "/MainWindow" );
settings.beginGroup( "/Geometry" );
int x = settings.readEntry( "/x" );
// ...
settings.endGroup();
settings.beginGroup( "/Toolbars" );
// ...
settings.endGroup();
settings.endGroup();
\endcode
You can get a list of entry-holding keys by calling entryList(), and
a list of key-holding keys using subkeyList().
\code
QStringList keys = settings.entryList( "/MyApplication" );
// keys contains 'background color' and 'foreground color'.
QStringList keys = settings.entryList( "/MyApplication/recent files" );
// keys contains '1', '2' and '3'.
QStringList subkeys = settings.subkeyList( "/MyApplication" );
// subkeys contains 'geometry' and 'recent files'
QStringList subkeys = settings.subkeyList( "/MyApplication/recent files" );
// subkeys is empty.
\endcode
Since settings for Windows are stored in the registry there are
some size limitations as follows:
\list
\i A subkey may not exceed 255 characters.
\i An entry's value may not exceed 16,300 characters.
\i All the values of a key (for example, all the 'recent files'
subkeys values), may not exceed 65,535 characters.
\endlist
These limitations are not enforced on Unix or Mac OS X.
\warning Creating multiple, simultaneous instances of QSettings writing
to a text file may lead to data loss! This is a known issue which will
be fixed in a future release of Qt.
\section1 Notes for Mac OS X Applications
The location where settings are stored is not formally defined by
the CFPreferences API.
At the time of writing settings are stored (either on a global or
user basis, preferring locally) into a plist file in \c
$ROOT/System/Library/Preferences (in XML format). QSettings will
create an appropriate plist file (\c{com.<first group name>.plist})
out of the full path to a key.
For further information on CFPreferences see
\link http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFPreferences/index.html
Apple's Specifications\endlink
\section1 Notes for Unix Applications
There is no universally accepted place for storing application
settings under Unix. In the examples the settings file will be
searched for in the following directories:
\list 1
\i \c SYSCONF - the default value is \c INSTALL/etc/settings
\i \c /opt/MyCompany/share/etc
\i \c /opt/MyCompany/share/MyApplication/etc
\i \c $HOME/.qt
\endlist
When reading settings the files are searched in the order shown
above, with later settings overriding earlier settings. Files for
which the user doesn't have read permission are ignored. When saving
settings QSettings works in the order shown above, writing
to the first settings file for which the user has write permission.
(\c INSTALL is the directory where Qt was installed. This can be
modified by using the configure script's -prefix argument )
If you want to put the settings in a particular place in the
filesystem you could do this:
\code
settings.insertSearchPath( QSettings::Unix, "/opt/MyCompany/share" );
\endcode
But in practice you may prefer not to use a search path for Unix.
For example the following code:
\code
settings.writeEntry( "/MyApplication/geometry/width", width );
\endcode
will end up writing the "geometry/width" setting to the file
\c{$HOME/.qt/myapplicationrc} (assuming that the application is
being run by an ordinary user, i.e. not by root).
For cross-platform applications you should ensure that the
\link #sizelimit Windows size limitations \endlink are not exceeded.
\warning QSettings doesn't write the settings until it is destroyed so
you should construct the QSettings object on the stack.
*/
/*!
\enum QSettings::System
\value Mac Macintosh execution environments
\value Unix Mac OS X, Unix, Linux and Unix-like execution environments
\value Windows Windows execution environments
*/
/*!
\enum QSettings::Format
\value Native Store the settings in a platform dependent location
\value Ini Store the settings in a text file
*/
/*!
\enum QSettings::Scope
\value Global Save settings as global as possible
\value User Save settings in user space
*/
#if defined(Q_OS_UNIX)
typedef int HANDLE;
#define Q_LOCKREAD F_RDLCK
#define Q_LOCKWRITE F_WRLCK
/*
Locks the file specified by name. The lockfile is created as a
hidden file in the same directory as the target file, with .lock
appended to the name. For example, "/etc/settings/onerc" uses a
lockfile named "/etc/settings/.onerc.lock". The type argument
controls the type of the lock, it can be either F_RDLCK for a read
lock, or F_WRLCK for a write lock.
A file descriptor for the lock file is returned, and should be
closed with closelock() when the lock is no longer needed.
*/
static HANDLE openlock( const QString &name, int type )
{
QFileInfo info( name );
// lockfile should be hidden, and never removed
QString lockfile = info.dirPath() + "/." + info.fileName() + ".lock";
// open the lockfile
HANDLE fd = qt_open( QFile::encodeName( lockfile ),
O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
if ( fd < 0 ) {
// failed to open the lock file, most likely because of permissions
return fd;
}
struct flock fl;
fl.l_type = type;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
if ( fcntl( fd, F_SETLKW, &fl ) == -1 ) {
// the lock failed, so we should fail silently, so that people
// using filesystems that do not support locking don't see
// numerous warnings about a failed lock
close( fd );
fd = -1;
}
return fd;
}
/*
Closes the lock file specified by fd. fd is the file descriptor
returned by the openlock() function.
*/
static void closelock( HANDLE fd )
{
if ( fd < 0 ) {
// the lock file is not open
return;
}
struct flock fl;
fl.l_type = F_UNLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
// ignore the return value, so that the unlock fails silently
(void) fcntl( fd, F_SETLKW, &fl );
close( fd );
}
#endif
QSettingsGroup::QSettingsGroup()
: modified(FALSE)
{
}
void QSettingsHeading::read(const QString &filename)
{
if (! QFileInfo(filename).exists())
return;
#ifndef Q_WS_WIN
HANDLE lockfd = openlock( filename, Q_LOCKREAD );
#endif
QFile file(filename);
if (! file.open(IO_ReadOnly)) {
#if defined(QT_CHECK_STATE)
qWarning("QSettings: failed to open file '%s'", filename.latin1());
#endif
return;
}
git = end();
QTextStream stream(&file);
stream.setEncoding(QTextStream::UnicodeUTF8);
while (! stream.atEnd())
parseLine(stream);
git = end();
file.close();
#ifndef Q_WS_WIN
closelock( lockfd );
#endif
}
void QSettingsHeading::parseLine(QTextStream &stream)
{
QString line = stream.readLine();
if (line.isEmpty())
// empty line... we'll allow it
return;
if (line[0] == QChar('#'))
// commented line
return;
if (line[0] == QChar('[')) {
QString gname = line;
gname = gname.remove((uint)0, 1);
if (gname[(int)gname.length() - 1] == QChar(']'))
gname = gname.remove(gname.length() - 1, 1);
git = find(gname);
if (git == end())
git = replace(gname, QSettingsGroup());
} else {
if (git == end()) {
#if defined(QT_CHECK_STATE)
qWarning("QSettings: line '%s' out of group", line.latin1());
#endif
return;
}
int i = line.find('=');
if (i == -1) {
#if defined(QT_CHECK_STATE)
qWarning("QSettings: malformed line '%s' in group '%s'",
line.latin1(), git.key().latin1());
#endif
return;
} else {
QString key, value;
key = line.left(i);
value = "";
bool esc=TRUE;
i++;
while (esc) {
esc = FALSE;
for ( ; i < (int)line.length(); i++ ) {
if ( esc ) {
if ( line[i] == 'n' )
value.append('\n'); // escaped newline
else if ( line[i] == '0' )
value = QString::null; // escaped empty string
else
value.append(line[i]);
esc = FALSE;
} else if ( line[i] == '\\' )
esc = TRUE;
else
value.append(line[i]);
}
if ( esc ) {
// Backwards-compatiblity...
// still escaped at EOL - manually escaped "newline"
if (stream.atEnd()) {
#if defined(QT_CHECK_STATE)
qWarning("QSettings: reached end of file, expected continued line");
#endif
break;
}
value.append('\n');
line = stream.readLine();
i = 0;
}
}
(*git).insert(key, value);
}
}
}
#ifdef Q_WS_WIN // for homedirpath reading from registry
#include "qt_windows.h"
#include "qlibrary.h"
#ifndef CSIDL_APPDATA
#define CSIDL_APPDATA 0x001a // <user name>\Application Data
#endif
#ifndef CSIDL_COMMON_APPDATA
#define CSIDL_COMMON_APPDATA 0x0023 // All Users\Application Data
#endif
#endif
QSettingsPrivate::QSettingsPrivate( QSettings::Format format )
: groupDirty( TRUE ), modified(FALSE), globalScope(TRUE)
{
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( format != QSettings::Ini )
return;
#else
Q_UNUSED( format );
#endif
QString appSettings(QDir::homeDirPath() + "/.qt/");
QString defPath;
#ifdef Q_WS_WIN
#ifdef Q_OS_TEMP
TCHAR path[MAX_PATH];
SHGetSpecialFolderPath( 0, path, CSIDL_APPDATA, FALSE );
appSettings = QString::fromUcs2( path );
SHGetSpecialFolderPath( 0, path, CSIDL_COMMON_APPDATA, FALSE );
defPath = QString::fromUcs2( path );
#else
QLibrary library( "shell32" );
library.setAutoUnload( FALSE );
QT_WA( {
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPTSTR, int, BOOL);
GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve( "SHGetSpecialFolderPathW" );
if ( SHGetSpecialFolderPath ) {
TCHAR path[MAX_PATH];
SHGetSpecialFolderPath( 0, path, CSIDL_APPDATA, FALSE );
appSettings = QString::fromUcs2( (ushort*)path );
SHGetSpecialFolderPath( 0, path, CSIDL_COMMON_APPDATA, FALSE );
defPath = QString::fromUcs2( (ushort*)path );
}
} , {
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, char*, int, BOOL);
GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve( "SHGetSpecialFolderPathA" );
if ( SHGetSpecialFolderPath ) {
char path[MAX_PATH];
SHGetSpecialFolderPath( 0, path, CSIDL_APPDATA, FALSE );
appSettings = QString::fromLocal8Bit( path );
SHGetSpecialFolderPath( 0, path, CSIDL_COMMON_APPDATA, FALSE );
defPath = QString::fromLocal8Bit( path );
}
} );
#endif // Q_OS_TEMP
#else
defPath = qInstallPathSysconf();
#endif
QDir dir(appSettings);
if (! dir.exists()) {
if (! dir.mkdir(dir.path()))
#if defined(QT_CHECK_STATE)
qWarning("QSettings: error creating %s", dir.path().latin1());
#else
;
#endif
}
if ( !!defPath )
searchPaths.append(defPath);
searchPaths.append(dir.path());
}
QSettingsPrivate::~QSettingsPrivate()
{
}
QSettingsGroup QSettingsPrivate::readGroup()
{
QSettingsHeading hd;
QSettingsGroup grp;
QMap<QString,QSettingsHeading>::Iterator headingsit = headings.find(heading);
if (headingsit != headings.end())
hd = *headingsit;
QSettingsHeading::Iterator grpit = hd.find(group);
if (grpit == hd.end()) {
QStringList::Iterator it = searchPaths.begin();
if ( !globalScope )
++it;
while (it != searchPaths.end()) {
QString filebase = heading.lower().replace(QRegExp(QString::fromLatin1("\\s+")), "_");
QString fn((*it++) + "/" + filebase + "rc");
if (! hd.contains(fn + "cached")) {
hd.read(fn);
hd.insert(fn + "cached", QSettingsGroup());
}
}
headings.replace(heading, hd);
grpit = hd.find(group);
if (grpit != hd.end())
grp = *grpit;
} else if (hd.count() != 0)
grp = *grpit;
return grp;
}
void QSettingsPrivate::removeGroup(const QString &key)
{
QSettingsHeading hd;
QSettingsGroup grp;
bool found = FALSE;
QMap<QString,QSettingsHeading>::Iterator headingsit = headings.find(heading);
if (headingsit != headings.end())
hd = *headingsit;
QSettingsHeading::Iterator grpit = hd.find(group);
if (grpit == hd.end()) {
QStringList::Iterator it = searchPaths.begin();
if ( !globalScope )
++it;
while (it != searchPaths.end()) {
QString filebase = heading.lower().replace(QRegExp(QString::fromLatin1("\\s+")), "_");
QString fn((*it++) + "/" + filebase + "rc");
if (! hd.contains(fn + "cached")) {
hd.read(fn);
hd.insert(fn + "cached", QSettingsGroup());
}
}
headings.replace(heading, hd);
grpit = hd.find(group);
if (grpit != hd.end()) {
found = TRUE;
grp = *grpit;
}
} else if (hd.count() != 0) {
found = TRUE;
grp = *grpit;
}
if (found) {
grp.remove(key);
if (grp.count() > 0)
hd.replace(group, grp);
else
hd.remove(group);
if (hd.count() > 0)
headings.replace(heading, hd);
else
headings.remove(heading);
modified = TRUE;
}
}
void QSettingsPrivate::writeGroup(const QString &key, const QString &value)
{
QSettingsHeading hd;
QSettingsGroup grp;
QMap<QString,QSettingsHeading>::Iterator headingsit = headings.find(heading);
if (headingsit != headings.end())
hd = *headingsit;
QSettingsHeading::Iterator grpit = hd.find(group);
if (grpit == hd.end()) {
QStringList::Iterator it = searchPaths.begin();
if ( !globalScope )
++it;
while (it != searchPaths.end()) {
QString filebase = heading.lower().replace(QRegExp(QString::fromLatin1("\\s+")), "_");
QString fn((*it++) + "/" + filebase + "rc");
if (! hd.contains(fn + "cached")) {
hd.read(fn);
hd.insert(fn + "cached", QSettingsGroup());
}
}
headings.replace(heading, hd);
grpit = hd.find(group);
if (grpit != hd.end())
grp = *grpit;
} else if (hd.count() != 0)
grp = *grpit;
grp.modified = TRUE;
grp.replace(key, value);
hd.replace(group, grp);
headings.replace(heading, hd);
modified = TRUE;
}
QDateTime QSettingsPrivate::modificationTime()
{
QSettingsHeading hd = headings[heading];
QSettingsGroup grp = hd[group];
QDateTime datetime;
QStringList::Iterator it = searchPaths.begin();
if ( !globalScope )
++it;
while (it != searchPaths.end()) {
QFileInfo fi((*it++) + "/" + heading + "rc");
if (fi.exists() && fi.lastModified() > datetime)
datetime = fi.lastModified();
}
return datetime;
}
bool qt_verify_key( const QString &key )
{
if ( key.isEmpty() || key[0] != '/' || key.contains( QRegExp(QString::fromLatin1("[=\\r\\n]")) ) )
return FALSE;
return TRUE;
}
static QString groupKey( const QString &group, const QString &key )
{
QString grp_key;
if ( group.isEmpty() || ( group.length() == 1 && group[0] == '/' ) ) {
// group is empty, or it contains a single '/', so we just return the key
if ( key.startsWith( "/" ) )
grp_key = key;
else
grp_key = "/" + key;
} else if ( group.endsWith( "/" ) || key.startsWith( "/" ) ) {
grp_key = group + key;
} else {
grp_key = group + "/" + key;
}
return grp_key;
}
/*!
Inserts \a path into the settings search path. The semantics of \a
path depends on the system \a s. It is usually easier and better to
use setPath() instead of this function.
When \a s is \e Windows and the execution environment is \e not
Windows the function does nothing. Similarly when \a s is \e Unix and
the execution environment is \e not Unix the function does nothing.
When \a s is \e Windows, and the execution environment is Windows, the
search path list will be used as the first subfolder of the "Software"
folder in the registry.
When reading settings the folders are searched forwards from the
first folder (listed below) to the last, returning the first
settings found, and ignoring any folders for which the user doesn't
have read permission.
\list 1
\i HKEY_CURRENT_USER/Software/MyCompany/MyApplication
\i HKEY_LOCAL_MACHINE/Software/MyCompany/MyApplication
\i HKEY_CURRENT_USER/Software/MyApplication
\i HKEY_LOCAL_MACHINE/Software/MyApplication
\endlist
\code
QSettings settings;
settings.insertSearchPath( QSettings::Windows, "/MyCompany" );
settings.writeEntry( "/MyApplication/Tip of the day", TRUE );
\endcode
The code above will write the subkey "Tip of the day" into the \e
first of the registry folders listed below that is found and for
which the user has write permission.
\list 1
\i HKEY_LOCAL_MACHINE/Software/MyCompany/MyApplication
\i HKEY_CURRENT_USER/Software/MyCompany/MyApplication
\i HKEY_LOCAL_MACHINE/Software/MyApplication
\i HKEY_CURRENT_USER/Software/MyApplication
\endlist
If a setting is found in the HKEY_CURRENT_USER space, this setting
is overwritten independently of write permissions in the
HKEY_LOCAL_MACHINE space.
When \a s is \e Unix, and the execution environment is Unix, the
search path list will be used when trying to determine a suitable
filename for reading and writing settings files. By default, there are
two entries in the search path:
\list 1
\i \c SYSCONF - where \c SYSCONF is a directory specified when
configuring Qt; by default it is INSTALL/etc/settings.
\i \c $HOME/.qt/ - where \c $HOME is the user's home directory.
\endlist
All insertions into the search path will go before $HOME/.qt/.
For example:
\code
QSettings settings;
settings.insertSearchPath( QSettings::Unix, "/opt/MyCompany/share/etc" );
settings.insertSearchPath( QSettings::Unix, "/opt/MyCompany/share/MyApplication/etc" );
// ...
\endcode
Will result in a search path of:
\list 1
\i SYSCONF
\i /opt/MyCompany/share/etc
\i /opt/MyCompany/share/MyApplication/etc
\i $HOME/.qt
\endlist
When reading settings the files are searched in the order shown
above, with later settings overriding earlier settings. Files for
which the user doesn't have read permission are ignored. When saving
settings QSettings works in the order shown above, writing
to the first settings file for which the user has write permission.
Note that paths in the file system are not created by this
function, so they must already exist to be useful.
Settings under Unix are stored in files whose names are based on the
first subkey of the key (not including the search path). The algorithm
for creating names is essentially: lowercase the first subkey, replace
spaces with underscores and add 'rc', e.g.
<tt>/MyCompany/MyApplication/background color</tt> will be stored in
<tt>myapplicationrc</tt> (assuming that <tt>/MyCompany</tt> is part of
the search path).
\sa removeSearchPath()
*/
void QSettings::insertSearchPath( System s, const QString &path)
{
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd ) {
d->sysInsertSearchPath( s, path );
return;
}
#endif
#if !defined(Q_WS_WIN)
if ( s == Windows )
return;
#endif
#if !defined(Q_OS_MAC)
if ( s == Mac )
return;
#endif
if ( !qt_verify_key( path ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::insertSearchPath: Invalid key: '%s'", path.isNull() ? "(null)" : path.latin1() );
#endif
return;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd && s != Unix ) {
#else
if ( s != Unix ) {
#endif
#if !defined(QWS) && defined(Q_OS_MAC)
if(s != Mac) //mac is respected on the mac as well
#endif
return;
}
QString realPath = path;
#if defined(Q_WS_WIN)
QString defPath = d->globalScope ? d->searchPaths.first() : d->searchPaths.last();
realPath = defPath + path;
#endif
QStringList::Iterator it = d->searchPaths.find(d->searchPaths.last());
if (it != d->searchPaths.end()) {
d->searchPaths.insert(it, realPath);
}
}
/*!
Removes all occurrences of \a path (using exact matching) from the
settings search path for system \a s. Note that the default search
paths cannot be removed.
\sa insertSearchPath()
*/
void QSettings::removeSearchPath( System s, const QString &path)
{
if ( !qt_verify_key( path ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::insertSearchPath: Invalid key: '%s'", path.isNull() ? "(null)" : path.latin1() );
#endif
return;
}
#ifdef Q_WS_WIN
if ( d->sysd ) {
d->sysRemoveSearchPath( s, path );
return;
}
#endif
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd && s != Unix ) {
#else
if ( s != Unix ) {
#endif
#if !defined(QWS) && defined(Q_OS_MAC)
if(s != Mac) //mac is respected on the mac as well
#endif
return;
}
if (path == d->searchPaths.first() || path == d->searchPaths.last())
return;
d->searchPaths.remove(path);
}
/*!
Creates a settings object.
Be aware that you must call setPath() or insertSearchPath() before
you can use the QSettings object.
*/
QSettings::QSettings()
{
d = new QSettingsPrivate( Native );
Q_CHECK_PTR(d);
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
d->sysd = 0;
d->sysInit();
#endif
}
/*!
Creates a settings object. If \a format is 'Ini' the settings will
be stored in a text file, using the Unix strategy (see above). If \a format
is 'Native', the settings will be stored in a platform specific way
(ie. the Windows registry).
Be aware that you must call setPath() or insertSearchPath() before
you can use the QSettings object.
*/
QSettings::QSettings( Format format )
{
d = new QSettingsPrivate( format );
Q_CHECK_PTR(d);
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
d->sysd = 0;
if ( format == Native )
d->sysInit();
#else
Q_UNUSED(format);
#endif
}
/*!
Destroys the settings object. All modifications made to the settings
will automatically be saved.
*/
QSettings::~QSettings()
{
sync();
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
d->sysClear();
#endif
delete d;
}
/*! \internal
Writes all modifications to the settings to disk. If any errors are
encountered, this function returns FALSE, otherwise it will return TRUE.
*/
bool QSettings::sync()
{
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysSync();
#endif
if (! d->modified)
// fake success
return TRUE;
bool success = TRUE;
QMap<QString,QSettingsHeading>::Iterator it = d->headings.begin();
while (it != d->headings.end()) {
// determine filename
QSettingsHeading hd(*it);
QSettingsHeading::Iterator hdit = hd.begin();
QString filename;
QStringList::Iterator pit = d->searchPaths.begin();
if ( !d->globalScope )
++pit;
while (pit != d->searchPaths.end()) {
QString filebase = it.key().lower().replace(QRegExp(QString::fromLatin1("\\s+")), "_");
QFileInfo di(*pit);
if ( !di.exists() ) {
QDir dir;
dir.mkdir( *pit );
}
QFileInfo fi((*pit++) + "/" + filebase + "rc");
if ((fi.exists() && fi.isFile() && fi.isWritable()) ||
(! fi.exists() && di.isDir()
#ifndef Q_WS_WIN
&& di.isWritable()
#else
&& ((qWinVersion()&Qt::WV_NT_based) > Qt::WV_2000 || di.isWritable())
#endif
)) {
filename = fi.filePath();
break;
}
}
++it;
if ( filename.isEmpty() ) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::sync: filename is null/empty");
#endif // QT_CHECK_STATE
success = FALSE;
continue;
}
#ifndef Q_WS_WIN
HANDLE lockfd = openlock( filename, Q_LOCKWRITE );
#endif
QFile file( filename + ".tmp" );
if (! file.open(IO_WriteOnly)) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::sync: failed to open '%s' for writing",
file.name().latin1());
#endif // QT_CHECK_STATE
success = FALSE;
continue;
}
// spew to file
QTextStream stream(&file);
stream.setEncoding(QTextStream::UnicodeUTF8);
while (hdit != hd.end()) {
if ((*hdit).count() > 0) {
stream << "[" << hdit.key() << "]" << endl;
QSettingsGroup grp(*hdit);
QSettingsGroup::Iterator grpit = grp.begin();
while (grpit != grp.end()) {
QString v = grpit.data();
if ( v.isNull() ) {
v = QString::fromLatin1("\\0"); // escape null string
} else {
v.replace('\\', QString::fromLatin1("\\\\")); // escape backslash
v.replace('\n', QString::fromLatin1("\\n")); // escape newlines
}
stream << grpit.key() << "=" << v << endl;
++grpit;
}
stream << endl;
}
++hdit;
}
if (file.status() != IO_Ok) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::sync: error at end of write");
#endif // QT_CHECK_STATE
success = FALSE;
}
file.close();
if ( success ) {
QDir dir( QFileInfo( file ).dir( TRUE ) );
if ( ( dir.exists( filename ) && !dir.remove( filename ) ) ||
!dir.rename( file.name(), filename, TRUE ) ) {
#ifdef QT_CHECK_STATE
qWarning( "QSettings::sync: error writing file '%s'",
QFile::encodeName( filename ).data() );
#endif // QT_CHECK_STATE
success = FALSE;
}
}
// remove temporary file
file.remove();
#ifndef Q_WS_WIN
closelock( lockfd );
#endif
}
d->modified = FALSE;
return success;
}
/*!
\fn bool QSettings::readBoolEntry(const QString &key, bool def, bool *ok ) const
Reads the entry specified by \a key, and returns a bool, or the
default value, \a def, if the entry couldn't be read.
If \a ok is non-null, *ok is set to TRUE if the key was read, FALSE
otherwise.
\sa readEntry(), readNumEntry(), readDoubleEntry(), writeEntry(), removeEntry()
*/
/*!
\internal
*/
bool QSettings::readBoolEntry(const QString &key, bool def, bool *ok )
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::readBoolEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
if ( ok )
*ok = FALSE;
return def;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysReadBoolEntry( grp_key, def, ok );
#endif
QString value = readEntry( key, ( def ? "true" : "false" ), ok );
if (value.lower() == "true")
return TRUE;
else if (value.lower() == "false")
return FALSE;
else if (value == "1")
return TRUE;
else if (value == "0")
return FALSE;
if (! value.isEmpty())
qWarning("QSettings::readBoolEntry: '%s' is not 'true' or 'false'",
value.latin1());
if ( ok )
*ok = FALSE;
return def;
}
/*!
\fn double QSettings::readDoubleEntry(const QString &key, double def, bool *ok ) const
Reads the entry specified by \a key, and returns a double, or the
default value, \a def, if the entry couldn't be read.
If \a ok is non-null, *ok is set to TRUE if the key was read, FALSE
otherwise.
\sa readEntry(), readNumEntry(), readBoolEntry(), writeEntry(), removeEntry()
*/
/*!
\internal
*/
double QSettings::readDoubleEntry(const QString &key, double def, bool *ok )
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::readDoubleEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
if ( ok )
*ok = FALSE;
return def;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysReadDoubleEntry( grp_key, def, ok );
#endif
QString value = readEntry( key, QString::number(def), ok );
bool conv_ok;
double retval = value.toDouble( &conv_ok );
if ( conv_ok )
return retval;
if ( ! value.isEmpty() )
qWarning( "QSettings::readDoubleEntry: '%s' is not a number",
value.latin1() );
if ( ok )
*ok = FALSE;
return def;
}
/*!
\fn int QSettings::readNumEntry(const QString &key, int def, bool *ok ) const
Reads the entry specified by \a key, and returns an integer, or the
default value, \a def, if the entry couldn't be read.
If \a ok is non-null, *ok is set to TRUE if the key was read, FALSE
otherwise.
\sa readEntry(), readDoubleEntry(), readBoolEntry(), writeEntry(), removeEntry()
*/
/*!
\internal
*/
int QSettings::readNumEntry(const QString &key, int def, bool *ok )
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::readNumEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
if ( ok )
*ok = FALSE;
return def;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysReadNumEntry( grp_key, def, ok );
#endif
QString value = readEntry( key, QString::number( def ), ok );
bool conv_ok;
int retval = value.toInt( &conv_ok );
if ( conv_ok )
return retval;
if ( ! value.isEmpty() )
qWarning( "QSettings::readNumEntry: '%s' is not a number",
value.latin1() );
if ( ok )
*ok = FALSE;
return def;
}
/*!
\fn QString QSettings::readEntry(const QString &key, const QString &def, bool *ok ) const
Reads the entry specified by \a key, and returns a QString, or the
default value, \a def, if the entry couldn't be read.
If \a ok is non-null, *ok is set to TRUE if the key was read, FALSE
otherwise.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), writeEntry(), removeEntry()
*/
/*!
\internal
*/
QString QSettings::readEntry(const QString &key, const QString &def, bool *ok )
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::readEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
if ( ok )
*ok = FALSE;
return def;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysReadEntry( grp_key, def, ok );
#endif
if ( ok ) // no, everything is not ok
*ok = FALSE;
QString realkey;
if (grp_key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', grp_key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::readEntry: invalid key '%s'", grp_key.latin1());
#endif // QT_CHECK_STATE
if ( ok )
*ok = FALSE;
return def;
}
if (list.count() == 2) {
d->heading = list[0];
d->group = "General";
realkey = list[1];
} else {
d->heading = list[0];
d->group = list[1];
// remove the group from the list
list.remove(list.at(1));
// remove the heading from the list
list.remove(list.at(0));
realkey = list.join("/");
}
} else {
realkey = grp_key;
}
QSettingsGroup grp = d->readGroup();
QSettingsGroup::const_iterator it = grp.find( realkey ), end = grp.end();
QString retval = def;
if ( it != end ) {
// found the value we needed
retval = *it;
if ( ok ) *ok = TRUE;
}
return retval;
}
#if !defined(Q_NO_BOOL_TYPE)
/*!
Writes the boolean entry \a value into key \a key. The \a key is
created if it doesn't exist. Any previous value is overwritten by \a
value.
If an error occurs the settings are left unchanged and FALSE is
returned; otherwise TRUE is returned.
\warning On certain platforms, keys are required to contain at least
two components (e.g., "/foo/bar"). This limitation does not apply to
Qt 4.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, bool value)
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::writeEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return FALSE;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysWriteEntry( grp_key, value );
#endif
QString s(value ? "true" : "false");
return writeEntry(key, s);
}
#endif
/*!
\overload
Writes the double entry \a value into key \a key. The \a key is
created if it doesn't exist. Any previous value is overwritten by \a
value.
If an error occurs the settings are left unchanged and FALSE is
returned; otherwise TRUE is returned.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, double value)
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::writeEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return FALSE;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysWriteEntry( grp_key, value );
#endif
QString s(QString::number(value));
return writeEntry(key, s);
}
/*!
\overload
Writes the integer entry \a value into key \a key. The \a key is
created if it doesn't exist. Any previous value is overwritten by \a
value.
If an error occurs the settings are left unchanged and FALSE is
returned; otherwise TRUE is returned.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, int value)
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::writeEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return FALSE;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysWriteEntry( grp_key, value );
#endif
QString s(QString::number(value));
return writeEntry(key, s);
}
/*!
\internal
Writes the entry specified by \a key with the string-literal \a value,
replacing any previous setting. If \a value is zero-length or null, the
entry is replaced by an empty setting.
\e NOTE: This function is provided because some compilers use the
writeEntry (const QString &, bool) overload for this code:
writeEntry ("/foo/bar", "baz")
If an error occurs, this functions returns FALSE and the object is left
unchanged.
\sa readEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, const char *value)
{
return writeEntry(key, QString(value));
}
/*!
\overload
Writes the string entry \a value into key \a key. The \a key is
created if it doesn't exist. Any previous value is overwritten by \a
value. If \a value is an empty string or a null string the key's
value will be an empty string.
If an error occurs the settings are left unchanged and FALSE is
returned; otherwise TRUE is returned.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, const QString &value)
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::writeEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return FALSE;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysWriteEntry( grp_key, value );
#endif
// NOTE: we *do* allow value to be a null/empty string
QString realkey;
if (grp_key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', grp_key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::writeEntry: invalid key '%s'", grp_key.latin1());
#endif // QT_CHECK_STATE
return FALSE;
}
if (list.count() == 2) {
d->heading = list[0];
d->group = "General";
realkey = list[1];
} else {
d->heading = list[0];
d->group = list[1];
// remove the group from the list
list.remove(list.at(1));
// remove the heading from the list
list.remove(list.at(0));
realkey = list.join("/");
}
} else {
realkey = grp_key;
}
d->writeGroup(realkey, value);
return TRUE;
}
/*!
Removes the entry specified by \a key.
Returns true if the entry was successfully removed; otherwise
returns false. Note that removing the last entry in any given
folder, will also remove the folder.
\sa readEntry(), writeEntry()
*/
bool QSettings::removeEntry(const QString &key)
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::removeEntry: Invalid key: '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return FALSE;
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysRemoveEntry( grp_key );
#endif
QString realkey;
if (grp_key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', grp_key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::removeEntry: invalid key '%s'", grp_key.latin1());
#endif // QT_CHECK_STATE
return FALSE;
}
if (list.count() == 2) {
d->heading = list[0];
d->group = "General";
realkey = list[1];
} else {
d->heading = list[0];
d->group = list[1];
// remove the group from the list
list.remove(list.at(1));
// remove the heading from the list
list.remove(list.at(0));
realkey = list.join("/");
}
} else {
realkey = grp_key;
}
d->removeGroup(realkey);
return TRUE;
}
/*!
Returns a list of the keys which contain entries under \a key. Does \e
not return any keys that contain subkeys.
Example settings:
\code
/MyCompany/MyApplication/background color
/MyCompany/MyApplication/foreground color
/MyCompany/MyApplication/geometry/x
/MyCompany/MyApplication/geometry/y
/MyCompany/MyApplication/geometry/width
/MyCompany/MyApplication/geometry/height
\endcode
\code
QStringList keys = settings.entryList( "/MyCompany/MyApplication" );
\endcode
In the above example, \c keys will contain 'background color' and
'foreground color'. It will not contain 'geometry' because this key
contains subkeys not entries.
To access the geometry values, you could either use subkeyList()
to read the keys then read each entry, or simply read each entry
directly by specifying its full key, e.g.
"/MyCompany/MyApplication/geometry/y".
\sa subkeyList()
*/
QStringList QSettings::entryList(const QString &key) const
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::entryList: Invalid key: %s", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return QStringList();
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysEntryList( grp_key );
#endif
QString realkey;
if (grp_key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', grp_key));
if (list.count() < 1) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::listEntries: invalid key '%s'", grp_key.latin1());
#endif // QT_CHECK_STATE
return QStringList();
}
if (list.count() == 1) {
d->heading = list[0];
d->group = "General";
} else {
d->heading = list[0];
d->group = list[1];
// remove the group from the list
list.remove(list.at(1));
// remove the heading from the list
list.remove(list.at(0));
realkey = list.join("/");
}
} else
realkey = grp_key;
QSettingsGroup grp = d->readGroup();
QSettingsGroup::Iterator it = grp.begin();
QStringList ret;
QString itkey;
while (it != grp.end()) {
itkey = it.key();
++it;
if ( realkey.length() > 0 ) {
if ( itkey.left( realkey.length() ) != realkey )
continue;
else
itkey.remove( 0, realkey.length() + 1 );
}
if ( itkey.find( '/' ) != -1 )
continue;
ret << itkey;
}
return ret;
}
/*!
Returns a list of the keys which contain subkeys under \a key. Does \e
not return any keys that contain entries.
Example settings:
\code
/MyCompany/MyApplication/background color
/MyCompany/MyApplication/foreground color
/MyCompany/MyApplication/geometry/x
/MyCompany/MyApplication/geometry/y
/MyCompany/MyApplication/geometry/width
/MyCompany/MyApplication/geometry/height
/MyCompany/MyApplication/recent files/1
/MyCompany/MyApplication/recent files/2
/MyCompany/MyApplication/recent files/3
\endcode
\code
QStringList keys = settings.subkeyList( "/MyCompany/MyApplication" );
\endcode
In the above example, \c keys will contain 'geometry' and
'recent files'. It will not contain 'background color' or
'foreground color' because those keys contain entries not
subkeys. To get a list of keys that contain entries rather than
subkeys use entryList() instead.
\warning In the above example, if QSettings is writing to an Ini file,
then a call to
\code subkeyList("/MyCompany") \endcode
will return an empty list. This happens because a key like
\code /MyCompany/MyApplication/background color \endcode
is written to the file \e{"mycompanyrc"}, under the section \e{[MyApplication]}.
This call is therefore a request to list the sections in an ini file, which
is not supported in this version of QSettings. This is a known issue which
will be fixed in Qt-4.
\sa entryList()
*/
QStringList QSettings::subkeyList(const QString &key) const
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::subkeyList: Invalid key: %s", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return QStringList();
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return d->sysSubkeyList( grp_key );
#endif
QString realkey;
int subkeycount = 2;
if (grp_key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', grp_key));
if (list.count() < 1) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::subkeyList: invalid key '%s'", grp_key.latin1());
#endif // QT_CHECK_STATE
return QStringList();
}
subkeycount = (int)list.count();
if (list.count() == 1) {
d->heading = list[0];
d->group = "General";
} else {
d->heading = list[0];
d->group = list[1];
// remove the group from the list
list.remove(list.at(1));
// remove the heading from the list
list.remove(list.at(0));
realkey = list.join("/");
}
} else
realkey = grp_key;
QStringList ret;
if ( subkeycount == 1 ) {
QMap<QString,QSettingsHeading>::Iterator it = d->headings.begin();
while ( it != d->headings.end() ) {
if ( it.key() != "General" && ! ret.contains( it.key() ) )
ret << it.key();
++it;
}
return ret;
}
QSettingsGroup grp = d->readGroup();
QSettingsGroup::Iterator it = grp.begin();
QString itkey;
while (it != grp.end()) {
itkey = it.key();
++it;
if ( realkey.length() > 0 ) {
if ( itkey.left( realkey.length() ) != realkey
|| itkey.length()+1 < realkey.length()
|| itkey[(int)realkey.length()] != '/')
continue;
else
itkey.remove( 0, realkey.length() + 1 );
}
int slash = itkey.find( '/' );
if ( slash == -1 )
continue;
itkey.truncate( slash );
if ( ! ret.contains( itkey ) )
ret << itkey;
}
return ret;
}
/*!
\internal
This function returns the time of last modification for \a key.
*/
QDateTime QSettings::lastModificationTime( const QString &key )
{
QString grp_key( groupKey( group(), key ) );
if ( !qt_verify_key( grp_key ) ) {
#if defined(QT_CHECK_STATE)
qWarning( "QSettings::lastModificationTime: Invalid key '%s'", grp_key.isNull() ? "(null)" : grp_key.latin1() );
#endif
return QDateTime();
}
#if !defined(QWS) && (defined(Q_WS_WIN) || defined(Q_OS_MAC))
if ( d->sysd )
return QDateTime();
#endif
if (grp_key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', grp_key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::lastModificationTime: Invalid key '%s'", grp_key.latin1());
#endif // QT_CHECK_STATE
return QDateTime();
}
if (list.count() == 2) {
d->heading = list[0];
d->group = "General";
} else {
d->heading = list[0];
d->group = list[1];
}
}
return d->modificationTime();
}
/*!
\overload
\obsolete
Writes the string list entry \a value into key \a key. The \a key
is created if it doesn't exist. Any previous value is overwritten
by \a value. The list is stored as a sequence of strings separated
by \a separator (using QStringList::join()), so none of the
strings in the list should contain the separator. If the list is
empty or null the key's value will be an empty string.
\warning The list should not contain empty or null strings, as
readListEntry() will use QStringList::split() to recreate the
list. As the documentation states, QStringList::split() will omit
empty strings from the list. Because of this, it is impossible to
retrieve identical list data that is stored with this function.
We recommend using the writeEntry() and readListEntry() overloads
that do not take a \a separator argument.
If an error occurs the settings are left unchanged and FALSE is
returned; otherwise returns TRUE.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry(), QStringList::join()
*/
bool QSettings::writeEntry(const QString &key, const QStringList &value,
const QChar &separator)
{
QString s(value.join(separator));
return writeEntry(key, s);
}
/*!
\overload
Writes the string list entry \a value into key \a key. The \a key
is created if it doesn't exist. Any previous value is overwritten
by \a value.
If an error occurs the settings are left unchanged and FALSE is
returned; otherwise returns TRUE.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, const QStringList &value)
{
QString s;
for (QStringList::ConstIterator it=value.begin(); it!=value.end(); ++it) {
QString el = *it;
if ( el.isNull() ) {
el = "^0";
} else {
el.replace("^", "^^");
}
s+=el;
s+="^e"; // end of element
}
return writeEntry(key, s);
}
/*!
\overload QStringList QSettings::readListEntry(const QString &key, const QChar &separator, bool *ok ) const
\obsolete
Reads the entry specified by \a key as a string. The \a separator
is used to create a QStringList by calling QStringList::split(\a
separator, entry). If \a ok is not 0: \a *ok is set to TRUE
if the key was read, otherwise \a *ok is set to FALSE.
\warning As the documentation states, QStringList::split() will
omit empty strings from the list. Because of this, it is
impossible to retrieve identical list data with this function. We
recommend using the readListEntry() and writeEntry() overloads
that do not take a \a separator argument.
Note that if you want to iterate over the list, you should iterate
over a copy, e.g.
\code
QStringList list = mySettings.readListEntry( "size", " " );
QStringList::Iterator it = list.begin();
while( it != list.end() ) {
myProcessing( *it );
++it;
}
\endcode
\sa readEntry(), readDoubleEntry(), readBoolEntry(), writeEntry(), removeEntry(), QStringList::split()
*/
/*!
\internal
*/
QStringList QSettings::readListEntry(const QString &key, const QChar &separator, bool *ok )
{
QString value = readEntry( key, QString::null, ok );
if ( ok && !*ok )
return QStringList();
return QStringList::split(separator, value);
}
/*!
\fn QStringList QSettings::readListEntry(const QString &key, bool *ok ) const
Reads the entry specified by \a key as a string. If \a ok is not
0, \a *ok is set to TRUE if the key was read, otherwise \a *ok is
set to FALSE.
Note that if you want to iterate over the list, you should iterate
over a copy, e.g.
\code
QStringList list = mySettings.readListEntry( "recentfiles" );
QStringList::Iterator it = list.begin();
while( it != list.end() ) {
myProcessing( *it );
++it;
}
\endcode
\sa readEntry(), readDoubleEntry(), readBoolEntry(), writeEntry(), removeEntry(), QStringList::split()
*/
/*!
\internal
*/
QStringList QSettings::readListEntry(const QString &key, bool *ok )
{
QString value = readEntry( key, QString::null, ok );
if ( ok && !*ok )
return QStringList();
QStringList l;
QString s;
bool esc=FALSE;
for (int i=0; i<(int)value.length(); i++) {
if ( esc ) {
if ( value[i] == 'e' ) { // end-of-string
l.append(s);
s="";
} else if ( value[i] == '0' ) { // null string
s=QString::null;
} else {
s.append(value[i]);
}
esc=FALSE;
} else if ( value[i] == '^' ) {
esc = TRUE;
} else {
s.append(value[i]);
if ( i == (int)value.length()-1 )
l.append(s);
}
}
return l;
}
#ifdef Q_OS_MAC
void qt_setSettingsBasePath(const QString &); //qsettings_mac.cpp
#endif
/*!
Insert platform-dependent paths from platform-independent information.
The \a domain should be an Internet domain name
controlled by the producer of the software, eg. Trolltech products
use "trolltech.com".
The \a product should be the official name of the product.
The \a scope should be
QSettings::User for user-specific settings, or
QSettings::Global for system-wide settings (generally
these will be read-only to many users).
Not all information is relevant on all systems.
*/
void QSettings::setPath( const QString &domain, const QString &product, Scope scope )
{
// On Windows, any trailing ".com(\..*)" is stripped from the domain. The
// Global scope corresponds to HKEY_LOCAL_MACHINE, and User corresponds to
// HKEY_CURRENT_USER. Note that on some installations, not all users can
// write to the Global scope. On UNIX, any trailing ".com(\..*)" is stripped
// from the domain. The Global scope corresponds to "/opt" (this would be
// configurable at library build time - eg. to "/usr/local" or "/usr"),
// while the User scope corresponds to $HOME/.*rc.
// Note that on most installations, not all users can write to the System
// scope.
//
// On MacOS X, if there is no "." in domain, append ".com", then reverse the
// order of the elements (Mac OS uses "com.apple.finder" as domain+product).
// The Global scope corresponds to /Library/Preferences/*.plist, while the
// User scope corresponds to ~/Library/Preferences/*.plist.
// Note that on most installations, not all users can write to the System
// scope.
d->globalScope = scope == Global;
QString actualSearchPath;
int lastDot = domain.findRev( '.' );
#if defined(Q_WS_WIN)
actualSearchPath = "/" + domain.mid( 0, lastDot ) + "/" + product;
insertSearchPath( Windows, actualSearchPath );
#elif !defined(QWS) && defined(Q_OS_MAC)
if(lastDot != -1) {
QString topLevelDomain = domain.right( domain.length() - lastDot - 1 ) + ".";
if ( !topLevelDomain.isEmpty() )
qt_setSettingsBasePath( topLevelDomain );
}
actualSearchPath = "/" + domain.left( lastDot ) + "." + product;
insertSearchPath( Mac, actualSearchPath );
#else
if (scope == User)
actualSearchPath = QDir::homeDirPath() + "/.";
else
actualSearchPath = QString(qInstallPathSysconf()) + "/";
actualSearchPath += domain.mid( 0, lastDot ) + "/" + product;
insertSearchPath( Unix, actualSearchPath );
#endif
}
/*!
Appends \a group to the current key prefix.
\code
QSettings settings;
settings.beginGroup( "/MainWindow" );
// read values
settings.endGroup();
\endcode
*/
void QSettings::beginGroup( const QString &group )
{
d->groupStack.push( group );
d->groupDirty = TRUE;
}
/*!
Undo previous calls to beginGroup(). Note that a single beginGroup("a/b/c") is undone
by a single call to endGroup().
\code
QSettings settings;
settings.beginGroup( "/MainWindow/Geometry" );
// read values
settings.endGroup();
\endcode
*/
void QSettings::endGroup()
{
d->groupStack.pop();
d->groupDirty = TRUE;
}
/*!
Set the current key prefix to the empty string.
*/
void QSettings::resetGroup()
{
d->groupStack.clear();
d->groupDirty = FALSE;
d->groupPrefix = QString::null;
}
/*!
Returns the current key prefix, or a null string if there is no key prefix set.
\sa beginGroup();
*/
QString QSettings::group() const
{
if ( d->groupDirty ) {
d->groupDirty = FALSE;
d->groupPrefix = QString::null;
QValueStack<QString>::Iterator it = d->groupStack.begin();
while ( it != d->groupStack.end() ) {
QString group = *it;
++it;
if ( group[0] != '/' )
group.prepend( "/" );
d->groupPrefix += group;
}
}
return d->groupPrefix;
}
#endif