/****************************************************************************
** $Id$
**
** Implementation of QSettings class
**
** Created: 2000.06.26
**
** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
**
** This file is part of the tools module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
** 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
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
** information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/
#include "qplatformdefs.h"
#include "qsettings.h"
#ifndef QT_NO_SETTINGS
#include "qdir.h"
#include "qfile.h"
#include "qfileinfo.h"
#include "qmap.h"
#include "qtextstream.h"
#include "qcleanuphandler.h"
#include "qregexp.h"
#include <errno.h>
// for now
#define QSETTINGS_DEFAULT_PATH_SUFFIX "/etc/settings"
/*!
\class QSettings
\brief The QSettings class provides persistent platform-independent application settings.
\ingroup io
\ingroup misc
\mainclass
On Unix systems (including Mac OS X), QSettings uses text files to
store settings. On Windows systems, QSettings uses the system
registry.
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 application startup:
\code
QSettings settings;
settings.insertSearchPath( QSettings::Windows, "/MyCompany" );
// No search path needed for Unix; see notes further on.
// Use default values if the keys don't exist
QString bgColor = settings.readEntry( "/MyApplication/background color", "white" );
int width = settings.readNumEntry( "/MyApplication/geometry/width", 640 );
// ...
\endcode
A typical usage pattern for application exit or 'save preferences':
\code
QSettings settings;
settings.insertSearchPath( QSettings::Windows, "/MyCompany" );
// No search path needed for Unix; see notes further on.
settings.writeEntry( "/MyApplication/background color", bgColor );
settings.writeEntry( "/MyApplication/geometry/width", width );
// ...
\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 = entryList( "/MyApplication" );
// keys contains 'background color' and 'foreground color'.
QStringList keys = entryList( "/MyApplication/recent files" );
// keys contains '1', '2' and '3'.
QStringList subkeys = subkeyList( "/MyApplication" );
// subkeys contains 'geometry' and 'recent files'
QStringList subkeys = subkeyList( "/MyApplication/recent files" );
// subkeys is empty.
\endcode
If you wish to use a different search path call insertSearchPath()
as often as necessary to add your preferred paths. Call
removeSearchPath() to remove any unwanted paths.
Since settings for Windows are stored in the registry some size
limits apply:
\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.
\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 $QT_INSTALL_PREFIX/etc/settings
\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. ($QT_INSTALL_PREFIX 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 multiplatform applications you should ensure that the Windows
size limitations are not exceeded.
*/
/*!
\enum QSettings::System
\value Mac Macintosh execution environments (internal)
\value Unix Mac OS X, Unix, Linux and Unix-like execution environments
\value Windows Windows execution environments
*/
/*
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 int 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
int fd = 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( int 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 );
}
// QSettingsGroup is a map of key/value pairs
class QSettingsGroup : public QMap<QString,QString>
{
public:
QSettingsGroup();
bool modified;
};
QSettingsGroup::QSettingsGroup()
: modified(FALSE)
{
}
// QSettingsHeading is a map of heading/group pairs
class QSettingsHeading : public QMap<QString,QSettingsGroup>
{
public:
QSettingsHeading::Iterator git;
void read(const QString &);
void parseLine(QTextStream &);
};
void QSettingsHeading::read(const QString &filename)
{
if (! QFileInfo(filename).exists())
return;
int lockfd = openlock( filename, F_RDLCK );
QFile file(filename);
if (! file.open(IO_ReadOnly)) {
qWarning("QSettings: failed to open file '%s'", filename.latin1());
return;
}
git = end();
QTextStream stream(&file);
stream.setEncoding(QTextStream::UnicodeUTF8);
while (! stream.atEnd())
parseLine(stream);
git = end();
file.close();
closelock( lockfd );
}
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(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()) {
qWarning("QSettings: line '%s' out of group", line.latin1());
return;
}
int i = line.find('=');
if (i == -1) {
qWarning("QSettings: malformed line '%s' in group '%s'",
line.latin1(), git.key().latin1());
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()) {
qWarning("QSettings: reached end of file, expected continued line");
break;
}
value.append('\n');
line = stream.readLine();
i = 0;
}
}
(*git).insert(key, value);
}
}
}
class QSettingsPrivate
{
public:
QSettingsPrivate();
QSettingsGroup readGroup();
void removeGroup(const QString &);
void writeGroup(const QString &, const QString &);
QDateTime modificationTime();
QStringList searchPaths;
QMap<QString,QSettingsHeading> headings;
QString group;
QString heading;
bool modified;
};
QSettingsPrivate::QSettingsPrivate()
: modified(FALSE)
{
QDir dir(QDir::homeDirPath() + "/.qt/");
if (! dir.exists()) {
if (! dir.mkdir(dir.path()))
qWarning("QSettings: error creating %s", dir.path().latin1());
}
#ifndef QT_INSTALL_PREFIX
# define QT_INSTALL_PREFIX "/usr/local/qt"
#endif // QT_INSTALL_PREFIX
QString defpath(QT_INSTALL_PREFIX);
defpath += QSETTINGS_DEFAULT_PATH_SUFFIX;
searchPaths.append(defpath);
searchPaths.append(dir.path());
}
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();
while (it != searchPaths.end()) {
QString filebase = heading.lower().replace(QRegExp("\\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();
while (it != searchPaths.end()) {
QString filebase = heading.lower().replace(QRegExp("\\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();
while (it != searchPaths.end()) {
QString filebase = heading.lower().replace(QRegExp("\\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();
while (it != searchPaths.end()) {
QFileInfo fi((*it++) + "/" + heading + "rc");
if (fi.exists() && fi.lastModified() > datetime)
datetime = fi.lastModified();
}
return datetime;
}
/*!
Inserts \a path into the settings search path. The semantics of \a
path depends on the system \a s.
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 $QTDIR/etc - where \c $QTDIR is the directory where Qt was installed.
\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 $QTDIR/etc
\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 as shown above,
writing to the first settings file for which the user has write
permission. ($QTDIR is the directory where Qt was installed.)
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.
\c{/MyCompany/MyApplication/background color} will be stored
in \c{myapplicationrc} (assuming that \c{/MyCompany} is
part of the search path).
\sa removeSearchPath()
*/
void QSettings::insertSearchPath( System s, const QString &path)
{
if ( s != Unix ) {
#ifdef Q_OS_MAC
if(s != Mac) //mac is respected on the mac as well
#endif
return;
}
QStringList::Iterator it = d->searchPaths.find(d->searchPaths.last());
if (it != d->searchPaths.end()) {
d->searchPaths.insert(it, path);
}
}
/*!
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 ( s != Unix ) {
#ifdef 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.
*/
QSettings::QSettings()
{
d = new QSettingsPrivate;
Q_CHECK_PTR(d);
}
/*!
Destroys the settings object. All modifications made to the
settings will automatically be saved.
*/
QSettings::~QSettings()
{
sync();
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 (! 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();
QFile file;
QStringList::Iterator pit = d->searchPaths.begin();
while (pit != d->searchPaths.end()) {
QString filebase = it.key().lower().replace(QRegExp("\\s+"), "_");
QFileInfo di(*pit);
QFileInfo fi((*pit++) + "/" + filebase + "rc");
if ((fi.exists() && fi.isFile() && fi.isWritable()) ||
(! fi.exists() && di.isDir() && di.isWritable())) {
file.setName(fi.filePath());
break;
}
}
it++;
if (file.name().isNull() || file.name().isEmpty()) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::sync: filename is null/empty");
#endif // QT_CHECK_STATE
success = FALSE;
continue;
}
int lockfd = openlock( file.name(), F_WRLCK );
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 = "\\0"; // escape null string
} else {
v.replace(QRegExp("\\\\"), "\\\\"); // escape backslash
v.replace(QRegExp("\n"), "\\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();
closelock( lockfd );
}
d->modified = FALSE;
return success;
}
/*!
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
not 0: \a *ok is set to TRUE if the key was read, otherwise \a *ok
is set to FALSE.
\sa readEntry(), readNumEntry(), readDoubleEntry(), writeEntry(), removeEntry()
*/
bool QSettings::readBoolEntry(const QString &key, bool def, bool *ok )
{
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;
}
/*!
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
not 0: \a *ok is set to TRUE if the key was read, otherwise \a *ok
is set to FALSE.
\sa readEntry(), readNumEntry(), readBoolEntry(), writeEntry(),
removeEntry()
*/
double QSettings::readDoubleEntry(const QString &key, double def, bool *ok )
{
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;
}
/*!
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 not 0: \a *ok is set to TRUE if the key was read, otherwise \a *ok
is set to FALSE.
\sa readEntry(), readDoubleEntry(), readBoolEntry(), writeEntry(),
removeEntry()
*/
int QSettings::readNumEntry(const QString &key, int def, bool *ok )
{
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;
}
/*!
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
not 0: \a *ok is set to TRUE if the key was read, otherwise \a *ok
is set to FALSE.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), writeEntry(), removeEntry()
*/
QString QSettings::readEntry(const QString &key, const QString &def, bool *ok )
{
if ( ok ) // no, everything is not ok
*ok = FALSE;
if (key.isNull() || key.isEmpty()) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::readEntry: invalid null/empty key.");
#endif // QT_CHECK_STATE
return def;
}
QString realkey;
if (key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::readEntry: invalid key '%s'", 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 = key;
QSettingsGroup grp = d->readGroup();
QString retval = grp[realkey];
if ( retval.isNull() )
retval = def;
else if ( ok ) // everything is 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 returns TRUE.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, bool value)
{
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 returns TRUE.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, double value)
{
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 returns TRUE.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, int value)
{
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 returns TRUE.
\sa readListEntry(), readNumEntry(), readDoubleEntry(), readBoolEntry(), removeEntry()
*/
bool QSettings::writeEntry(const QString &key, const QString &value)
{
// NOTE: we *do* allow value to be a null/empty string
if (key.isNull() || key.isEmpty()) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::writeEntry: invalid null/empty key.");
#endif // QT_CHECK_STATE
return FALSE;
}
QString realkey;
if (key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::writeEntry: invalid key '%s'", 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 = key;
d->writeGroup(realkey, value);
return TRUE;
}
/*!
Removes the entry specified by \a key.
Returns FALSE if the entry existed and could not be removed;
otherwise returns TRUE.
\sa readEntry(), writeEntry()
*/
bool QSettings::removeEntry(const QString &key)
{
if (key.isNull() || key.isEmpty()) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::removeEntry: invalid null/empty key.");
#endif // QT_CHECK_STATE
return FALSE;
}
QString realkey;
if (key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::removeEntry: invalid key '%s'", 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 = 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 keys.
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 = entryList( "/MyCompany/MyApplication" );
\endcode
\c keys contains 'background color' and 'foreground color'. It does
not contain 'geometry' because that key contains keys not entries.
To access the geometry values either use subkeyList() to read the
keys and 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
{
if (key.isNull() || key.isEmpty()) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::listEntries: invalid null/empty key.");
#endif // QT_CHECK_STATE
return QStringList();
}
QString realkey;
if (key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', key));
if (list.count() < 1) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::listEntries: invalid key '%s'", 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 = 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 keys 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 = subkeyList( "/MyCompany/MyApplication" );
\endcode
\c keys contains 'geometry' and 'recent files'. It does not
contain 'background color' or 'foreground color' because they are
keys which contain entries not keys. To get a list of keys that
have values rather than subkeys use entryList().
\sa entryList()
*/
QStringList QSettings::subkeyList(const QString &key) const
{
if (key.isNull() || key.isEmpty()) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::listSubkeys: invalid null/empty key.");
#endif // QT_CHECK_STATE
return QStringList();
}
QString realkey;
if (key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', key));
if (list.count() < 1) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::listSubkeys: invalid key '%s'", 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 = 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 );
}
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::lastModficationTime(const QString &key)
{
if (key.isNull() || key.isEmpty()) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::lastModficationTime: invalid null/empty key.");
#endif // QT_CHECK_STATE
return QDateTime();
}
if (key[0] == '/') {
// parse our key
QStringList list(QStringList::split('/', key));
if (list.count() < 2) {
#ifdef QT_CHECK_STATE
qWarning("QSettings::lastModficationTime: invalid key '%s'", 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();
}
#endif //QT_NO_SETTINGS
syntax highlighted by Code2HTML, v. 0.9.1