diff options
Diffstat (limited to 'src/core')
30 files changed, 10548 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 00000000..ea272d31 --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,53 @@ + +find_package(Qt5Core 5.2.0 REQUIRED NO_MODULE) + +set(libkconfigcore_SRCS + kconfig.cpp + kconfigbase.cpp + kconfigdata.cpp + kconfiggroup.cpp + kconfigbackend.cpp + kconfigini.cpp + kdesktopfile.cpp + ksharedconfig.cpp + kcoreconfigskeleton.cpp + kauthorized.cpp + kemailsettings.cpp +) + +add_library(KF5ConfigCore ${libkconfigcore_SRCS}) +generate_export_header(KF5ConfigCore BASE_NAME KConfigCore) +add_library(KF5::ConfigCore ALIAS KF5ConfigCore) + +target_link_libraries(KF5ConfigCore PUBLIC Qt5::Core) +if(WIN32) + target_link_libraries(KF5ConfigCore PRIVATE ${KDEWIN_LIBRARIES}) +endif() + +if(IS_ABSOLUTE "${INCLUDE_INSTALL_DIR}") + target_include_directories(KF5ConfigCore INTERFACE "$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>" ) +else() + target_include_directories(KF5ConfigCore INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}>" ) +endif() + +set_target_properties(KF5ConfigCore PROPERTIES VERSION ${KCONFIG_VERSION_STRING} + SOVERSION ${KCONFIG_SOVERSION} + EXPORT_NAME ConfigCore +) + +install(TARGETS KF5ConfigCore EXPORT KF5ConfigTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) + +install( FILES + ${CMAKE_CURRENT_BINARY_DIR}/kconfigcore_export.h + conversion_check.h + kconfig.h + #kconfigbackend.h re-enable post-API review and implementation (4.2?) + kconfigbase.h + kconfiggroup.h + kdesktopfile.h + ksharedconfig.h + kcoreconfigskeleton.h + kauthorized.h + kemailsettings.h + DESTINATION ${INCLUDE_INSTALL_DIR} COMPONENT Devel +) diff --git a/src/core/bufferfragment_p.h b/src/core/bufferfragment_p.h new file mode 100644 index 00000000..5a753ad4 --- /dev/null +++ b/src/core/bufferfragment_p.h @@ -0,0 +1,181 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2008 Jakub Stachowski <qbast@go2.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef BUFFERFRAGMENT_H +#define BUFFERFRAGMENT_H + +#define bf_isspace(str) ((str == ' ') || (str == '\t') || (str == '\r')) + +// This class provides wrapper around fragment of existing buffer (array of bytes). +// If underlying buffer gets deleted, all BufferFragment objects referencing it become invalid. +// Use toByteArray() to make deep copy of the buffer fragment. +// +// API is designed to subset of QByteArray methods with some changes: +// - trim() is like QByteArray.trimmed(), but it modifies current object +// - truncateLeft() provides way to cut off beginning of the buffer +// - split() works more like strtok_r than QByteArray.split() +// - truncateLeft() and mid() require position argument to be valid + +class KConfigIniBackend::BufferFragment +{ + +public: + + BufferFragment() : d(0), len(0) + { + } + + BufferFragment(char* buf, int size) : d(buf), len(size) + { + } + + int length() const + { + return len; + } + + char at(unsigned int i) const + { + Q_ASSERT(i < len); + return d[i]; + } + + void clear() + { + len = 0; + } + + const char* constData() const + { + return d; + } + + char* data() const + { + return d; + } + + void trim() + { + while (bf_isspace(*d) && len > 0) { + d++; + len--; + } + while (len > 0 && bf_isspace(d[len - 1])) + len--; + } + + // similar to strtok_r . On first call variable pointed by start should be set to 0. + // Each call will update *start to new starting position. + BufferFragment split(char c, unsigned int* start) + { + while (*start < len) { + int end = indexOf(c, *start); + if (end == -1) end = len; + BufferFragment line(d + (*start), end - (*start)); + *start = end + 1; + return line; + } + return BufferFragment(); + } + + bool isEmpty() const + { + return (len == 0); + } + + BufferFragment left(unsigned int size) const + { + return BufferFragment(d, qMin(size,len)); + } + + void truncateLeft(unsigned int size) + { + Q_ASSERT(size <= len); + d += size; + len -= size; + } + + void truncate(unsigned int pos) + { + if (pos < len) len = pos; + } + + bool isNull() const + { + return (d == 0); + } + + BufferFragment mid(unsigned int pos, int length=-1) const + { + Q_ASSERT(pos < len); + int size = length; + if (length == -1 || (pos + length) > len) + size = len - pos; + return BufferFragment(d + pos, size); + } + + bool operator==(const QByteArray& other) const + { + return (other.size() == (int)len && memcmp(d,other.constData(),len) == 0); + } + + bool operator!=(const QByteArray& other) const + { + return (other.size() != (int)len || memcmp(d,other.constData(),len) != 0); + } + + int indexOf(char c, unsigned int from = 0) const + { + const char* cursor = d + from - 1; + const char* end = d + len; + while ( ++cursor < end) + if (*cursor ==c ) + return cursor - d; + return -1; + } + + int lastIndexOf(char c) const + { + int from = len - 1; + while (from >= 0) + if (d[from] == c) + return from; + else + from--; + return -1; + } + + QByteArray toByteArray() const { + return QByteArray(d,len); + } + + // this is faster than toByteArray, but returned QByteArray becomes invalid + // when buffer for this BufferFragment disappears + QByteArray toVolatileByteArray() const { + return QByteArray::fromRawData(d, len); + } + +private: + char* d; + unsigned int len; +}; + +#endif diff --git a/src/core/conversion_check.h b/src/core/conversion_check.h new file mode 100644 index 00000000..23bdcf04 --- /dev/null +++ b/src/core/conversion_check.h @@ -0,0 +1,120 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006 Thomas Braxton <brax108@cox.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef CONVERSION_CHECK_H +#define CONVERSION_CHECK_H + +#include <QtCore/QString> +#include <QtCore/QDate> +#include <QtCore/QPoint> +#include <QtCore/QSize> +#include <QtCore/QRect> +#include <QtCore/QVariant> + +class QColor; +class QFont; + +namespace ConversionCheck { + +// used to distinguish between supported/unsupported types +struct supported { }; +struct unsupported { }; + +// traits type class to define support for constraints +template <typename T> +struct QVconvertible +{ + typedef unsupported toQString; + typedef unsupported toQVariant; +}; + +// constraint classes +template <typename T> +struct type_toQString +{ + void constraint() { supported x = y; Q_UNUSED(x); } + typename QVconvertible<T>::toQString y; +}; + +template <typename T> +struct type_toQVariant +{ + void constraint() { supported x = y; Q_UNUSED(x); } + typename QVconvertible<T>::toQVariant y; +}; + + +// check if T is convertible to QString thru QVariant +// if not supported can't be used in QList<T> functions +template <typename T> +inline void to_QString() +{ + void (type_toQString<T>::*x)() = &type_toQString<T>::constraint; + Q_UNUSED(x); +} + +// check if T is convertible to QVariant & supported in readEntry/writeEntry +template <typename T> +inline void to_QVariant() +{ + void (type_toQVariant<T>::*x)() = &type_toQVariant<T>::constraint; + Q_UNUSED(x); +} + +// define for all types handled in readEntry/writeEntry +// string_support - is supported by QVariant(type).toString(), +// can be used in QList<T> functions +// variant_support - has a QVariant constructor +#define QVConversions(type, string_support, variant_support) \ +template <> struct QVconvertible<type> {\ + typedef string_support toQString;\ + typedef variant_support toQVariant;\ +} + +// The only types needed here are the types handled in readEntry/writeEntry +// the default QVconvertible will take care of the rest. +QVConversions(bool, supported, supported); +QVConversions(int, supported, supported); +QVConversions(unsigned int, supported, supported); +QVConversions(long long, supported, supported); +QVConversions(unsigned long long, supported, supported); +QVConversions(float, supported, supported); +QVConversions(double, supported, supported); +QVConversions(QString, supported, supported); +QVConversions(QColor, unsupported, supported); +QVConversions(QFont, supported, supported); +QVConversions(QDateTime, unsupported, supported); +QVConversions(QDate, unsupported, supported); +QVConversions(QSize, unsupported, supported); +QVConversions(QRect, unsupported, supported); +QVConversions(QPoint, unsupported, supported); +QVConversions(QSizeF, unsupported, supported); +QVConversions(QRectF, unsupported, supported); +QVConversions(QPointF, unsupported, supported); +QVConversions(QByteArray, supported, supported); +QVConversions(QStringList, unsupported, supported); +QVConversions(QVariantList, unsupported, supported); +QVConversions(QUrl, supported, supported); +QVConversions(QList<QUrl>, unsupported, supported); +} + +#endif + diff --git a/src/core/kauthorized.cpp b/src/core/kauthorized.cpp new file mode 100644 index 00000000..abf479ca --- /dev/null +++ b/src/core/kauthorized.cpp @@ -0,0 +1,393 @@ +/* This file is part of the KDE libraries + Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) + Copyright (C) 1998, 1999, 2000 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kauthorized.h" + +#include <QtCore/QDir> +#include <QtCore/QRegExp> +#include <QList> +#include <QUrl> + +#include <QCoreApplication> +#include <ksharedconfig.h> +#include <stdlib.h> // srand(), rand() +#ifndef Q_OS_WIN +#include <unistd.h> +#include <netdb.h> +#endif + +#include <kconfiggroup.h> + +#include <QMutex> +#include <QMutexLocker> + +extern bool kde_kiosk_exception; + + +class URLActionRule +{ + public: +#define checkExactMatch(s, b) \ + if (s.isEmpty()) b = true; \ + else if (s[s.length()-1] == QLatin1Char('!')) \ + { b = false; s.truncate(s.length()-1); } \ + else b = true; +#define checkStartWildCard(s, b) \ + if (s.isEmpty()) b = true; \ + else if (s[0] == QLatin1Char('*')) \ + { b = true; s = s.mid(1); } \ + else b = false; +#define checkEqual(s, b) \ + b = (s == QString::fromLatin1("=")); + + URLActionRule(const QByteArray &act, + const QString &bProt, const QString &bHost, const QString &bPath, + const QString &dProt, const QString &dHost, const QString &dPath, + bool perm) + : action(act), + baseProt(bProt), baseHost(bHost), basePath(bPath), + destProt(dProt), destHost(dHost), destPath(dPath), + permission(perm) + { + checkExactMatch(baseProt, baseProtWildCard); + checkStartWildCard(baseHost, baseHostWildCard); + checkExactMatch(basePath, basePathWildCard); + checkExactMatch(destProt, destProtWildCard); + checkStartWildCard(destHost, destHostWildCard); + checkExactMatch(destPath, destPathWildCard); + checkEqual(destProt, destProtEqual); + checkEqual(destHost, destHostEqual); + } + + bool baseMatch(const QUrl &url, const QString &protClass) const + { + if (baseProtWildCard) + { + if ( !baseProt.isEmpty() && !url.scheme().startsWith(baseProt) && + (protClass.isEmpty() || (protClass != baseProt)) ) + return false; + } + else + { + if ( (url.scheme() != baseProt) && + (protClass.isEmpty() || (protClass != baseProt)) ) + return false; + } + if (baseHostWildCard) + { + if (!baseHost.isEmpty() && !url.host().endsWith(baseHost)) + return false; + } + else + { + if (url.host() != baseHost) + return false; + } + if (basePathWildCard) + { + if (!basePath.isEmpty() && !url.path().startsWith(basePath)) + return false; + } + else + { + if (url.path() != basePath) + return false; + } + return true; + } + + bool destMatch(const QUrl &url, const QString &protClass, const QUrl &base, const QString &baseClass) const + { + if (destProtEqual) + { + if ( (url.scheme() != base.scheme()) && + (protClass.isEmpty() || baseClass.isEmpty() || protClass != baseClass) ) + return false; + } + else if (destProtWildCard) + { + if ( !destProt.isEmpty() && !url.scheme().startsWith(destProt) && + (protClass.isEmpty() || (protClass != destProt)) ) + return false; + } + else + { + if ( (url.scheme() != destProt) && + (protClass.isEmpty() || (protClass != destProt)) ) + return false; + } + if (destHostWildCard) + { + if (!destHost.isEmpty() && !url.host().endsWith(destHost)) + return false; + } + else if (destHostEqual) + { + if (url.host() != base.host()) + return false; + } + else + { + if (url.host() != destHost) + return false; + } + if (destPathWildCard) + { + if (!destPath.isEmpty() && !url.path().startsWith(destPath)) + return false; + } + else + { + if (url.path() != destPath) + return false; + } + return true; + } + + QByteArray action; + QString baseProt; + QString baseHost; + QString basePath; + QString destProt; + QString destHost; + QString destPath; + bool baseProtWildCard : 1; + bool baseHostWildCard : 1; + bool basePathWildCard : 1; + bool destProtWildCard : 1; + bool destHostWildCard : 1; + bool destPathWildCard : 1; + bool destProtEqual : 1; + bool destHostEqual : 1; + bool permission; +}; + +class KAuthorizedPrivate { +public: + KAuthorizedPrivate() + : actionRestrictions( false ), blockEverything(false),mutex(QMutex::Recursive) + { + Q_ASSERT_X(QCoreApplication::instance(),"KAuthorizedPrivate()","There has to be an existing QCoreApplication::instance() pointer"); + + KSharedConfig::Ptr config = KSharedConfig::openConfig(); + + Q_ASSERT_X(config,"KAuthorizedPrivate()","There has to be an existing KSharedConfig::openConfig() pointer"); + if (!config) { + blockEverything=true; + return; + } + actionRestrictions = config->hasGroup("KDE Action Restrictions" ) && !kde_kiosk_exception; + } + + ~KAuthorizedPrivate() + { + } + + bool actionRestrictions : 1; + bool blockEverything : 1; + QList<URLActionRule> urlActionRestrictions; + QMutex mutex; +}; + +Q_GLOBAL_STATIC(KAuthorizedPrivate,authPrivate) +#define MY_D KAuthorizedPrivate *d=authPrivate(); + + +bool KAuthorized::authorize(const QString &genericAction) +{ + MY_D + if (d->blockEverything) return false; + + if (!d->actionRestrictions) + return true; + + KConfigGroup cg(KSharedConfig::openConfig(), "KDE Action Restrictions"); + return cg.readEntry(genericAction, true); +} + +bool KAuthorized::authorizeKAction(const QString& action) +{ + MY_D + if (d->blockEverything) return false; + if (!d->actionRestrictions || action.isEmpty()) + return true; + + return authorize(QLatin1String("action/") + action); +} + +bool KAuthorized::authorizeControlModule(const QString &menuId) +{ + if (menuId.isEmpty() || kde_kiosk_exception) + return true; + KConfigGroup cg(KSharedConfig::openConfig(), "KDE Control Module Restrictions"); + return cg.readEntry(menuId, true); +} + +QStringList KAuthorized::authorizeControlModules(const QStringList &menuIds) +{ + KConfigGroup cg(KSharedConfig::openConfig(), "KDE Control Module Restrictions"); + QStringList result; + for(QStringList::ConstIterator it = menuIds.begin(); + it != menuIds.end(); ++it) + { + if (cg.readEntry(*it, true)) + result.append(*it); + } + return result; +} + +static void initUrlActionRestrictions() +{ + MY_D + const QString Any; + + d->urlActionRestrictions.clear(); + d->urlActionRestrictions.append( + URLActionRule("open", Any, Any, Any, Any, Any, Any, true)); + d->urlActionRestrictions.append( + URLActionRule("list", Any, Any, Any, Any, Any, Any, true)); +// TEST: +// d->urlActionRestrictions.append( +// URLActionRule("list", Any, Any, Any, Any, Any, Any, false)); +// d->urlActionRestrictions.append( +// URLActionRule("list", Any, Any, Any, "file", Any, QDir::homePath(), true)); + d->urlActionRestrictions.append( + URLActionRule("link", Any, Any, Any, QLatin1String(":internet"), Any, Any, true)); + d->urlActionRestrictions.append( + URLActionRule("redirect", Any, Any, Any, QLatin1String(":internet"), Any, Any, true)); + + // We allow redirections to file: but not from internet protocols, redirecting to file: + // is very popular among io-slaves and we don't want to break them + d->urlActionRestrictions.append( + URLActionRule("redirect", Any, Any, Any, QLatin1String("file"), Any, Any, true)); + d->urlActionRestrictions.append( + URLActionRule("redirect", QLatin1String(":internet"), Any, Any, QLatin1String("file"), Any, Any, false)); + + // local protocols may redirect everywhere + d->urlActionRestrictions.append( + URLActionRule("redirect", QLatin1String(":local"), Any, Any, Any, Any, Any, true)); + + // Anyone may redirect to about: + d->urlActionRestrictions.append( + URLActionRule("redirect", Any, Any, Any, QLatin1String("about"), Any, Any, true)); + + // Anyone may redirect to mailto: + d->urlActionRestrictions.append( + URLActionRule("redirect", Any, Any, Any, QLatin1String("mailto"), Any, Any, true)); + + // Anyone may redirect to itself, cq. within it's own group + d->urlActionRestrictions.append( + URLActionRule("redirect", Any, Any, Any, QLatin1String("="), Any, Any, true)); + + d->urlActionRestrictions.append( + URLActionRule("redirect", QLatin1String("about"), Any, Any, Any, Any, Any, true)); + + + KConfigGroup cg(KSharedConfig::openConfig(), "KDE URL Restrictions"); + int count = cg.readEntry("rule_count", 0); + QString keyFormat = QString::fromLatin1("rule_%1"); + for(int i = 1; i <= count; i++) + { + QString key = keyFormat.arg(i); + const QStringList rule = cg.readEntry(key, QStringList()); + if (rule.count() != 8) + continue; + const QByteArray action = rule[0].toLatin1(); + QString refProt = rule[1]; + QString refHost = rule[2]; + QString refPath = rule[3]; + QString urlProt = rule[4]; + QString urlHost = rule[5]; + QString urlPath = rule[6]; + bool bEnabled = (rule[7].toLower() == QLatin1String("true")); + + if (refPath.startsWith(QLatin1String("$HOME"))) + refPath.replace(0, 5, QDir::homePath()); + else if (refPath.startsWith(QLatin1Char('~'))) + refPath.replace(0, 1, QDir::homePath()); + if (urlPath.startsWith(QLatin1String("$HOME"))) + urlPath.replace(0, 5, QDir::homePath()); + else if (urlPath.startsWith(QLatin1Char('~'))) + urlPath.replace(0, 1, QDir::homePath()); + + if (refPath.startsWith(QLatin1String("$TMP"))) + refPath.replace(0, 4, QDir::tempPath()); + if (urlPath.startsWith(QLatin1String("$TMP"))) + urlPath.replace(0, 4, QDir::tempPath()); + + d->urlActionRestrictions.append( + URLActionRule( action, refProt, refHost, refPath, urlProt, urlHost, urlPath, bEnabled)); + } +} + +namespace KAuthorized +{ +// Called by KAuthorized::allowUrlAction in KIO +KCONFIGCORE_EXPORT void allowUrlActionInternal(const QString &action, const QUrl &_baseURL, const QUrl &_destURL) +{ + MY_D + QMutexLocker locker((&d->mutex)); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) + const QString basePath = _baseURL.adjusted(QUrl::StripTrailingSlash).path(); + const QString destPath = _destURL.adjusted(QUrl::StripTrailingSlash).path(); +#else + const QString basePath = QUrl(_baseURL.toString(QUrl::StripTrailingSlash)).path(); + const QString destPath = QUrl(_destURL.toString(QUrl::StripTrailingSlash)).path(); +#endif + + d->urlActionRestrictions.append( URLActionRule + ( action.toLatin1(), _baseURL.scheme(), _baseURL.host(), basePath, + _destURL.scheme(), _destURL.host(), destPath, true)); +} + +// Called by KAuthorized::authorizeUrlAction in KIO +KCONFIGCORE_EXPORT bool authorizeUrlActionInternal(const QString& action, const QUrl &_baseURL, const QUrl &_destURL, const QString& baseClass, const QString& destClass) +{ + MY_D + QMutexLocker locker(&(d->mutex)); + if (d->blockEverything) return false; + + if (_destURL.isEmpty()) + return true; + + bool result = false; + if (d->urlActionRestrictions.isEmpty()) + initUrlActionRestrictions(); + + QUrl baseURL(_baseURL); + baseURL.setPath(QDir::cleanPath(baseURL.path())); + + QUrl destURL(_destURL); + destURL.setPath(QDir::cleanPath(destURL.path())); + + Q_FOREACH(const URLActionRule &rule, d->urlActionRestrictions) { + if ((result != rule.permission) && // No need to check if it doesn't make a difference + (action == QLatin1String(rule.action.constData())) && + rule.baseMatch(baseURL, baseClass) && + rule.destMatch(destURL, destClass, baseURL, baseClass)) + { + result = rule.permission; + } + } + return result; +} + +} // namespace diff --git a/src/core/kauthorized.h b/src/core/kauthorized.h new file mode 100644 index 00000000..a16368a8 --- /dev/null +++ b/src/core/kauthorized.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE libraries + Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) + Copyright (c) 1998, 1999 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KAUTHORIZED_H +#define KAUTHORIZED_H + +#include <kconfigcore_export.h> + +class QUrl; +class QString; +class QStringList; + +/** +* Kiosk authorization framework +* +* Core functionality, see kauthorized.h for authorizeUrlAction. +*/ +namespace KAuthorized +{ + /** + * Returns whether a certain action is authorized + * @param genericAction The name of a generic action + * @return true if the action is authorized + * @todo what are the generic actions? + */ + KCONFIGCORE_EXPORT bool authorize(const QString& genericAction); + + /** + * Returns whether a certain KAction is authorized. + * + * @param action The name of a KAction action. The name is prepended + * with "action/" before being passed to authorize() + * @return true if the KAction is authorized + */ + KCONFIGCORE_EXPORT bool authorizeKAction(const QString& action); + + /** + * Returns whether access to a certain control module is authorized. + * + * @param menuId identifying the control module, e.g. kde-mouse.desktop + * @return true if access to the module is authorized, false otherwise. + */ + KCONFIGCORE_EXPORT bool authorizeControlModule(const QString& menuId); + + /** + * Returns which control modules from a given list are authorized for access. + * + * @param menuIds list of menu-ids of control modules; + * an example of a menu-id is kde-mouse.desktop. + * @return Those control modules for which access has been authorized. + */ + KCONFIGCORE_EXPORT QStringList authorizeControlModules(const QStringList& menuIds); + +} + +#endif diff --git a/src/core/kconfig.cpp b/src/core/kconfig.cpp new file mode 100644 index 00000000..b311a366 --- /dev/null +++ b/src/core/kconfig.cpp @@ -0,0 +1,931 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Qt5 TODO: re-enable. No point in doing it before, it breaks on QString::fromUtf8(QByteArray), which exists in qt5. +#undef QT_NO_CAST_FROM_BYTEARRAY + +#include "kconfig.h" +#include "kconfig_p.h" + +#include <cstdlib> +#include <fcntl.h> + +#ifdef Q_OS_WIN +static inline FILE* popen(const char *cmd, const char *mode) { return _popen(cmd, mode); } +static inline int pclose(FILE* stream) { return _pclose(stream); } +#else +#include <unistd.h> +#endif + +#include "kconfigbackend.h" +#include "kconfiggroup.h" + +#include <qcoreapplication.h> +#include <qprocess.h> +#include <qstandardpaths.h> +#include <qbytearray.h> +#include <qfile.h> +#include <qlocale.h> +#include <qdir.h> +#include <QtCore/QProcess> +#include <QtCore/QSet> + +bool KConfigPrivate::mappingsRegistered=false; + +KConfigPrivate::KConfigPrivate(KConfig::OpenFlags flags, + QStandardPaths::StandardLocation resourceType) + : openFlags(flags), resourceType(resourceType), mBackend(0), + bDynamicBackend(true), bDirty(false), bReadDefaults(false), + bFileImmutable(false), bForceGlobal(false), bSuppressGlobal(false), + configState(KConfigBase::NoAccess) +{ + sGlobalFileName = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kdeglobals"); + + static int use_etc_kderc = -1; + if (use_etc_kderc < 0) + use_etc_kderc = !qEnvironmentVariableIsSet("KDE_SKIP_KDERC"); // for unit tests + if (use_etc_kderc) { + + etc_kderc = +#ifdef Q_OS_WIN + QFile::decodeName( qgetenv("WINDIR") + "/kde4rc" ); +#else + QLatin1String("/etc/kde4rc"); +#endif + if (!QFileInfo(etc_kderc).isReadable()) { + etc_kderc.clear(); + } + } + +// if (!mappingsRegistered) { +// KEntryMap tmp; +// if (!etc_kderc.isEmpty()) { +// QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(etc_kderc, QLatin1String("INI")); +// backend->parseConfig( "en_US", tmp, KConfigBackend::ParseDefaults); +// } +// const QString kde4rc(QDir::home().filePath(".kde4rc")); +// if (KStandardDirs::checkAccess(kde4rc, R_OK)) { +// QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(kde4rc, QLatin1String("INI")); +// backend->parseConfig( "en_US", tmp, KConfigBackend::ParseOptions()); +// } +// KConfigBackend::registerMappings(tmp); +// mappingsRegistered = true; +// } + +#if 0 // KDE4 code + setLocale(KLocale::global()->language()); +#else + setLocale(QLocale::system().name()); +#endif +} + + +bool KConfigPrivate::lockLocal() +{ + if (mBackend) { + return mBackend->lock(); + } + // anonymous object - pretend we locked it + return true; +} + +void KConfigPrivate::copyGroup(const QByteArray& source, const QByteArray& destination, + KConfigGroup *otherGroup, KConfigBase::WriteConfigFlags flags) const +{ + KEntryMap& otherMap = otherGroup->config()->d_ptr->entryMap; + const int len = source.length(); + const bool sameName = (destination == source); + + // we keep this bool outside the foreach loop so that if + // the group is empty, we don't end up marking the other config + // as dirty erroneously + bool dirtied = false; + + for (KEntryMap::ConstIterator entryMapIt( entryMap.constBegin() ); entryMapIt != entryMap.constEnd(); ++entryMapIt) { + const QByteArray& group = entryMapIt.key().mGroup; + + if (!group.startsWith(source)) // nothing to do + continue; + + // don't copy groups that start with the same prefix, but are not sub-groups + if (group.length() > len && group[len] != '\x1d') + continue; + + KEntryKey newKey = entryMapIt.key(); + + if (flags & KConfigBase::Localized) { + newKey.bLocal = true; + } + + if (!sameName) + newKey.mGroup.replace(0, len, destination); + + KEntry entry = entryMap[ entryMapIt.key() ]; + dirtied = entry.bDirty = flags & KConfigBase::Persistent; + + if (flags & KConfigBase::Global) { + entry.bGlobal = true; + } + + otherMap[newKey] = entry; + } + + if (dirtied) { + otherGroup->config()->d_ptr->bDirty = true; + } +} + +QString KConfigPrivate::expandString(const QString& value) +{ + QString aValue = value; + + // check for environment variables and make necessary translations + int nDollarPos = aValue.indexOf( QLatin1Char('$') ); + while( nDollarPos != -1 && nDollarPos+1 < aValue.length()) { + // there is at least one $ + if( aValue[nDollarPos+1] == QLatin1Char('(') ) { + int nEndPos = nDollarPos+1; + // the next character is not $ + while ( (nEndPos <= aValue.length()) && (aValue[nEndPos]!=QLatin1Char(')')) ) + nEndPos++; + nEndPos++; + QString cmd = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 ); + + QString result; +#if 0 // Removed in KDE Frameworks 5. No such concept anymore. Just set your PATH. + QByteArray oldpath = qgetenv( "PATH" ); + QByteArray newpath; + if (KComponentData::hasMainComponent()) { + newpath = QFile::encodeName(KGlobal::dirs()->resourceDirs("exe").join(QChar::fromLatin1(KPATH_SEPARATOR))); + if (!newpath.isEmpty() && !oldpath.isEmpty()) + newpath += KPATH_SEPARATOR; + } + newpath += oldpath; + qputenv( "PATH", newpath); +#endif + +// FIXME: wince does not have pipes +#ifndef _WIN32_WCE + FILE *fs = popen(QFile::encodeName(cmd).data(), "r"); + if (fs) { + QTextStream ts(fs, QIODevice::ReadOnly); + result = ts.readAll().trimmed(); + pclose(fs); + } +#endif +#if 0 // Removed in KDE Frameworks 5, see above. + qputenv( "PATH", oldpath); +#endif + aValue.replace( nDollarPos, nEndPos-nDollarPos, result ); + nDollarPos += result.length(); + } else if( aValue[nDollarPos+1] != QLatin1Char('$') ) { + int nEndPos = nDollarPos+1; + // the next character is not $ + QString aVarName; + if ( aValue[nEndPos] == QLatin1Char('{') ) { + while ( (nEndPos <= aValue.length()) && (aValue[nEndPos] != QLatin1Char('}')) ) + nEndPos++; + nEndPos++; + aVarName = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 ); + } else { + while ( nEndPos <= aValue.length() && + (aValue[nEndPos].isNumber() || + aValue[nEndPos].isLetter() || + aValue[nEndPos] == QLatin1Char('_') ) ) + nEndPos++; + aVarName = aValue.mid( nDollarPos+1, nEndPos-nDollarPos-1 ); + } + QString env; + if (!aVarName.isEmpty()) { +#ifdef Q_OS_WIN + if (aVarName == QLatin1String("HOME")) + env = QDir::homePath(); + else +#endif + { + QByteArray pEnv = qgetenv(aVarName.toLatin1().constData()); + if( !pEnv.isEmpty() ) + env = QString::fromLocal8Bit(pEnv.constData()); + } + aValue.replace(nDollarPos, nEndPos-nDollarPos, env); + nDollarPos += env.length(); + } else + aValue.remove( nDollarPos, nEndPos-nDollarPos ); + } else { + // remove one of the dollar signs + aValue.remove( nDollarPos, 1 ); + nDollarPos++; + } + nDollarPos = aValue.indexOf( QLatin1Char('$'), nDollarPos ); + } + + return aValue; +} + + +KConfig::KConfig(const QString& file, OpenFlags mode, + QStandardPaths::StandardLocation resourceType) + : d_ptr(new KConfigPrivate(mode, resourceType)) +{ + d_ptr->changeFileName(file); // set the local file name + + // read initial information off disk + reparseConfiguration(); +} + +KConfig::KConfig(const QString& file, const QString& backend, QStandardPaths::StandardLocation resourceType) + : d_ptr(new KConfigPrivate(SimpleConfig, resourceType)) +{ + d_ptr->mBackend = KConfigBackend::create(file, backend); + d_ptr->bDynamicBackend = false; + d_ptr->changeFileName(file); // set the local file name + + // read initial information off disk + reparseConfiguration(); +} + +KConfig::KConfig(KConfigPrivate &d) + : d_ptr(&d) +{ +} + +KConfig::~KConfig() +{ + Q_D(KConfig); + if (d->bDirty && (d->mBackend && d->mBackend->ref.load() == 1)) + sync(); + delete d; +} + +QStringList KConfig::groupList() const +{ + Q_D(const KConfig); + QSet<QString> groups; + + for (KEntryMap::ConstIterator entryMapIt( d->entryMap.constBegin() ); entryMapIt != d->entryMap.constEnd(); ++entryMapIt) { + const KEntryKey& key = entryMapIt.key(); + const QByteArray group = key.mGroup; + if (key.mKey.isNull() && !group.isEmpty() && group != "<default>" && group != "$Version") { + const QString groupname = QString::fromUtf8(group); + groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d'))); + } + } + + return groups.toList(); +} + +QStringList KConfigPrivate::groupList(const QByteArray& group) const +{ + QByteArray theGroup = group + '\x1d'; + QSet<QString> groups; + + for (KEntryMap::ConstIterator entryMapIt( entryMap.constBegin() ); entryMapIt != entryMap.constEnd(); ++entryMapIt) { + const KEntryKey& key = entryMapIt.key(); + if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) { + const QString groupname = QString::fromUtf8(key.mGroup.mid(theGroup.length())); + groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d'))); + } + } + + return groups.toList(); +} + +// List all sub groups, including subsubgroups +QSet<QByteArray> KConfigPrivate::allSubGroups(const QByteArray& parentGroup) const +{ + QSet<QByteArray> groups; + QByteArray theGroup = parentGroup + '\x1d'; + groups << parentGroup; + + for (KEntryMap::const_iterator entryMapIt = entryMap.begin(); entryMapIt != entryMap.end(); ++entryMapIt) { + const KEntryKey& key = entryMapIt.key(); + if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) { + groups << key.mGroup; + } + } + return groups; +} + +bool KConfigPrivate::hasNonDeletedEntries(const QByteArray& group) const +{ + const QSet<QByteArray> allGroups = allSubGroups(group); + + Q_FOREACH(const QByteArray& aGroup, allGroups) { + // Could be optimized, let's use the slow way for now + // Check for any non-deleted entry + if (!keyListImpl(aGroup).isEmpty()) + return true; + } + return false; +} + + +QStringList KConfigPrivate::keyListImpl(const QByteArray& theGroup) const +{ + QStringList keys; + + const KEntryMapConstIterator theEnd = entryMap.constEnd(); + KEntryMapConstIterator it = entryMap.findEntry(theGroup); + if (it != theEnd) { + ++it; // advance past the special group entry marker + + QSet<QString> tmp; + for (; it != theEnd && it.key().mGroup == theGroup; ++it) { + const KEntryKey& key = it.key(); + if (key.mGroup == theGroup && !key.mKey.isNull() && !it->bDeleted) + tmp << QString::fromUtf8(key.mKey); + } + keys = tmp.toList(); + } + + return keys; +} + +QStringList KConfig::keyList(const QString& aGroup) const +{ + Q_D(const KConfig); + const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8()); + return d->keyListImpl(theGroup); +} + +QMap<QString,QString> KConfig::entryMap(const QString& aGroup) const +{ + Q_D(const KConfig); + QMap<QString, QString> theMap; + const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8()); + + const KEntryMapConstIterator theEnd = d->entryMap.constEnd(); + KEntryMapConstIterator it = d->entryMap.findEntry(theGroup, 0, 0); + if (it != theEnd) { + ++it; // advance past the special group entry marker + + for (; it != theEnd && it.key().mGroup == theGroup; ++it) { + // leave the default values and deleted entries out + if (!it->bDeleted && !it.key().bDefault) { + const QString key = QString::fromUtf8(it.key().mKey.constData()); + // the localized entry should come first, so don't overwrite it + // with the non-localized entry + if (!theMap.contains(key)) { + if (it->bExpand) { + theMap.insert(key,KConfigPrivate::expandString(QString::fromUtf8(it->mValue.constData()))); + } else { + theMap.insert(key,QString::fromUtf8(it->mValue.constData())); + } + } + } + } + } + + return theMap; +} + +bool KConfig::sync() +{ + Q_D(KConfig); + + if (isImmutable() || name().isEmpty()) { + // can't write to an immutable or anonymous file. + return false; + } + + if (d->bDirty && d->mBackend) { + const QByteArray utf8Locale(locale().toUtf8()); + + // Create the containing dir, maybe it wasn't there + d->mBackend->createEnclosing(); + + // lock the local file + if (d->configState == ReadWrite && !d->lockLocal()) { + qWarning() << "couldn't lock local file"; + return false; + } + + // Rewrite global/local config only if there is a dirty entry in it. + bool writeGlobals = false; + bool writeLocals = false; + Q_FOREACH (const KEntry& e, d->entryMap) { + if (e.bDirty) { + if (e.bGlobal) { + writeGlobals = true; + } else { + writeLocals = true; + } + + if (writeGlobals && writeLocals) { + break; + } + } + } + + d->bDirty = false; // will revert to true if a config write fails + + if (d->wantGlobals() && writeGlobals) { + QExplicitlySharedDataPointer<KConfigBackend> tmp = KConfigBackend::create(d->sGlobalFileName); + if (d->configState == ReadWrite && !tmp->lock()) { + qWarning() << "couldn't lock global file"; + d->bDirty = true; + return false; + } + if (!tmp->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteGlobal)) { + d->bDirty = true; + } + if (tmp->isLocked()) { + tmp->unlock(); + } + } + + if (writeLocals) { + if (!d->mBackend->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteOptions())) { + d->bDirty = true; + } + } + if (d->mBackend->isLocked()) { + d->mBackend->unlock(); + } + } + return !d->bDirty; +} + +void KConfig::markAsClean() +{ + Q_D(KConfig); + d->bDirty = false; + + // clear any dirty flags that entries might have set + const KEntryMapIterator theEnd = d->entryMap.end(); + for (KEntryMapIterator it = d->entryMap.begin(); it != theEnd; ++it) + it->bDirty = false; +} + +bool KConfig::isDirty() const +{ + Q_D(const KConfig); + return d->bDirty; +} + +void KConfig::checkUpdate(const QString &id, const QString &updateFile) +{ + const KConfigGroup cg(this, "$Version"); + const QString cfg_id = updateFile+QLatin1Char(':')+id; + const QStringList ids = cg.readEntry("update_info", QStringList()); + if (!ids.contains(cfg_id)) { +#if 0 + KToolInvocation::kdeinitExecWait(QString::fromLatin1("kconf_update"), QStringList() << QString::fromLatin1("--check") << updateFile); +#else + QProcess::execute(QString::fromLatin1("kconf_update"), QStringList() << QString::fromLatin1("--check") << updateFile); +#endif + reparseConfiguration(); + } +} + +KConfig* KConfig::copyTo(const QString &file, KConfig *config) const +{ + Q_D(const KConfig); + if (!config) + config = new KConfig(QString(), SimpleConfig, d->resourceType); + config->d_func()->changeFileName(file); + config->d_func()->entryMap = d->entryMap; + config->d_func()->bFileImmutable = false; + + const KEntryMapIterator theEnd = config->d_func()->entryMap.end(); + for (KEntryMapIterator it = config->d_func()->entryMap.begin(); it != theEnd; ++it) + it->bDirty = true; + config->d_ptr->bDirty = true; + + return config; +} + +QString KConfig::name() const +{ + Q_D(const KConfig); + return d->fileName; +} + +Q_GLOBAL_STATIC(QString, globalMainConfigName) + +void KConfig::setMainConfigName(const QString& str) +{ + *globalMainConfigName() = str; +} + +QString KConfig::mainConfigName() +{ + // --config on the command line overrides everything else + const QStringList args = QCoreApplication::arguments(); + for (int i = 1; i < args.count() ; ++i) { + if (args.at(i) == QLatin1String("--config") && i < args.count()-1) { + return args.at(i+1); + } + } + const QString globalName = *globalMainConfigName(); + if (!globalName.isEmpty()) + return globalName; + + QString appName = QCoreApplication::applicationName(); + return appName + QLatin1String("rc"); +} + +void KConfigPrivate::changeFileName(const QString& name) +{ + fileName = name; + + QString file; + if (name.isEmpty()) { + if (wantDefaults()) { // accessing default app-specific config "appnamerc" + fileName = KConfig::mainConfigName(); + file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName; + } else if (wantGlobals()) { // accessing "kdeglobals" - XXX used anywhere? + resourceType = QStandardPaths::GenericConfigLocation; + fileName = QLatin1String("kdeglobals"); + file = sGlobalFileName; + } else { + // anonymous config + openFlags = KConfig::SimpleConfig; + return; + } + } else if (QDir::isAbsolutePath(fileName)) { + fileName = QFileInfo(fileName).canonicalFilePath(); + if (fileName.isEmpty()) // file doesn't exist (yet) + fileName = name; + file = fileName; + } else { + file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName; + } + + Q_ASSERT(!file.isEmpty()); + +#ifndef Q_OS_WIN + bSuppressGlobal = (file == sGlobalFileName); +#else + bSuppressGlobal = (file.compare(sGlobalFileName, Qt::CaseInsensitive) == 0); +#endif + + if (bDynamicBackend || !mBackend) // allow dynamic changing of backend + mBackend = KConfigBackend::create(file); + else + mBackend->setFilePath(file); + + configState = mBackend->accessMode(); +} + +void KConfig::reparseConfiguration() +{ + Q_D(KConfig); + if (d->fileName.isEmpty()) { + return; + } + + // Don't lose pending changes + if (!d->isReadOnly() && d->bDirty) + sync(); + + d->entryMap.clear(); + + d->bFileImmutable = false; + + // Parse all desired files from the least to the most specific. + if (d->wantGlobals()) + d->parseGlobalFiles(); + + d->parseConfigFiles(); +} + + +QStringList KConfigPrivate::getGlobalFiles() const +{ + QStringList globalFiles; + Q_FOREACH (const QString& dir1, QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QLatin1String("kdeglobals"))) + globalFiles.push_front(dir1); + Q_FOREACH (const QString& dir2, QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QLatin1String("system.kdeglobals"))) + globalFiles.push_front(dir2); + if (!etc_kderc.isEmpty()) + globalFiles.push_front(etc_kderc); + return globalFiles; +} + +void KConfigPrivate::parseGlobalFiles() +{ + const QStringList globalFiles = getGlobalFiles(); +// qDebug() << "parsing global files" << globalFiles; + + // TODO: can we cache the values in etc_kderc / other global files + // on a per-application basis? + const QByteArray utf8Locale = locale.toUtf8(); + Q_FOREACH(const QString& file, globalFiles) { + KConfigBackend::ParseOptions parseOpts = KConfigBackend::ParseGlobal|KConfigBackend::ParseExpansions; +#ifndef Q_OS_WIN + if (file != sGlobalFileName) +#else + if (file.compare(sGlobalFileName, Qt::CaseInsensitive) != 0) +#endif + parseOpts |= KConfigBackend::ParseDefaults; + + QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(file); + if ( backend->parseConfig( utf8Locale, entryMap, parseOpts) == KConfigBackend::ParseImmutable) + break; + } +} + +void KConfigPrivate::parseConfigFiles() +{ + // can only read the file if there is a backend and a file name + if (mBackend && !fileName.isEmpty()) { + + bFileImmutable = false; + + QList<QString> files; + if (wantDefaults()) { + if (bSuppressGlobal) { + files = getGlobalFiles(); + } else { + if (QDir::isAbsolutePath(fileName)) { + files << fileName; + } else { + Q_FOREACH (const QString& f, QStandardPaths::locateAll(resourceType, fileName)) + files.prepend(f); + } + } + } else { + files << mBackend->filePath(); + } + if (!isSimple()) + files = extraFiles.toList() + files; + +// qDebug() << "parsing local files" << files; + + const QByteArray utf8Locale = locale.toUtf8(); + foreach(const QString& file, files) { +#ifndef Q_OS_WIN + if (file == mBackend->filePath()) { +#else + if (file.compare(mBackend->filePath(), Qt::CaseInsensitive) == 0) { +#endif + switch (mBackend->parseConfig(utf8Locale, entryMap, KConfigBackend::ParseExpansions)) { + case KConfigBackend::ParseOk: + break; + case KConfigBackend::ParseImmutable: + bFileImmutable = true; + break; + case KConfigBackend::ParseOpenError: + configState = KConfigBase::NoAccess; + break; + } + } else { + QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(file); + bFileImmutable = (backend->parseConfig(utf8Locale, entryMap, + KConfigBackend::ParseDefaults|KConfigBackend::ParseExpansions) + == KConfigBackend::ParseImmutable); + } + + if (bFileImmutable) + break; + } +#pragma message("TODO: enable kiosk feature again (resource restrictions), but without KStandardDirs... Needs a class in the kconfig framework.") +#if 0 + if (componentData.dirs()->isRestrictedResource(resourceType, fileName)) + bFileImmutable = true; +#endif + } +} + +KConfig::AccessMode KConfig::accessMode() const +{ + Q_D(const KConfig); + return d->configState; +} + +void KConfig::addConfigSources(const QStringList& files) +{ + Q_D(KConfig); + Q_FOREACH(const QString& file, files) { + d->extraFiles.push(file); + } + + if (!files.isEmpty()) { + reparseConfiguration(); + } +} + +QString KConfig::locale() const +{ + Q_D(const KConfig); + return d->locale; +} + +bool KConfigPrivate::setLocale(const QString& aLocale) +{ + if (aLocale != locale) { + locale = aLocale; + return true; + } + return false; +} + +bool KConfig::setLocale(const QString& locale) +{ + Q_D(KConfig); + if (d->setLocale(locale)) { + reparseConfiguration(); + return true; + } + return false; +} + +void KConfig::setReadDefaults(bool b) +{ + Q_D(KConfig); + d->bReadDefaults = b; +} + +bool KConfig::readDefaults() const +{ + Q_D(const KConfig); + return d->bReadDefaults; +} + +bool KConfig::isImmutable() const +{ + Q_D(const KConfig); + return d->bFileImmutable; +} + +bool KConfig::isGroupImmutableImpl(const QByteArray& aGroup) const +{ + Q_D(const KConfig); + return isImmutable() || d->entryMap.getEntryOption(aGroup, 0, 0, KEntryMap::EntryImmutable); +} + +#ifndef KDE_NO_DEPRECATED +void KConfig::setForceGlobal(bool b) +{ + Q_D(KConfig); + d->bForceGlobal = b; +} +#endif + +#ifndef KDE_NO_DEPRECATED +bool KConfig::forceGlobal() const +{ + Q_D(const KConfig); + return d->bForceGlobal; +} +#endif + +KConfigGroup KConfig::groupImpl(const QByteArray &group) +{ + return KConfigGroup(this, group.constData()); +} + +const KConfigGroup KConfig::groupImpl(const QByteArray &group) const +{ + return KConfigGroup(this, group.constData()); +} + +KEntryMap::EntryOptions convertToOptions(KConfig::WriteConfigFlags flags) +{ + KEntryMap::EntryOptions options=0; + + if (flags&KConfig::Persistent) + options |= KEntryMap::EntryDirty; + if (flags&KConfig::Global) + options |= KEntryMap::EntryGlobal; + if (flags&KConfig::Localized) + options |= KEntryMap::EntryLocalized; + return options; +} + +void KConfig::deleteGroupImpl(const QByteArray &aGroup, WriteConfigFlags flags) +{ + Q_D(KConfig); + KEntryMap::EntryOptions options = convertToOptions(flags)|KEntryMap::EntryDeleted; + + const QSet<QByteArray> groups = d->allSubGroups(aGroup); + Q_FOREACH (const QByteArray& group, groups) { + const QStringList keys = d->keyListImpl(group); + Q_FOREACH (const QString& _key, keys) { + const QByteArray &key = _key.toUtf8(); + if (d->canWriteEntry(group, key.constData())) { + d->entryMap.setEntry(group, key, QByteArray(), options); + d->bDirty = true; + } + } + } +} + +bool KConfig::isConfigWritable(bool warnUser) +{ + Q_D(KConfig); + bool allWritable = (d->mBackend ? d->mBackend->isWritable() : false); + + if (warnUser && !allWritable) { + QString errorMsg; + if (d->mBackend) // TODO how can be it be null? Set errorMsg appropriately + errorMsg = d->mBackend->nonWritableErrorMessage(); + + // Note: We don't ask the user if we should not ask this question again because we can't save the answer. + errorMsg += QCoreApplication::translate("KConfig", "Please contact your system administrator."); + QString cmdToExec = QStandardPaths::findExecutable(QString::fromLatin1("kdialog")); + if (!cmdToExec.isEmpty()) + { + QProcess::execute(cmdToExec, QStringList() + << QString::fromLatin1("--title") << QCoreApplication::applicationName() + << QString::fromLatin1("--msgbox") << errorMsg); + } + } + + d->configState = allWritable ? ReadWrite : ReadOnly; // update the read/write status + + return allWritable; +} + +bool KConfig::hasGroupImpl(const QByteArray& aGroup) const +{ + Q_D(const KConfig); + + // No need to look for the actual group entry anymore, or for subgroups: + // a group exists if it contains any non-deleted entry. + + return d->hasNonDeletedEntries(aGroup); +} + +bool KConfigPrivate::canWriteEntry(const QByteArray& group, const char* key, bool isDefault) const +{ + if (bFileImmutable || + entryMap.getEntryOption(group, key, KEntryMap::SearchLocalized, KEntryMap::EntryImmutable)) + return isDefault; + return true; +} + +void KConfigPrivate::putData( const QByteArray& group, const char* key, + const QByteArray& value, KConfigBase::WriteConfigFlags flags, bool expand) +{ + KEntryMap::EntryOptions options = convertToOptions(flags); + + if (bForceGlobal) + options |= KEntryMap::EntryGlobal; + if (expand) + options |= KEntryMap::EntryExpansion; + + if (value.isNull()) // deleting entry + options |= KEntryMap::EntryDeleted; + + bool dirtied = entryMap.setEntry(group, key, value, options); + if (dirtied && (flags & KConfigBase::Persistent)) + bDirty = true; +} + +void KConfigPrivate::revertEntry(const QByteArray& group, const char* key) +{ + bool dirtied = entryMap.revertEntry(group, key); + if (dirtied) + bDirty = true; +} + +QByteArray KConfigPrivate::lookupData(const QByteArray& group, const char* key, + KEntryMap::SearchFlags flags) const +{ + if (bReadDefaults) + flags |= KEntryMap::SearchDefaults; + const KEntryMapConstIterator it = entryMap.findEntry(group, key, flags); + if (it == entryMap.constEnd()) + return QByteArray(); + return it->mValue; +} + +QString KConfigPrivate::lookupData(const QByteArray& group, const char* key, + KEntryMap::SearchFlags flags, bool *expand) const +{ + if (bReadDefaults) + flags |= KEntryMap::SearchDefaults; + return entryMap.getEntry(group, key, QString(), flags, expand); +} + +QStandardPaths::StandardLocation KConfig::locationType() const +{ + Q_D(const KConfig); + return d->resourceType; +} + +void KConfig::virtual_hook(int /*id*/, void* /*data*/) +{ + /* nothing */ +} diff --git a/src/core/kconfig.h b/src/core/kconfig.h new file mode 100644 index 00000000..6dbf1d26 --- /dev/null +++ b/src/core/kconfig.h @@ -0,0 +1,397 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 2001 Waldo Bastian <bastian@kde.org> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIG_H +#define KCONFIG_H + +#include "kconfigbase.h" + +#include <kconfigcore_export.h> + +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <qstandardpaths.h> + +class KConfigGroup; +class KEntryMap; +class KConfigPrivate; + +/** + * \class KConfig kconfig.h <KConfig> + * + * \brief The central class of the KDE configuration data system. + * + * Quickstart: + * + * Get the default application config object via KSharedConfig::openConfig(). + * + * Load a specific configuration file: + * \code + * KConfig config( "/etc/kderc", KConfig::SimpleConfig ); + * \endcode + * + * Load the configuration of a specific component: + * \code + * KConfig config( "pluginrc" ); + * \endcode + * + * In general it is recommended to use KSharedConfig instead of + * creating multiple instances of KConfig to avoid the overhead of + * separate objects or concerns about synchronizing writes to disk + * even if the configuration object is updated from multiple code paths. + * KSharedConfig provides a set of open methods as counterparts for the + * KConfig constructors. + * + * \sa KSharedConfig, KConfigGroup, <a href="http://techbase.kde.org/index.php?title=Development/Tutorials/KConfig">the techbase HOWTO on KConfig</a>. + */ +class KCONFIGCORE_EXPORT KConfig : public KConfigBase +{ +public: + /** + * Determines how the system-wide and user's global settings will affect + * the reading of the configuration. + * + * If CascadeConfig is selected, system-wide configuration sources are used + * to provide defaults for the settings accessed through this object, or + * possibly to override those settings in certain cases. + * + * IncludeGlobals does the same, but with the global settings sources. + * + * Note that the main configuration source overrides the cascaded sources, + * which override those provided to addConfigSources(), which override the + * global sources. The exception is that if a key or group is marked as + * being immutable, it will not be overridden. + * + * Note that all values other than IncludeGlobals and CascadeConfig are + * convenience definitions for the basic mode. + * Do @em not combine them with anything. + */ + enum OpenFlag { + IncludeGlobals = 0x01, ///< Blend kdeglobals into the config object. + CascadeConfig = 0x02, ///< Cascade to system-wide config files. + + SimpleConfig = 0x00, ///< Just a single config file. + NoCascade = IncludeGlobals, ///< Include user's globals, but omit system settings. + NoGlobals = CascadeConfig, ///< Cascade to system settings, but omit user's globals. + FullConfig = IncludeGlobals|CascadeConfig ///< Fully-fledged config, including globals and cascading to system settings + }; + Q_DECLARE_FLAGS(OpenFlags, OpenFlag) + + /** + * Creates a KConfig object to manipulate a configuration file for the + * current application. + * + * If an absolute path is specified for @p file, that file will be used + * as the store for the configuration settings. If a non-absolute path + * is provided, the file will be looked for in the standard directory + * specified by type. If no path is provided, a default + * configuration file will be used based on the name of the main + * application component. + * + * @p mode determines whether the user or global settings will be allowed + * to influence the values returned by this object. See OpenFlags for + * more details. + * + * @note You probably want to use KSharedConfig::openConfig instead. + * + * @param file the name of the file. If an empty string is passed in + * and SimpleConfig is passed in for the OpenFlags, then an in-memory + * KConfig object is created which will not write out to file nor which + * requires any file in the filesystem at all. + * @param mode how global settings should affect the configuration + * options exposed by this KConfig object + * @param type The standard directory to look for the configuration + * file in + * + * @sa KSharedConfig::openConfig(const QString&, OpenFlags, const char*) + */ + explicit KConfig(const QString& file = QString(), OpenFlags mode = FullConfig, + QStandardPaths::StandardLocation type = QStandardPaths::GenericConfigLocation); + + /** + * @internal + * + * Creates a KConfig object using the specified backend. If the backend can not + * be found or loaded, then the standard configuration parser is used as a fallback. + * + * @param file the file to be parsed + * @param backend the backend to load + * @param type where to look for the file if an absolute path is not provided + * + * @since 4.1 + */ + KConfig(const QString& file, const QString& backend, QStandardPaths::StandardLocation type = QStandardPaths::GenericConfigLocation); + + virtual ~KConfig(); + + /** + * Returns the standard location enum passed to the constructor. + * Used by KSharedConfig. + * @since 5.0 + */ + QStandardPaths::StandardLocation locationType() const; + + /** + * Returns the filename used to store the configuration. + */ + QString name() const; + + /// @reimp + bool sync() Q_DECL_OVERRIDE; + + /// Returns true if sync has any changes to write out. + /// @since 4.12 + bool isDirty() const; + + /// @reimp + void markAsClean(); + + /// @{ configuration object state + /// @reimp + AccessMode accessMode() const; + + /** + * Whether the configuration can be written to. + * + * If @p warnUser is true and the configuration cannot be + * written to (ie: this method returns @c false), a warning + * message box will be shown to the user telling them to + * contact their system administrator to get the problem fixed. + * + * The most likely cause for this method returning @c false + * is that the user does not have write permission for the + * configuration file. + * + * @param warnUser whether to show a warning message to the user + * if the configuration cannot be written to + * + * @returns true if the configuration can be written to, false + * if the configuration cannot be written to + */ + bool isConfigWritable(bool warnUser); + /// @} + + /** + * Copies all entries from this config object to a new config + * object that will save itself to @p file. + * + * The configuration will not actually be saved to @p file + * until the returned object is destroyed, or sync() is called + * on it. + * + * Do not forget to delete the returned KConfig object if + * @p config was 0. + * + * @param file the new config object will save itself to + * @param config if not 0, copy to the given KConfig object rather + * than creating a new one + * + * @return @p config if it was set, otherwise a new KConfig object + */ + KConfig* copyTo(const QString &file, KConfig *config = 0) const; + + /** + * Ensures that the configuration file contains a certain update. + * + * If the configuration file does not contain the update @p id + * as contained in @p updateFile, kconf_update is run to update + * the configuration file. + * + * If you install config update files with critical fixes + * you may wish to use this method to verify that a critical + * update has indeed been performed to catch the case where + * a user restores an old config file from backup that has + * not been updated yet. + * + * @param id the update to check + * @param updateFile the file containing the update + */ + void checkUpdate(const QString &id, const QString &updateFile); + + /** + * Updates the state of this object to match the persistent storage. + */ + void reparseConfiguration(); + + /// @{ extra config files + /** + * Adds the list of configuration sources to the merge stack. + * + * Currently only files are accepted as configuration sources. + * + * The first entry in @p sources is treated as the most general and will + * be overridden by the second entry. The settings in the final entry + * in @p sources will override all the other sources provided in the list. + * + * The settings in @p sources will also be overridden by the sources + * provided by any previous calls to addConfigSources(). + * + * The settings in the global configuration sources will be overridden by + * the sources provided to this method (@see IncludeGlobals). + * All the sources provided to any call to this method will be overridden + * by any files that cascade from the source provided to the constructor + * (@see CascadeConfig), which will in turn be + * overridden by the source provided to the constructor. + * + * Note that only the most specific file, ie: the file provided to the + * constructor, will be written to by this object. + * + * The state is automatically updated by this method, so there is no need to call + * reparseConfiguration(). + * + * @param sources A list of extra config sources. + */ + void addConfigSources(const QStringList &sources); + + /// @} + /// @{ locales + /** + * Returns the current locale. + */ + QString locale() const; + /** + * Sets the locale to @p aLocale. + * + * The global locale is used by default. + * + * @note If set to the empty string, @b no locale will be matched. This effectively disables + * reading translated entries. + * + * @return @c true if locale was changed, @c false if the call had no + * effect (eg: @p aLocale was already the current locale for this + * object) + */ + bool setLocale(const QString& aLocale); + /// @} + + /// @{ defaults + /** + * When set, all readEntry calls return the system-wide (default) values + * instead of the user's settings. + * + * This is off by default. + * + * @param b whether to read the system-wide defaults instead of the + * user's settings + */ + void setReadDefaults(bool b); + /** + * @returns @c true if the system-wide defaults will be read instead of the + * user's settings + */ + bool readDefaults() const; + /// @} + + /// @{ immutability + /// @reimp + bool isImmutable() const; + /// @} + + /// @{ global + /** + * @deprecated + * + * Forces all following write-operations to be performed on @c kdeglobals, + * independent of the @c Global flag in writeEntry(). + * + * @param force true to force writing to kdeglobals + * @see forceGlobal + */ +#ifndef KDE_NO_DEPRECATED + KCONFIGCORE_DEPRECATED void setForceGlobal(bool force); +#endif + /** + * @deprecated + * + * Returns whether all entries are being written to @c kdeglobals. + * + * @return @c true if all entries are being written to @c kdeglobals + * @see setForceGlobal + * @deprecated + */ +#ifndef KDE_NO_DEPRECATED + KCONFIGCORE_DEPRECATED bool forceGlobal() const; +#endif + /// @} + + /// @reimp + QStringList groupList() const; + + /** + * Returns a map (tree) of entries in a particular group. + * + * The entries are all returned as strings. + * + * @param aGroup The group to get entries from. + * + * @return A map of entries in the group specified, indexed by key. + * The returned map may be empty if the group is empty, or not found. + * @see QMap + */ + QMap<QString, QString> entryMap(const QString &aGroup=QString()) const; + + /** + * Sets the name of the application config file. + * @since 5.0 + */ + static void setMainConfigName(const QString& str); + +protected: + virtual bool hasGroupImpl(const QByteArray &group) const; + virtual KConfigGroup groupImpl( const QByteArray &b); + virtual const KConfigGroup groupImpl(const QByteArray &b) const; + virtual void deleteGroupImpl(const QByteArray &group, WriteConfigFlags flags = Normal); + virtual bool isGroupImmutableImpl(const QByteArray& aGroup) const; + + friend class KConfigGroup; + friend class KConfigGroupPrivate; + friend class KSharedConfig; + + /** Virtual hook, used to add new "virtual" functions while maintaining + * binary compatibility. Unused in this class. + */ + virtual void virtual_hook( int id, void* data ); + + KConfigPrivate *const d_ptr; + + KConfig(KConfigPrivate &d); + +private: + friend class KConfigTest; + + QStringList keyList(const QString& aGroup=QString()) const; + + /** + * @internal for KSharedConfig. Could be made public if needed, though. + */ + static QString mainConfigName(); + + Q_DISABLE_COPY(KConfig) + + Q_DECLARE_PRIVATE(KConfig) +}; +Q_DECLARE_OPERATORS_FOR_FLAGS( KConfig::OpenFlags ) + +#endif // KCONFIG_H diff --git a/src/core/kconfig_p.h b/src/core/kconfig_p.h new file mode 100644 index 00000000..e6fb1ca0 --- /dev/null +++ b/src/core/kconfig_p.h @@ -0,0 +1,110 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 2001 Waldo Bastian <bastian@kde.org> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIG_P_H +#define KCONFIG_P_H + +#include "kconfigdata.h" +#include "kconfigbackend.h" +#include "kconfiggroup.h" + +#include <QtCore/QStringList> +#include <QtCore/QStack> +#include <QtCore/QFile> +#include <QtCore/QDir> + +class KConfigPrivate +{ + friend class KConfig; +public: + KConfig::OpenFlags openFlags; + QStandardPaths::StandardLocation resourceType; + + void changeFileName(const QString& fileName); + + // functions for KConfigGroup + bool canWriteEntry(const QByteArray& group, const char* key, bool isDefault=false) const; + QString lookupData(const QByteArray& group, const char* key, KEntryMap::SearchFlags flags, + bool* expand) const; + QByteArray lookupData(const QByteArray& group, const char* key, KEntryMap::SearchFlags flags) const; + + void putData(const QByteArray& group, const char* key, const QByteArray& value, + KConfigBase::WriteConfigFlags flags, bool expand=false); + void revertEntry(const QByteArray& group, const char* key); + QStringList groupList(const QByteArray& group) const; + // copies the entries from @p source to @p otherGroup changing all occurrences + // of @p source with @p destination + void copyGroup(const QByteArray& source, const QByteArray& destination, + KConfigGroup *otherGroup, KConfigBase::WriteConfigFlags flags) const; + QStringList keyListImpl(const QByteArray& theGroup) const; + QSet<QByteArray> allSubGroups(const QByteArray& parentGroup) const; + bool hasNonDeletedEntries(const QByteArray& group) const; + + static QString expandString(const QString& value); + +protected: + QExplicitlySharedDataPointer<KConfigBackend> mBackend; + + KConfigPrivate(KConfig::OpenFlags flags, + QStandardPaths::StandardLocation type); + + virtual ~KConfigPrivate() + { + } + + bool bDynamicBackend:1; // do we own the backend? +private: + bool bDirty:1; + bool bLocaleInitialized:1; + bool bReadDefaults:1; + bool bFileImmutable:1; + bool bForceGlobal:1; + bool bSuppressGlobal:1; + + QString sGlobalFileName; + static bool mappingsRegistered; + + + KEntryMap entryMap; + QString backendType; + QStack<QString> extraFiles; + + QString locale; + QString fileName; + QString etc_kderc; + KConfigBase::AccessMode configState; + + bool wantGlobals() const { return openFlags&KConfig::IncludeGlobals && !bSuppressGlobal; } + bool wantDefaults() const { return openFlags&KConfig::CascadeConfig; } + bool isSimple() const { return openFlags == KConfig::SimpleConfig; } + bool isReadOnly() const { return configState == KConfig::ReadOnly; } + + bool setLocale(const QString& aLocale); + QStringList getGlobalFiles() const; + void parseGlobalFiles(); + void parseConfigFiles(); + void initCustomized(KConfig*); + bool lockLocal(); +}; + +#endif // KCONFIG_P_H diff --git a/src/core/kconfigbackend.cpp b/src/core/kconfigbackend.cpp new file mode 100644 index 00000000..eb92a964 --- /dev/null +++ b/src/core/kconfigbackend.cpp @@ -0,0 +1,123 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006 Thomas Braxton <brax108@cox.net> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kconfigbackend.h" + +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QHash> +#include <QtCore/QDebug> + +#include "kconfig.h" +#include "kconfigini_p.h" +#include "kconfigdata.h" + +typedef QExplicitlySharedDataPointer<KConfigBackend> BackendPtr; + +class KConfigBackend::Private +{ +public: + qint64 size; + QDateTime lastModified; + QString localFileName; + + static QString whatSystem(const QString& /*fileName*/) + { + return QLatin1String("INI"); + } +}; + + +void KConfigBackend::registerMappings(const KEntryMap& /*entryMap*/) +{ +} + +BackendPtr KConfigBackend::create(const QString& file, const QString& sys) +{ + //qDebug() << "creating a backend for file" << file << "with system" << sys; + const QString system = (sys.isEmpty() ? Private::whatSystem(file) : sys); + KConfigBackend* backend = 0; + +#if 0 // TODO port to Qt5 plugin loading + if (system.compare(QLatin1String("INI"), Qt::CaseInsensitive) != 0) { + const QString constraint = QString::fromLatin1("[X-KDE-PluginInfo-Name] ~~ '%1'").arg(system); + KService::List offers = KServiceTypeTrader::self()->query(QLatin1String("KConfigBackend"), constraint); + + //qDebug() << "found" << offers.count() << "offers for KConfigBackend plugins with name" << system; + foreach (const KService::Ptr& offer, offers) { + backend = offer->createInstance<KConfigBackend>(0); + if (backend) { + //qDebug() << "successfully created a backend for" << system; + backend->setFilePath(file); + return BackendPtr(backend); + } + } // foreach offers + } +#endif + + //qDebug() << "default creation of the Ini backend"; + backend = new KConfigIniBackend; + backend->setFilePath(file); + return BackendPtr(backend); +} + +KConfigBackend::KConfigBackend() + : d(new Private) +{ +} + +KConfigBackend::~KConfigBackend() +{ + delete d; +} + +QDateTime KConfigBackend::lastModified() const +{ + return d->lastModified; +} + +void KConfigBackend::setLastModified(const QDateTime& dt) +{ + d->lastModified = dt; +} + +qint64 KConfigBackend::size() const +{ + return d->size; +} + +void KConfigBackend::setSize(qint64 sz) +{ + d->size = sz; +} + +QString KConfigBackend::filePath() const +{ + return d->localFileName; +} + +void KConfigBackend::setLocalFilePath(const QString& file) +{ + d->localFileName = file; +} diff --git a/src/core/kconfigbackend.desktop b/src/core/kconfigbackend.desktop new file mode 100644 index 00000000..bebcbff6 --- /dev/null +++ b/src/core/kconfigbackend.desktop @@ -0,0 +1,86 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KConfigBackend + +Comment=Storage backend for KConfig +Comment[ar]=خلفية الحفظ لـ KConfig +Comment[as]=KConfig ৰ বাবে ভঁৰালৰ বেকএন্ড +Comment[ast]=Infraestructura d'atroxamientu pa KConfig +Comment[be@latin]=Słužba schovišča dla systemy „KConfig” +Comment[bg]=Storage backend for KConfig +Comment[bn]=KConfig-এর স্টোরেজ ব্যাকেন্ড +Comment[bn_IN]=KConfig-র জন্য ব্যবহৃত ব্যাক-এন্ড সংগ্রহস্থল +Comment[bs]=Skladišna pozadina za K‑konfig +Comment[ca]=Dorsal d'emmagatzematge per al KConfig +Comment[ca@valencia]=Dorsal d'emmagatzematge per al KConfig +Comment[cs]=Úložiště pro KConfig +Comment[csb]=Zôpisownô zbiérnica dlô KConfig +Comment[da]=Lagringsmotor til KConfig +Comment[de]=Speicher-Unterstützung für KConfig +Comment[el]=Σύστημα υποστήριξης αποθήκευσης για το KConfig +Comment[en_GB]=Storage backend for KConfig +Comment[es]=Motor de almacenamiento para KConfig +Comment[et]=KConfigi salvestamise taustaprogramm +Comment[eu]=KConfig-en biltegiratze euskarria +Comment[fi]=Asetusvaraston taustaosa +Comment[fr]=Module de stockage pour KConfig +Comment[fy]=Opslach efterein foar KConfig +Comment[ga]=Inneall stórais KConfig +Comment[gl]=Infraestrutura de almacenaxe para KConfig +Comment[gu]=KConfig માટે સંગ્રહ પાશ્ર્વભાગ +Comment[he]=מנוע שמירה עבור KConfig +Comment[hne]=के-कानफिग बर भंडार बैकएण्ड +Comment[hr]=Skladišna pozadina za KConfig +Comment[hsb]=składowanski backend za KConfig +Comment[hu]=Tároló a KConfighoz +Comment[hy]=Բահեստի հետևի մասը KConfig-ի համար +Comment[ia]=Retro-administration de immagazinage (storage) pro Kconfig +Comment[id]=Backend penyimpanan untuk KConfig +Comment[is]=Geymslubakendi fyrir KConfig +Comment[it]=Backend di archiviazione per KConfig +Comment[ja]=KConfig のストレージバックエンド +Comment[kk]=KConfig үшін сақтау тетігі +Comment[km]=កម្មវិធីខាងក្រោយការផ្ទុកសម្រាប់ KConfig +Comment[kn]=ಕೆಕಾನ್ಫಿಗ್ ಗೆ ಸಂಗ್ರಹಣಾ ಹಿಂಬದಿ (ಬಾಕೆಂಡ್) +Comment[ko]=KConfig의 저장소 백엔드 +Comment[ku]=Embara ser-dawî ji bo KVeavakirin +Comment[lt]=KConfig saugojimo sąsaja +Comment[lv]=KConfig glabāšanas aizmugure +Comment[mai]=KConfig क' लेल भंडारक बैकेंड +Comment[ml]=കെകോണ്ഫിഗിനുളള സ്റ്റോറേജ് ബാക്കെന്ഡ് +Comment[mr]=के-कॉन्फिग करिता संचयन बॅकएन्ड +Comment[ms]=Storan belakang untuk KConfig +Comment[nb]=Bakgrunnslagring for KConfig +Comment[nds]=Spieker-Hülpprogramm för KConfig +Comment[nl]=Opslagbackend voor KConfig +Comment[nn]=Lagringsmotor for KConfig +Comment[or]=KConfig ପାଇଁ ଭଣ୍ଡାର ପଛ ପାଖ +Comment[pa]=KConfig ਲਈ ਸਟੋਰੇਜ਼ ਬੈਕਐਂਡ +Comment[pl]=Przechowywanie danych dla KConfig +Comment[pt]=Armazenamento da infra-estrutura do KConfig +Comment[pt_BR]=Infraestrutura de armazenamento para o KConfig +Comment[ro]=Suport de stocare pentru KConfig +Comment[ru]=Модуль хранения параметров KConfig +Comment[se]=KConfig vurkenduogáš +Comment[si]=KConfig සඳහා ගබඩා පසුඈදුම +Comment[sk]=Úložisko pre KConfig +Comment[sl]=Zaledje za shranjevanje v KConfig +Comment[sq]=Mbështetëse Ruajtëse për KConfig +Comment[sr]=Складишна позадина за К‑конфиг +Comment[sr@ijekavian]=Складишна позадина за К‑конфиг +Comment[sr@ijekavianlatin]=Skladišna pozadina za KConfig +Comment[sr@latin]=Skladišna pozadina za KConfig +Comment[sv]=Lagringsgränssnitt för KConfig +Comment[ta]=கேவடிவமைப்பிற்கான பின்னணி சேமிப்பகம் +Comment[tg]=Пуштибонии захирагоҳ барои KConfig +Comment[th]=โปรแกรมเบื้องหลังพื้นที่จัดเก็บข้อมูลสำหรับ KConfig +Comment[tr]=KConfig için Depolama Arka Ucu +Comment[tt]=KConfig өчен саклау бэкенды +Comment[ug]=KConfig نىڭ ساقلاش ئارقا ئۇچى +Comment[uk]=Засіб збереження для KConfig +Comment[vi]=Hậu trường lưu trữ cho KConfig +Comment[wa]=Bouye di fond di stocaedje po KConfig +Comment[x-test]=xxStorage backend for KConfigxx +Comment[zh_CN]=KConfig 存储后端 +Comment[zh_TW]=KConfig 儲存後端介面 + diff --git a/src/core/kconfigbackend.h b/src/core/kconfigbackend.h new file mode 100644 index 00000000..49239e66 --- /dev/null +++ b/src/core/kconfigbackend.h @@ -0,0 +1,211 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Portions copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGBACKEND_H +#define KCONFIGBACKEND_H + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QExplicitlySharedDataPointer> + +#include <kconfigcore_export.h> +#include <kconfigbase.h> + +class KEntryMap; +class QFile; +class QByteArray; +class QDateTime; + +/** + * \class KConfigBackend kconfigbackend.h <KConfigBackend> + * + * Provides the implementation for accessing configuration sources. + * + * KDELibs only provides an INI backend, but this class can be used + * to create plugins that allow access to other file formats and + * configuration systems. + */ +class KCONFIGCORE_EXPORT KConfigBackend : public QObject, public QSharedData +{ + Q_OBJECT + Q_FLAGS(ParseOption) + Q_FLAGS(WriteOption) + +public: + /** + * Creates a new KConfig backend. + * + * If no @p system is given, or the given @p system is unknown, this method tries + * to determine the correct backend to use. + * + * @param fileName the absolute file name of the configuration file + * @param system the configuration system to use + * @return a KConfigBackend object to be used with KConfig + */ + static QExplicitlySharedDataPointer<KConfigBackend> create(const QString& fileName = QString(), + const QString& system = QString()); + + /** + * Registers mappings from directories/files to configuration systems + * + * Allows you to tell KConfigBackend that create() should use a particular + * backend for a particular file or directory. + * + * @warning currently does nothing + * + * @param entryMap the KEntryMap to build the mappings from + */ + static void registerMappings(const KEntryMap& entryMap); + + /** Destroys the backend */ + virtual ~KConfigBackend(); + + /** Allows the behaviour of parseConfig() to be tuned */ + enum ParseOption { + ParseGlobal = 1, /// entries should be marked as @em global + ParseDefaults = 2, /// entries should be marked as @em default + ParseExpansions = 4 /// entries are allowed to be marked as @em expandable + }; + /// @typedef typedef QFlags<ParseOption> ParseOptions + Q_DECLARE_FLAGS(ParseOptions, ParseOption) + + /** Allows the behaviour of writeConfig() to be tuned */ + enum WriteOption { + WriteGlobal = 1 /// only write entries marked as "global" + }; + /// @typedef typedef QFlags<WriteOption> WriteOptions + Q_DECLARE_FLAGS(WriteOptions, WriteOption) + + /** Return value from parseConfig() */ + enum ParseInfo { + ParseOk, /// the configuration was opened read/write + ParseImmutable, /// the configuration is @em immutable + ParseOpenError /// the configuration could not be opened + }; + + /** + * Read persistent storage + * + * @param locale the locale to read entries for (if the backend supports localized entries) + * @param pWriteBackMap the KEntryMap where the entries are placed + * @param options @see ParseOptions + * @return @see ParseInfo + */ + virtual ParseInfo parseConfig(const QByteArray& locale, + KEntryMap& pWriteBackMap, + ParseOptions options = ParseOptions()) = 0; + + /** + * Write the @em dirty entries to permanent storage + * + * @param locale the locale to write entries for (if the backend supports localized entries) + * @param entryMap the KEntryMap containing the config object's entries. + * @param options @see WriteOptions + * + * @return @c true if the write was successful, @c false if writing the configuration failed + */ + virtual bool writeConfig(const QByteArray& locale, KEntryMap& entryMap, + WriteOptions options) = 0; + + /** + * If isWritable() returns false, writeConfig() will always fail. + * + * @return @c true if the configuration is writable, @c false if it is immutable + */ + virtual bool isWritable() const = 0; + /** + * When isWritable() returns @c false, return an error message to + * explain to the user why saving configuration will not work. + * + * The return value when isWritable() returns @c true is undefined. + * + * @returns a translated user-visible explanation for the configuration + * object not being writable + */ + virtual QString nonWritableErrorMessage() const = 0; + /** + * @return the read/write status of the configuration object + * + * @see KConfigBase::AccessMode + */ + virtual KConfigBase::AccessMode accessMode() const = 0; + /** + * Create the enclosing object of the configuration object + * + * For example, if the configuration object is a file, this should create + * the parent directory. + */ + virtual void createEnclosing() = 0; + + /** + * Set the file path. + * + * @note @p path @b MUST be @em absolute. + * + * @param path the absolute file path + */ + virtual void setFilePath(const QString& path) = 0; + + /** + * Lock the file + */ + virtual bool lock() = 0; + /** + * Release the lock on the file + */ + virtual void unlock() = 0; + /** + * @return @c true if the file is locked, @c false if it is not locked + */ + virtual bool isLocked() const = 0; + + /** + * @return the date and time when the object was last modified + */ + QDateTime lastModified() const; + /** @return the absolute path to the object */ + QString filePath() const; + /** @return the size of the object */ + qint64 size() const; + +protected: + KConfigBackend(); + void setLastModified(const QDateTime& dt); + void setSize(qint64 sz); + void setLocalFilePath(const QString& file); + +private: + class Private; + Private *const d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigBackend::ParseOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigBackend::WriteOptions) + +/** + * Register a KConfig backend when it is contained in a loadable module + */ +#define K_EXPORT_KCONFIGBACKEND(libname, classname) \ +K_PLUGIN_FACTORY(factory, registerPlugin<classname>();) + + +#endif // KCONFIGBACKEND_H diff --git a/src/core/kconfigbase.cpp b/src/core/kconfigbase.cpp new file mode 100644 index 00000000..4be7ac23 --- /dev/null +++ b/src/core/kconfigbase.cpp @@ -0,0 +1,112 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kconfigbase.h" + +#include "kconfiggroup.h" + +#include <QtCore/QString> + +bool KConfigBase::hasGroup(const QString &group) const +{ + return hasGroupImpl(group.toUtf8()); +} + +bool KConfigBase::hasGroup(const char *group) const +{ + return hasGroupImpl(QByteArray(group)); +} + +bool KConfigBase::hasGroup(const QByteArray &group) const +{ + return hasGroupImpl(group); +} + +KConfigGroup KConfigBase::group( const QByteArray &b) +{ + return groupImpl(b); +} + +KConfigGroup KConfigBase::group( const QString &str) +{ + return groupImpl(str.toUtf8()); +} + +KConfigGroup KConfigBase::group( const char *str) +{ + return groupImpl(QByteArray(str)); +} + +const KConfigGroup KConfigBase::group( const QByteArray &b ) const +{ + return groupImpl(b); +} + +const KConfigGroup KConfigBase::group( const QString &s ) const +{ + return groupImpl(s.toUtf8()); +} + +const KConfigGroup KConfigBase::group( const char *s ) const +{ + return groupImpl(QByteArray(s)); +} + +void KConfigBase::deleteGroup(const QByteArray &group, WriteConfigFlags flags) +{ + deleteGroupImpl(group, flags); +} + +void KConfigBase::deleteGroup(const QString &group, WriteConfigFlags flags) +{ + deleteGroupImpl(group.toUtf8(), flags); +} + +void KConfigBase::deleteGroup(const char *group, WriteConfigFlags flags) +{ + deleteGroupImpl(QByteArray(group), flags); +} + +bool KConfigBase::isGroupImmutable(const QByteArray& aGroup) const +{ + return isGroupImmutableImpl(aGroup); +} + +bool KConfigBase::isGroupImmutable(const QString& aGroup) const +{ + return isGroupImmutableImpl(aGroup.toUtf8()); +} + + +bool KConfigBase::isGroupImmutable(const char *aGroup) const +{ + return isGroupImmutableImpl(QByteArray(aGroup)); +} + +KConfigBase::~KConfigBase() +{} + +KConfigBase::KConfigBase() +{} + +void KConfigBase::virtual_hook(int , void *) +{} diff --git a/src/core/kconfigbase.h b/src/core/kconfigbase.h new file mode 100644 index 00000000..782ff4b6 --- /dev/null +++ b/src/core/kconfigbase.h @@ -0,0 +1,184 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 2001 Waldo Bastian <bastian@kde.org> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGBASE_H +#define KCONFIGBASE_H + +#include <kconfigcore_export.h> + +#include <QtCore/QtGlobal> + +class QStringList; +class KConfigGroup; +class KConfigBasePrivate; + +/** + * \class KConfigBase kconfigbase.h <KConfigBase> + */ +class KCONFIGCORE_EXPORT KConfigBase +{ +public: + /** + * Flags to control write entry + */ + enum WriteConfigFlag + { + Persistent = 0x01, + /**< + * Save this entry when saving the config object. + */ + Global = 0x02, + /**< + * Save the entry to the global %KDE config file instead of the + * application specific config file. + */ + Localized = 0x04, + /**< + * Add the locale tag to the key when writing it. + */ + Normal=Persistent + /**< + * Save the entry to the application specific config file without + * a locale tag. This is the default. + */ + + }; + Q_DECLARE_FLAGS(WriteConfigFlags, WriteConfigFlag) + + /** + * Destructs the KConfigBase object. + */ + virtual ~KConfigBase(); + + /** + * Returns a list of groups that are known about. + * + * @return The list of groups. + **/ + virtual QStringList groupList() const = 0; + + /** + * Returns true if the specified group is known about. + * + * @param group The group to search for. + * @return true if the group exists. + */ + bool hasGroup(const QString &group) const; + bool hasGroup(const char *group) const; + bool hasGroup(const QByteArray &group) const; + + /** + * Returns an object for the named subgroup. + * + * @param group the group to open. Pass a null string on to the KConfig + * object to obtain a handle on the root group. + * @return The list of groups. + **/ + KConfigGroup group(const QByteArray &group); + KConfigGroup group(const QString &group); + KConfigGroup group(const char *group); + + /** + * @overload + **/ + const KConfigGroup group(const QByteArray &group) const; + const KConfigGroup group(const QString &group) const; + const KConfigGroup group(const char *group) const; + + /** + * Delete @p aGroup. This marks @p aGroup as @em deleted in the config object. This effectively + * removes any cascaded values from config files earlier in the stack. + */ + void deleteGroup(const QByteArray &group, WriteConfigFlags flags = Normal); + void deleteGroup(const QString &group, WriteConfigFlags flags = Normal); + void deleteGroup(const char *group, WriteConfigFlags flags = Normal); + + /** + * Syncs the configuration object that this group belongs to. + * Unrelated concurrent changes to the same file are merged and thus + * not overwritten. Note however, that this object is @em not automatically + * updated with those changes. + */ + virtual bool sync() = 0; + + /** + * Reset the dirty flags of all entries in the entry map, so the + * values will not be written to disk on a later call to sync(). + */ + virtual void markAsClean() = 0; + + /** + * Possible return values for accessMode(). + */ + enum AccessMode { NoAccess, ReadOnly, ReadWrite }; + + /** + * Returns the access mode of the app-config object. + * + * Possible return values + * are NoAccess (the application-specific config file could not be + * opened neither read-write nor read-only), ReadOnly (the + * application-specific config file is opened read-only, but not + * read-write) and ReadWrite (the application-specific config + * file is opened read-write). + * + * @return the access mode of the app-config object + */ + virtual AccessMode accessMode() const = 0; + + /** + * Checks whether this configuration object can be modified. + * @return whether changes may be made to this configuration object. + */ + virtual bool isImmutable() const = 0; + + /** + * Can changes be made to the entries in @p aGroup? + * + * @param aGroup The group to check for immutability. + * @return @c false if the entries in @p aGroup can be modified. + */ + bool isGroupImmutable(const QByteArray& aGroup) const; + bool isGroupImmutable(const QString& aGroup) const; + bool isGroupImmutable(const char *aGroup) const; + +protected: + KConfigBase(); + + virtual bool hasGroupImpl(const QByteArray &group) const = 0; + virtual KConfigGroup groupImpl( const QByteArray &b) = 0; + virtual const KConfigGroup groupImpl(const QByteArray &b) const = 0; + virtual void deleteGroupImpl(const QByteArray &group, WriteConfigFlags flags = Normal) = 0; + virtual bool isGroupImmutableImpl(const QByteArray& aGroup) const = 0; + + /** Virtual hook, used to add new "virtual" functions while maintaining + * binary compatibility. Unused in this class. + */ + virtual void virtual_hook( int id, void* data ); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigBase::WriteConfigFlags) + + + +#endif // KCONFIG_H diff --git a/src/core/kconfigbase_p.h b/src/core/kconfigbase_p.h new file mode 100644 index 00000000..c1066267 --- /dev/null +++ b/src/core/kconfigbase_p.h @@ -0,0 +1,30 @@ +/* + This file is part of the KDE libraries + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (C) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGBASE_P_H +#define KCONFIGBASE_P_H + +#include <QtCore/QSharedData> + +class KConfigBasePrivate : public QSharedData +{ +}; +#endif diff --git a/src/core/kconfigdata.cpp b/src/core/kconfigdata.cpp new file mode 100644 index 00000000..74a068be --- /dev/null +++ b/src/core/kconfigdata.cpp @@ -0,0 +1,317 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999-2000 Preston Brown <pbrown@kde.org> + Copyright (C) 1996-2000 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kconfigdata.h> + +QDebug operator<<(QDebug dbg, const KEntryKey& key) +{ + dbg.nospace() << "[" << key.mGroup << ", " << key.mKey << (key.bLocal?" localized":"") << + (key.bDefault?" default":"") << (key.bRaw?" raw":"") << "]"; + return dbg.space(); +} + +QDebug operator<<(QDebug dbg, const KEntry& entry) +{ + dbg.nospace() << "[" << entry.mValue << (entry.bDirty?" dirty":"") << + (entry.bGlobal?" global":"") << (entry.bImmutable?" immutable":"") << + (entry.bDeleted?" deleted":"") << (entry.bReverted?" reverted":"") << + (entry.bExpand?" expand":"") << "]"; + + return dbg.space(); +} + +QMap< KEntryKey, KEntry >::Iterator KEntryMap::findExactEntry(const QByteArray& group, const QByteArray& key, KEntryMap::SearchFlags flags) +{ + KEntryKey theKey(group, key, bool(flags&SearchLocalized), bool(flags&SearchDefaults)); + return find(theKey); +} + +QMap< KEntryKey, KEntry >::Iterator KEntryMap::findEntry(const QByteArray& group, const QByteArray& key, KEntryMap::SearchFlags flags) +{ + KEntryKey theKey(group, key, false, bool(flags&SearchDefaults)); + + // try the localized key first + if (flags&SearchLocalized) { + theKey.bLocal = true; + + Iterator it = find(theKey); + if (it != end()) + return it; + + theKey.bLocal = false; + } + return find(theKey); +} + +QMap< KEntryKey, KEntry >::ConstIterator KEntryMap::findEntry(const QByteArray& group, const QByteArray& key, KEntryMap::SearchFlags flags) const +{ + KEntryKey theKey(group, key, false, bool(flags&SearchDefaults)); + + // try the localized key first + if (flags&SearchLocalized) { + theKey.bLocal = true; + + ConstIterator it = find(theKey); + if (it != constEnd()) + return it; + + theKey.bLocal = false; + } + return find(theKey); +} + +bool KEntryMap::setEntry(const QByteArray& group, const QByteArray& key, const QByteArray& value, KEntryMap::EntryOptions options) +{ + KEntryKey k; + KEntry e; + bool newKey = false; + + const Iterator it = findExactEntry(group, key, SearchFlags(options>>16)); + + if (key.isEmpty()) { // inserting a group marker + k.mGroup = group; + e.bImmutable = (options&EntryImmutable); + if (options&EntryDeleted) { + qWarning("Internal KConfig error: cannot mark groups as deleted"); + } + if(it == end()) { + insert(k, e); + return true; + } else if(it.value() == e) { + return false; + } + + it.value() = e; + return true; + } + + + if (it != end()) { + if (it->bImmutable) + return false; // we cannot change this entry. Inherits group immutability. + k = it.key(); + e = *it; + //qDebug() << "found existing entry for key" << k; + } else { + // make sure the group marker is in the map + KEntryMap const *that = this; + ConstIterator cit = that->findEntry(group); + if (cit == constEnd()) + insert(KEntryKey(group), KEntry()); + else if (cit->bImmutable) + return false; // this group is immutable, so we cannot change this entry. + + k = KEntryKey(group, key); + newKey = true; + } + + // set these here, since we may be changing the type of key from the one we found + k.bLocal = (options&EntryLocalized); + k.bDefault = (options&EntryDefault); + k.bRaw = (options&EntryRawKey); + + e.mValue = value; + e.bDirty = e.bDirty || (options&EntryDirty); + e.bGlobal = (options&EntryGlobal); //we can't use || here, because changes to entries in + //kdeglobals would be written to kdeglobals instead + //of the local config file, regardless of the globals flag + e.bImmutable = e.bImmutable || (options&EntryImmutable); + if (value.isNull()) + e.bDeleted = e.bDeleted || (options&EntryDeleted); + else + e.bDeleted = false; // setting a value to a previously deleted entry + e.bExpand = (options&EntryExpansion); + e.bReverted = false; + + if(newKey) + { + //qDebug() << "inserting" << k << "=" << value; + insert(k, e); + if(k.bDefault) + { + k.bDefault = false; + //qDebug() << "also inserting" << k << "=" << value; + insert(k, e); + } + // TODO check for presence of unlocalized key + return true; + } else { +// KEntry e2 = it.value(); + if(it.value() != e) + { + //qDebug() << "changing" << k << "from" << e.mValue << "to" << value; + it.value() = e; + if(k.bDefault) + { + KEntryKey nonDefaultKey(k); + nonDefaultKey.bDefault = false; + insert(nonDefaultKey, e); + } + if (!(options & EntryLocalized)) { + KEntryKey theKey(group, key, true, false); + //qDebug() << "non-localized entry, remove localized one:" << theKey; + remove(theKey); + if (k.bDefault) { + theKey.bDefault = true; + remove(theKey); + } + } + return true; + } else { + //qDebug() << k << "was already set to" << e.mValue; + if (!(options & EntryLocalized)) { + //qDebug() << "unchanged non-localized entry, remove localized one."; + KEntryKey theKey(group, key, true, false); + bool ret = false; + Iterator cit = find(theKey); + if (cit != end()) { + erase(cit); + ret = true; + } + if (k.bDefault) { + theKey.bDefault = true; + Iterator cit = find(theKey); + if (cit != end()) { + erase(cit); + return true; + } + } + return ret; + } + //qDebug() << "localized entry, unchanged, return false"; + // When we are writing a default, we know that the non- + // default is the same as the default, so we can simply + // use the same branch. + return false; + } + } +} + +QString KEntryMap::getEntry(const QByteArray& group, const QByteArray& key, const QString& defaultValue, KEntryMap::SearchFlags flags, bool* expand) const +{ + const ConstIterator it = findEntry(group, key, flags); + QString theValue = defaultValue; + + if (it != constEnd() && !it->bDeleted) { + if (!it->mValue.isNull()) { + const QByteArray data=it->mValue; + theValue = QString::fromUtf8(data.constData(), data.length()); + if (expand) + *expand = it->bExpand; + } + } + + return theValue; +} + +bool KEntryMap::hasEntry(const QByteArray& group, const QByteArray& key, KEntryMap::SearchFlags flags) const +{ + const ConstIterator it = findEntry(group, key, flags); + if (it == constEnd()) + return false; + if (it->bDeleted) + return false; + if (key.isNull()) { // looking for group marker + return it->mValue.isNull(); + } + // if it->bReverted, we'll just return true; the real answer depends on lookup up with SearchDefaults, though. + return true; +} + +bool KEntryMap::getEntryOption(const QMap< KEntryKey, KEntry >::ConstIterator& it, KEntryMap::EntryOption option) const +{ + if (it != constEnd()) { + switch (option) { + case EntryDirty: + return it->bDirty; + case EntryLocalized: + return it.key().bLocal; + case EntryGlobal: + return it->bGlobal; + case EntryImmutable: + return it->bImmutable; + case EntryDeleted: + return it->bDeleted; + case EntryExpansion: + return it->bExpand; + default: + break; // fall through + } + } + + return false; +} + +void KEntryMap::setEntryOption(QMap< KEntryKey, KEntry >::Iterator it, KEntryMap::EntryOption option, bool bf) +{ + if (it != end()) { + switch (option) { + case EntryDirty: + it->bDirty = bf; + break; + case EntryGlobal: + it->bGlobal = bf; + break; + case EntryImmutable: + it->bImmutable = bf; + break; + case EntryDeleted: + it->bDeleted = bf; + break; + case EntryExpansion: + it->bExpand = bf; + break; + default: + break; // fall through + } + } +} + +bool KEntryMap::revertEntry(const QByteArray& group, const QByteArray& key, KEntryMap::SearchFlags flags) +{ + Q_ASSERT((flags & KEntryMap::SearchDefaults) == 0); + Iterator entry = findEntry(group, key, flags); + if (entry != end()) { + //qDebug() << "reverting" << entry.key() << " = " << entry->mValue; + if (entry->bReverted) { // already done before + return false; + } + + KEntryKey defaultKey(entry.key()); + defaultKey.bDefault = true; + //qDebug() << "looking up default entry with key=" << defaultKey; + const ConstIterator defaultEntry = constFind(defaultKey); + if (defaultEntry != constEnd()) { + Q_ASSERT(defaultEntry.key().bDefault); + //qDebug() << "found, update entry"; + *entry = *defaultEntry; // copy default value, for subsequent lookups + } else { + entry->mValue = QByteArray(); + } + entry->bDirty = true; + entry->bReverted = true; // skip it when writing out to disk + + //qDebug() << "Here's what we have now:" << *this; + return true; + } + return false; +} diff --git a/src/core/kconfigdata.h b/src/core/kconfigdata.h new file mode 100644 index 00000000..f7ad81b9 --- /dev/null +++ b/src/core/kconfigdata.h @@ -0,0 +1,237 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999-2000 Preston Brown <pbrown@kde.org> + Copyright (C) 1996-2000 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGDATA_H +#define KCONFIGDATA_H + +#include <QtCore/QByteArray> +#include <QtCore/QString> +#include <QtCore/QMap> +#include <QtCore/QDebug> + +/** + * map/dict/list config node entry. + * @internal + */ +struct KEntry +{ + /** Constructor. @internal */ + KEntry() + : mValue(), bDirty(false), + bGlobal(false), bImmutable(false), bDeleted(false), bExpand(false), bReverted(false) {} + /** @internal */ + QByteArray mValue; + /** + * Must the entry be written back to disk? + */ + bool bDirty :1; + /** + * Entry should be written to the global config file + */ + bool bGlobal:1; + /** + * Entry can not be modified. + */ + bool bImmutable:1; + /** + * Entry has been deleted. + */ + bool bDeleted:1; + /** + * Whether to apply dollar expansion or not. + */ + bool bExpand:1; + /** + * Entry has been reverted to its default value (from a more global file). + */ + bool bReverted:1; +}; + +// These operators are used to check whether an entry which is about +// to be written equals the previous value. As such, this intentionally +// omits the dirty flag from the comparison. +inline bool operator ==(const KEntry &k1, const KEntry &k2) +{ + return k1.bGlobal == k2.bGlobal && k1.bImmutable == k2.bImmutable + && k1.bDeleted == k2.bDeleted && k1.bExpand == k2.bExpand + && k1.mValue == k2.mValue; +} + +inline bool operator !=(const KEntry &k1, const KEntry &k2) +{ + return !(k1 == k2); +} + +/** + * key structure holding both the actual key and the group + * to which it belongs. + * @internal + */ +struct KEntryKey +{ + /** Constructor. @internal */ + KEntryKey(const QByteArray& _group = QByteArray(), + const QByteArray& _key = QByteArray(), bool isLocalized=false, bool isDefault=false) + : mGroup(_group), mKey(_key), bLocal(isLocalized), bDefault(isDefault), bRaw(false) + { ; } + /** + * The "group" to which this EntryKey belongs + */ + QByteArray mGroup; + /** + * The _actual_ key of the entry in question + */ + QByteArray mKey; + /** + * Entry is localised or not + */ + bool bLocal :1; + /** + * Entry indicates if this is a default value. + */ + bool bDefault:1; + /** @internal + * Key is a raw unprocessed key. + * @warning this should only be set during merging, never for normal use. + */ + bool bRaw:1; +}; + +/** + * Compares two KEntryKeys (needed for QMap). The order is localized, localized-default, + * non-localized, non-localized-default + * @internal + */ +inline bool operator <(const KEntryKey &k1, const KEntryKey &k2) +{ + int result = qstrcmp(k1.mGroup, k2.mGroup); + if (result != 0) { + return result < 0; + } + + result = qstrcmp(k1.mKey, k2.mKey); + if (result != 0) { + return result < 0; + } + + if (k1.bLocal != k2.bLocal) + return k1.bLocal; + return (!k1.bDefault && k2.bDefault); +} + + +QDebug operator<<(QDebug dbg, const KEntryKey& key); +QDebug operator<<(QDebug dbg, const KEntry& entry); + +/** + * \relates KEntry + * type specifying a map of entries (key,value pairs). + * The keys are actually a key in a particular config file group together + * with the group name. + * @internal + */ +class KEntryMap : public QMap<KEntryKey, KEntry> +{ + public: + enum SearchFlag { + SearchDefaults=1, + SearchLocalized=2 + }; + Q_DECLARE_FLAGS(SearchFlags, SearchFlag) + + enum EntryOption { + EntryDirty=1, + EntryGlobal=2, + EntryImmutable=4, + EntryDeleted=8, + EntryExpansion=16, + EntryRawKey=32, + EntryDefault=(SearchDefaults<<16), + EntryLocalized=(SearchLocalized<<16) + }; + Q_DECLARE_FLAGS(EntryOptions, EntryOption) + + Iterator findExactEntry(const QByteArray& group, const QByteArray& key = QByteArray(), + SearchFlags flags = SearchFlags()); + + Iterator findEntry(const QByteArray& group, const QByteArray& key = QByteArray(), + SearchFlags flags = SearchFlags()); + + ConstIterator findEntry(const QByteArray& group, const QByteArray& key = QByteArray(), + SearchFlags flags = SearchFlags()) const; + + /** + * Returns true if the entry gets dirtied or false in other case + */ + bool setEntry(const QByteArray& group, const QByteArray& key, + const QByteArray& value, EntryOptions options); + + void setEntry(const QByteArray& group, const QByteArray& key, + const QString & value, EntryOptions options) + { + setEntry(group, key, value.toUtf8(), options); + } + + QString getEntry(const QByteArray& group, const QByteArray& key, + const QString & defaultValue = QString(), + SearchFlags flags = SearchFlags(), + bool * expand=0) const; + + bool hasEntry(const QByteArray& group, const QByteArray& key = QByteArray(), + SearchFlags flags = SearchFlags()) const; + + bool getEntryOption(const ConstIterator& it, EntryOption option) const; + bool getEntryOption(const QByteArray& group, const QByteArray& key, + SearchFlags flags, EntryOption option) const + { + return getEntryOption(findEntry(group, key, flags), option); + } + + void setEntryOption(Iterator it, EntryOption option, bool bf); + void setEntryOption(const QByteArray& group, const QByteArray& key, SearchFlags flags, + EntryOption option, bool bf) + { + setEntryOption(findEntry(group, key, flags), option, bf); + } + + bool revertEntry(const QByteArray& group, const QByteArray& key, SearchFlags flags=SearchFlags()); +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::SearchFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::EntryOptions) + +/** + * \relates KEntry + * type for iterating over keys in a KEntryMap in sorted order. + * @internal + */ +typedef QMap<KEntryKey, KEntry>::Iterator KEntryMapIterator; + +/** + * \relates KEntry + * type for iterating over keys in a KEntryMap in sorted order. + * It is const, thus you cannot change the entries in the iterator, + * only examine them. + * @internal + */ +typedef QMap<KEntryKey, KEntry>::ConstIterator KEntryMapConstIterator; + +#endif diff --git a/src/core/kconfiggroup.cpp b/src/core/kconfiggroup.cpp new file mode 100644 index 00000000..ab7d494f --- /dev/null +++ b/src/core/kconfiggroup.cpp @@ -0,0 +1,1243 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Qt5 TODO: re-enable. No point in doing it before, it breaks on QString::fromUtf8(QByteArray), which exists in qt5. +#undef QT_NO_CAST_FROM_BYTEARRAY + +#include "kconfiggroup.h" +#include "kconfiggroup_p.h" + +#include "kconfig.h" +#include "kconfig_p.h" +#include "ksharedconfig.h" +#include "kconfigdata.h" + +#include <QtCore/QDate> +#include <QtCore/QSharedData> +#include <QtCore/QFile> +#include <QtCore/QPoint> +#include <QtCore/QRect> +#include <QtCore/QString> +#include <QtCore/QTextStream> +#include <QtCore/QDir> +#include <QtCore/QUrl> + +#include <stdlib.h> + +class KConfigGroupPrivate : public QSharedData +{ + public: + KConfigGroupPrivate(KConfig* owner, bool isImmutable, bool isConst, const QByteArray &name) + : mOwner(owner), mName(name), bImmutable(isImmutable), bConst(isConst) + { + } + + KConfigGroupPrivate(const KSharedConfigPtr &owner, const QByteArray& name) + : sOwner(owner), mOwner(sOwner.data()), mName(name), + bImmutable(name.isEmpty()? owner->isImmutable(): owner->isGroupImmutable(name)), bConst(false) + { + } + + KConfigGroupPrivate(KConfigGroup* parent, bool isImmutable, bool isConst, const QByteArray& name) + : sOwner(parent->d->sOwner), mOwner(parent->d->mOwner), mName(name), + bImmutable(isImmutable), bConst(isConst) + { + if (!parent->d->mName.isEmpty()) + mParent = parent->d; + } + + KConfigGroupPrivate(const KConfigGroupPrivate* other, bool isImmutable, const QByteArray &name) + : sOwner(other->sOwner), mOwner(other->mOwner), mName(name), + bImmutable(isImmutable), bConst(other->bConst) + { + if (!other->mName.isEmpty()) + mParent = const_cast<KConfigGroupPrivate *>(other); + } + + KSharedConfig::Ptr sOwner; + KConfig *mOwner; + QExplicitlySharedDataPointer<KConfigGroupPrivate> mParent; + QByteArray mName; + + /* bitfield */ + const bool bImmutable:1; // is this group immutable? + const bool bConst:1; // is this group read-only? + + QByteArray fullName() const + { + if (!mParent) { + return name(); + } + return mParent->fullName(mName); + } + + QByteArray name() const + { + if (mName.isEmpty()) + return "<default>"; + return mName; + } + + QByteArray fullName(const QByteArray& aGroup) const + { + if (mName.isEmpty()) + return aGroup; + return fullName() + '\x1d' + aGroup; + } + + static QExplicitlySharedDataPointer<KConfigGroupPrivate> create(KConfigBase *master, + const QByteArray &name, + bool isImmutable, + bool isConst) + { + QExplicitlySharedDataPointer<KConfigGroupPrivate> data; + if (dynamic_cast<KConfigGroup*>(master)) + data = new KConfigGroupPrivate(dynamic_cast<KConfigGroup*>(master), isImmutable, isConst, name); + else + data = new KConfigGroupPrivate(dynamic_cast<KConfig*>(master), isImmutable, isConst, name); + return data; + } + + static QByteArray serializeList(const QList<QByteArray> &list); + static QStringList deserializeList(const QString &data); +}; + +QByteArray KConfigGroupPrivate::serializeList(const QList<QByteArray> &list) +{ + QByteArray value = ""; + + if (!list.isEmpty()) { + QList<QByteArray>::ConstIterator it = list.constBegin(); + const QList<QByteArray>::ConstIterator end = list.constEnd(); + + value = QByteArray(*it).replace('\\', "\\\\").replace(',', "\\,"); + + while (++it != end) { + // In the loop, so it is not done when there is only one element. + // Doing it repeatedly is a pretty cheap operation. + value.reserve(4096); + + value += ','; + value += QByteArray(*it).replace('\\', "\\\\").replace(',', "\\,"); + } + + // To be able to distinguish an empty list from a list with one empty element. + if (value.isEmpty()) + value = "\\0"; + } + + return value; +} + +QStringList KConfigGroupPrivate::deserializeList(const QString &data) +{ + if (data.isEmpty()) + return QStringList(); + if (data == QLatin1String("\\0")) + return QStringList(QString()); + QStringList value; + QString val; + val.reserve(data.size()); + bool quoted = false; + for (int p = 0; p < data.length(); p++) { + if (quoted) { + val += data[p]; + quoted = false; + } else if (data[p].unicode() == '\\') { + quoted = true; + } else if (data[p].unicode() == ',') { + val.squeeze(); // release any unused memory + value.append(val); + val.clear(); + val.reserve(data.size() - p); + } else { + val += data[p]; + } + } + value.append(val); + return value; +} + +static QList<int> asIntList(const QByteArray& string) +{ + QList<int> list; + Q_FOREACH(const QByteArray& s, string.split(',')) + list << s.toInt(); + return list; +} + +static QList<qreal> asRealList(const QByteArray& string) +{ + QList<qreal> list; + Q_FOREACH(const QByteArray& s, string.split(',')) + list << s.toDouble(); + return list; +} + +static QString errString( const char * pKey, const QByteArray & value, const QVariant & aDefault ) { + return QString::fromLatin1("\"%1\" - conversion of \"%3\" to %2 failed") + .arg(QString::fromLatin1(pKey)) + .arg(QString::fromLatin1(QVariant::typeToName(aDefault.type()))) + .arg(QString::fromLatin1(value)); +} + +static QString formatError( int expected, int got ) { + return QString::fromLatin1(" (wrong format: expected %1 items, got %2)").arg( expected ).arg( got ); +} + +QVariant KConfigGroup::convertToQVariant(const char *pKey, const QByteArray& value, const QVariant& aDefault) +{ + // if a type handler is added here you must add a QVConversions definition + // to conversion_check.h, or ConversionCheck::to_QVariant will not allow + // readEntry<T> to convert to QVariant. + switch( aDefault.type() ) { + case QVariant::Invalid: + return QVariant(); + case QVariant::String: + // this should return the raw string not the dollar expanded string. + // imho if processed string is wanted should call + // readEntry(key, QString) not readEntry(key, QVariant) + return QString::fromUtf8(value); + case QVariant::List: + case QVariant::StringList: + return KConfigGroupPrivate::deserializeList(QString::fromUtf8(value)); + case QVariant::ByteArray: + return value; + case QVariant::Bool: { + const QByteArray lower(value.toLower()); + if (lower == "false" || lower == "no" || lower == "off" || lower == "0") + return false; + return true; + } + case QVariant::Double: + case QMetaType::Float: + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: { + QVariant tmp = value; + if ( !tmp.convert(aDefault.type()) ) + tmp = aDefault; + return tmp; + } + case QVariant::Point: { + const QList<int> list = asIntList(value); + + if ( list.count() != 2 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 2, list.count() ); + return aDefault; + } + return QPoint(list.at( 0 ), list.at( 1 )); + } + case QVariant::PointF: { + const QList<qreal> list = asRealList(value); + + if ( list.count() != 2 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 2, list.count() ); + return aDefault; + } + return QPointF(list.at( 0 ), list.at( 1 )); + } + case QVariant::Rect: { + const QList<int> list = asIntList(value); + + if ( list.count() != 4 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 4, list.count() ); + return aDefault; + } + const QRect rect(list.at( 0 ), list.at( 1 ), list.at( 2 ), list.at( 3 )); + if ( !rect.isValid() ) { + qWarning() << errString( pKey, value, aDefault ); + return aDefault; + } + return rect; + } + case QVariant::RectF: { + const QList<qreal> list = asRealList(value); + + if ( list.count() != 4 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 4, list.count() ); + return aDefault; + } + const QRectF rect(list.at( 0 ), list.at( 1 ), list.at( 2 ), list.at( 3 )); + if ( !rect.isValid() ) { + qWarning() << errString( pKey, value, aDefault ); + return aDefault; + } + return rect; + } + case QVariant::Size: { + const QList<int> list = asIntList(value); + + if ( list.count() != 2 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 2, list.count() ); + return aDefault; + } + const QSize size(list.at( 0 ), list.at( 1 )); + if ( !size.isValid() ) { + qWarning() << errString( pKey, value, aDefault ); + return aDefault; + } + return size; + } + case QVariant::SizeF: { + const QList<qreal> list = asRealList(value); + + if ( list.count() != 2 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 2, list.count() ); + return aDefault; + } + const QSizeF size(list.at( 0 ), list.at( 1 )); + if ( !size.isValid() ) { + qWarning() << errString( pKey, value, aDefault ); + return aDefault; + } + return size; + } + case QVariant::DateTime: { + const QList<int> list = asIntList(value); + if ( list.count() != 6 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 6, list.count() ); + return aDefault; + } + const QDate date( list.at( 0 ), list.at( 1 ), list.at( 2 ) ); + const QTime time( list.at( 3 ), list.at( 4 ), list.at( 5 ) ); + const QDateTime dt( date, time ); + if ( !dt.isValid() ) { + qWarning() << errString( pKey, value, aDefault ); + return aDefault; + } + return dt; + } + case QVariant::Date: { + QList<int> list = asIntList(value); + if ( list.count() == 6 ) + list = list.mid(0, 3); // don't break config files that stored QDate as QDateTime + if ( list.count() != 3 ) { + qWarning() << errString( pKey, value, aDefault ) + << formatError( 3, list.count() ); + return aDefault; + } + const QDate date( list.at( 0 ), list.at( 1 ), list.at( 2 ) ); + if ( !date.isValid() ) { + qWarning() << errString( pKey, value, aDefault ); + return aDefault; + } + return date; + } + case QVariant::Color: + case QVariant::Font: + qWarning() << "KConfigGroup::readEntry was passed GUI type '" + << aDefault.typeName() + << "' but kdeui isn't linked! If it is linked to your program, " + "this is a platform bug. Please inform the KDE developers"; + break; + case QVariant::Url: + return QUrl(QString::fromUtf8(value)); + + default: + break; + } + + qWarning() << "unhandled type " << aDefault.typeName(); + return QVariant(); +} + +#ifdef Q_OS_WIN +# include <QtCore/QDir> +#endif + +static bool cleanHomeDirPath( QString &path, const QString &homeDir ) +{ +#ifdef Q_OS_WIN //safer + if (!QDir::toNativeSeparators(path).startsWith(QDir::toNativeSeparators(homeDir))) + return false; +#else + if (!path.startsWith(homeDir)) + return false; +#endif + + int len = homeDir.length(); + // replace by "$HOME" if possible + if (len && (path.length() == len || path[len] == QLatin1Char('/'))) { + path.replace(0, len, QString::fromLatin1("$HOME")); + return true; + } else + return false; +} + +static QString translatePath( QString path ) // krazy:exclude=passbyvalue +{ + if (path.isEmpty()) + return path; + + // only "our" $HOME should be interpreted + path.replace(QLatin1Char('$'), QLatin1String("$$")); + + bool startsWithFile = path.startsWith(QLatin1String("file:"), Qt::CaseInsensitive); + + // return original path, if it refers to another type of URL (e.g. http:/), or + // if the path is already relative to another directory + if ((!startsWithFile && QFileInfo(path).isRelative()) || + (startsWithFile && QFileInfo(path.mid(5)).isRelative())) + return path; + + if (startsWithFile) + path.remove(0,5); // strip leading "file:/" off the string + + // keep only one single '/' at the beginning - needed for cleanHomeDirPath() + while (path[0] == QLatin1Char('/') && path[1] == QLatin1Char('/')) + path.remove(0,1); + + // we can not use KGlobal::dirs()->relativeLocation("home", path) here, + // since it would not recognize paths without a trailing '/'. + // All of the 3 following functions to return the user's home directory + // can return different paths. We have to test all them. + const QString homeDir0 = QFile::decodeName(qgetenv("HOME")); + const QString homeDir1 = QDir::homePath(); + const QString homeDir2 = QDir(homeDir1).canonicalPath(); + if (cleanHomeDirPath(path, homeDir0) || + cleanHomeDirPath(path, homeDir1) || + cleanHomeDirPath(path, homeDir2) ) { + // qDebug() << "Path was replaced\n"; + } + + if (startsWithFile) + path.prepend(QString::fromLatin1("file://")); + + return path; +} + + +KConfigGroup::KConfigGroup() : d(0) +{ +} + +bool KConfigGroup::isValid() const +{ + return 0 != d.constData(); +} + +KConfigGroupGui _kde_internal_KConfigGroupGui; +static inline bool readEntryGui(const QByteArray& data, const char* key, const QVariant &input, + QVariant &output) +{ + if (_kde_internal_KConfigGroupGui.readEntryGui) + return _kde_internal_KConfigGroupGui.readEntryGui(data, key, input, output); + return false; +} + +static inline bool writeEntryGui(KConfigGroup *cg, const char* key, const QVariant &input, + KConfigGroup::WriteConfigFlags flags) +{ + if (_kde_internal_KConfigGroupGui.writeEntryGui) + return _kde_internal_KConfigGroupGui.writeEntryGui(cg, key, input, flags); + return false; +} + +KConfigGroup::KConfigGroup(KConfigBase *master, const QString &_group) + : d(KConfigGroupPrivate::create(master, _group.toUtf8(), master->isGroupImmutable(_group), false)) +{ +} + +KConfigGroup::KConfigGroup(KConfigBase *master, const char *_group) + : d(KConfigGroupPrivate::create(master, _group, master->isGroupImmutable(_group), false)) +{ +} + +KConfigGroup::KConfigGroup(const KConfigBase *master, const QString &_group) + : d(KConfigGroupPrivate::create(const_cast<KConfigBase*>(master), _group.toUtf8(), master->isGroupImmutable(_group), true)) +{ +} + +KConfigGroup::KConfigGroup(const KConfigBase *master, const char * _group) + : d(KConfigGroupPrivate::create(const_cast<KConfigBase*>(master), _group, master->isGroupImmutable(_group), true)) +{ +} + +KConfigGroup::KConfigGroup(const KSharedConfigPtr &master, const QString &_group) + : d(new KConfigGroupPrivate(master, _group.toUtf8())) +{ +} + +KConfigGroup::KConfigGroup(const KSharedConfigPtr &master, const char * _group) + : d(new KConfigGroupPrivate(master, _group)) +{ +} + +KConfigGroup &KConfigGroup::operator=(const KConfigGroup &rhs) +{ + d = rhs.d; + return *this; +} + +KConfigGroup::KConfigGroup(const KConfigGroup &rhs) + : KConfigBase(), d(rhs.d) +{ +} + +KConfigGroup::~KConfigGroup() +{ + d = 0; +} + +KConfigGroup KConfigGroup::groupImpl(const QByteArray& aGroup) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group"); + Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group"); + + KConfigGroup newGroup; + + newGroup.d = new KConfigGroupPrivate(this, isGroupImmutableImpl(aGroup), d->bConst, aGroup); + + return newGroup; +} + +const KConfigGroup KConfigGroup::groupImpl(const QByteArray& aGroup) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group"); + Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group"); + + KConfigGroup newGroup; + + newGroup.d = new KConfigGroupPrivate(const_cast<KConfigGroup*>(this), isGroupImmutableImpl(aGroup), + true, aGroup); + + return newGroup; +} + +KConfigGroup KConfigGroup::parent() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::parent", "accessing an invalid group"); + + KConfigGroup parentGroup; + + if (d->mParent) { + parentGroup.d = d->mParent; + } else { + parentGroup.d = new KConfigGroupPrivate(d->mOwner, d->mOwner->isImmutable(), d->bConst, ""); + // make sure we keep the refcount up on the KConfig object + parentGroup.d->sOwner = d->sOwner; + } + + return parentGroup; +} + +void KConfigGroup::deleteGroup(WriteConfigFlags flags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroup", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroup", "deleting a read-only group"); + + config()->deleteGroup(d->fullName(), flags); +} + +#ifndef KDE_NO_DEPRECATED +void KConfigGroup::changeGroup( const QString &group ) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::changeGroup", "accessing an invalid group"); + d.detach(); + d->mName = group.toUtf8(); +} +#endif + +#ifndef KDE_NO_DEPRECATED +void KConfigGroup::changeGroup( const char *group ) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::changeGroup", "accessing an invalid group"); + d.detach(); + d->mName = group; +} +#endif + +QString KConfigGroup::name() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::name", "accessing an invalid group"); + + return QString::fromUtf8(d->name()); +} + +bool KConfigGroup::exists() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::exists", "accessing an invalid group"); + + return config()->hasGroup( d->fullName() ); +} + +bool KConfigGroup::sync() +{ + Q_ASSERT_X(isValid(), "KConfigGroup::sync", "accessing an invalid group"); + + if (!d->bConst) + return config()->sync(); + + return false; +} + +QMap<QString, QString> KConfigGroup::entryMap() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::entryMap", "accessing an invalid group"); + + return config()->entryMap(QString::fromUtf8(d->fullName())); +} + +KConfig* KConfigGroup::config() +{ + Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group"); + + return d->mOwner; +} + +const KConfig* KConfigGroup::config() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group"); + + return d->mOwner; +} + +bool KConfigGroup::isEntryImmutable(const char* key) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::isEntryImmutable", "accessing an invalid group"); + + return (isImmutable() || + !config()->d_func()->canWriteEntry(d->fullName(), key, config()->readDefaults())); +} + +bool KConfigGroup::isEntryImmutable(const QString& key) const +{ + return isEntryImmutable(key.toUtf8().constData()); +} + +QString KConfigGroup::readEntryUntranslated(const QString& pKey, const QString& aDefault) const +{ + return readEntryUntranslated(pKey.toUtf8().constData(), aDefault); +} + +QString KConfigGroup::readEntryUntranslated(const char *key, const QString& aDefault) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readEntryUntranslated", "accessing an invalid group"); + + QString result = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchFlags(), 0); + if (result.isNull()) + return aDefault; + return result; +} + +QString KConfigGroup::readEntry(const char *key, const char* aDefault) const +{ + return readEntry(key, QString::fromUtf8(aDefault)); +} + +QString KConfigGroup::readEntry(const QString &key, const char* aDefault) const +{ + return readEntry(key.toUtf8().constData(), aDefault); +} + +QString KConfigGroup::readEntry(const char* key, const QString& aDefault) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group"); + + bool expand = false; + + // read value from the entry map + QString aValue = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized, + &expand); + if (aValue.isNull()) + aValue = aDefault; + + if (expand) + return KConfigPrivate::expandString(aValue); + + return aValue; +} + +QString KConfigGroup::readEntry(const QString &key, const QString& aDefault) const +{ + return readEntry(key.toUtf8().constData(), aDefault); +} + +QStringList KConfigGroup::readEntry(const char* key, const QStringList& aDefault) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group"); + + const QString data = readEntry(key, QString()); + if (data.isNull()) + return aDefault; + + return KConfigGroupPrivate::deserializeList(data); +} + +QStringList KConfigGroup::readEntry( const QString& key, const QStringList& aDefault) const +{ + return readEntry( key.toUtf8().constData(), aDefault ); +} + +QVariant KConfigGroup::readEntry( const char* key, const QVariant &aDefault ) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group"); + + const QByteArray data = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized); + if (data.isNull()) + return aDefault; + + QVariant value; + if (!readEntryGui( data, key, aDefault, value )) + return convertToQVariant(key, data, aDefault); + + return value; +} + +QVariant KConfigGroup::readEntry( const QString& key, const QVariant& aDefault) const +{ + return readEntry( key.toUtf8().constData(), aDefault ); +} + +QVariantList KConfigGroup::readEntry( const char* key, const QVariantList& aDefault) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group"); + + const QString data = readEntry(key, QString()); + if (data.isNull()) + return aDefault; + + QVariantList value; + Q_FOREACH(const QString& v, KConfigGroupPrivate::deserializeList(data)) + value << v; + + return value; +} + +QVariantList KConfigGroup::readEntry( const QString& key, const QVariantList& aDefault) const +{ + return readEntry( key.toUtf8().constData(), aDefault ); +} + +QStringList KConfigGroup::readXdgListEntry(const QString& key, const QStringList& aDefault) const +{ + return readXdgListEntry(key.toUtf8().constData(), aDefault); +} + +QStringList KConfigGroup::readXdgListEntry(const char *key, const QStringList& aDefault) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readXdgListEntry", "accessing an invalid group"); + + const QString data = readEntry(key, QString()); + if (data.isNull()) + return aDefault; + + QStringList value; + QString val; + val.reserve(data.size()); + // XXX List serialization being a separate layer from low-level parsing is + // probably a bug. No affected entries are defined, though. + bool quoted = false; + for (int p = 0; p < data.length(); p++) { + if (quoted) { + val += data[p]; + quoted = false; + } else if (data[p] == QLatin1Char('\\')) { + quoted = true; + } else if (data[p] == QLatin1Char(';')) { + value.append(val); + val.clear(); + val.reserve(data.size() - p); + } else { + val += data[p]; + } + } + if (!val.isEmpty()) { + qWarning() << "List entry" << key << "in" << config()->name() << "is not compliant with XDG standard (missing trailing semicolon)."; + value.append(val); + } + return value; +} + +QString KConfigGroup::readPathEntry(const QString& pKey, const QString & aDefault) const +{ + return readPathEntry(pKey.toUtf8().constData(), aDefault); +} + +QString KConfigGroup::readPathEntry(const char *key, const QString & aDefault) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group"); + + bool expand = false; + + QString aValue = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized, + &expand); + if (aValue.isNull()) + aValue = aDefault; + + return KConfigPrivate::expandString(aValue); +} + +QStringList KConfigGroup::readPathEntry(const QString& pKey, const QStringList& aDefault) const +{ + return readPathEntry(pKey.toUtf8().constData(), aDefault); +} + +QStringList KConfigGroup::readPathEntry(const char *key, const QStringList& aDefault) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group"); + + const QString data = readPathEntry(key, QString()); + if (data.isNull()) + return aDefault; + + return KConfigGroupPrivate::deserializeList(data); +} + +void KConfigGroup::writeEntry( const char* key, const QString& value, WriteConfigFlags flags ) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group"); + + writeEntry(key, value.toUtf8(), flags); +} + +void KConfigGroup::writeEntry( const QString& key, const QString& value, WriteConfigFlags flags ) +{ + writeEntry(key.toUtf8().constData(), value, flags); +} + +void KConfigGroup::writeEntry(const QString &key, const char *value, WriteConfigFlags pFlags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group"); + + writeEntry(key.toUtf8().constData(), QVariant(QString::fromLatin1(value)), pFlags); +} + +void KConfigGroup::writeEntry(const char *key, const char *value, WriteConfigFlags pFlags) +{ + writeEntry(key, QVariant(QString::fromLatin1(value)), pFlags); +} + +void KConfigGroup::writeEntry( const char* key, const QByteArray& value, + WriteConfigFlags flags ) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group"); + + config()->d_func()->putData(d->fullName(), key, value.isNull()? QByteArray(""): value, flags); +} + +void KConfigGroup::writeEntry(const QString& key, const QByteArray& value, + WriteConfigFlags pFlags) +{ + writeEntry(key.toUtf8().constData(), value, pFlags); +} + +void KConfigGroup::writeEntry(const char* key, const QStringList &list, WriteConfigFlags flags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group"); + + QList<QByteArray> balist; + + Q_FOREACH(const QString &entry, list) + balist.append(entry.toUtf8()); + + writeEntry(key, KConfigGroupPrivate::serializeList(balist), flags); +} + +void KConfigGroup::writeEntry(const QString& key, const QStringList &list, WriteConfigFlags flags) +{ + writeEntry(key.toUtf8().constData(), list, flags); +} + +void KConfigGroup::writeEntry( const char* key, const QVariantList& list, WriteConfigFlags flags ) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group"); + + QList<QByteArray> data; + + Q_FOREACH(const QVariant& v, list) { + if (v.type() == QVariant::ByteArray) + data << v.toByteArray(); + else + data << v.toString().toUtf8(); + } + + writeEntry(key, KConfigGroupPrivate::serializeList(data), flags); +} + +void KConfigGroup::writeEntry( const char* key, const QVariant &value, + WriteConfigFlags flags ) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group"); + + if ( writeEntryGui( this, key, value, flags ) ) + return; // GUI type that was handled + + QByteArray data; + // if a type handler is added here you must add a QVConversions definition + // to conversion_check.h, or ConversionCheck::to_QVariant will not allow + // writeEntry<T> to convert to QVariant. + switch( value.type() ) { + case QVariant::Invalid: + data = ""; + break; + case QVariant::ByteArray: + data = value.toByteArray(); + break; + case QVariant::String: + case QVariant::Int: + case QVariant::UInt: + case QVariant::Double: + case QMetaType::Float: + case QVariant::Bool: + case QVariant::LongLong: + case QVariant::ULongLong: + data = value.toString().toUtf8(); + break; + case QVariant::List: + if (!value.canConvert(QVariant::StringList)) + qWarning() << "not all types in \"" << key << "\" can convert to QString," + " information will be lost"; + case QVariant::StringList: + writeEntry( key, value.toList(), flags ); + return; + case QVariant::Point: { + QVariantList list; + const QPoint rPoint = value.toPoint(); + list.insert( 0, rPoint.x() ); + list.insert( 1, rPoint.y() ); + + writeEntry( key, list, flags ); + return; + } + case QVariant::PointF: { + QVariantList list; + const QPointF point = value.toPointF(); + list.insert( 0, point.x() ); + list.insert( 1, point.y() ); + + writeEntry( key, list, flags ); + return; + } + case QVariant::Rect:{ + QVariantList list; + const QRect rRect = value.toRect(); + list.insert( 0, rRect.left() ); + list.insert( 1, rRect.top() ); + list.insert( 2, rRect.width() ); + list.insert( 3, rRect.height() ); + + writeEntry( key, list, flags ); + return; + } + case QVariant::RectF:{ + QVariantList list; + const QRectF rRectF = value.toRectF(); + list.insert(0, rRectF.left()); + list.insert(1, rRectF.top()); + list.insert(2, rRectF.width()); + list.insert(3, rRectF.height()); + + writeEntry(key, list, flags); + return; + } + case QVariant::Size:{ + QVariantList list; + const QSize rSize = value.toSize(); + list.insert( 0, rSize.width() ); + list.insert( 1, rSize.height() ); + + writeEntry( key, list, flags ); + return; + } + case QVariant::SizeF:{ + QVariantList list; + const QSizeF rSizeF = value.toSizeF(); + list.insert(0, rSizeF.width()); + list.insert(1, rSizeF.height()); + + writeEntry(key, list, flags); + return; + } + case QVariant::Date: { + QVariantList list; + const QDate date = value.toDate(); + + list.insert( 0, date.year() ); + list.insert( 1, date.month() ); + list.insert( 2, date.day() ); + + writeEntry( key, list, flags ); + return; + } + case QVariant::DateTime: { + QVariantList list; + const QDateTime rDateTime = value.toDateTime(); + + const QTime time = rDateTime.time(); + const QDate date = rDateTime.date(); + + list.insert( 0, date.year() ); + list.insert( 1, date.month() ); + list.insert( 2, date.day() ); + + list.insert( 3, time.hour() ); + list.insert( 4, time.minute() ); + list.insert( 5, time.second() ); + + writeEntry( key, list, flags ); + return; + } + + case QVariant::Color: + case QVariant::Font: + qWarning() << "KConfigGroup::writeEntry was passed GUI type '" + << value.typeName() + << "' but kdeui isn't linked! If it is linked to your program, this is a platform bug. " + "Please inform the KDE developers"; + break; + case QVariant::Url: + data = QUrl(value.toUrl()).toString().toUtf8(); + break; + default: + qWarning() << "KConfigGroup::writeEntry - unhandled type" << value.typeName() << "in group" << name(); + } + + writeEntry(key, data, flags); +} + +void KConfigGroup::writeEntry( const QString& key, const QVariant& value, WriteConfigFlags flags ) +{ + writeEntry(key.toUtf8().constData(), value, flags); +} + +void KConfigGroup::writeEntry(const QString& key, const QVariantList &list, WriteConfigFlags flags) +{ + writeEntry(key.toUtf8().constData(), list, flags); +} + +void KConfigGroup::writeXdgListEntry(const QString& key, const QStringList &value, WriteConfigFlags pFlags) +{ + writeXdgListEntry(key.toUtf8().constData(), value, pFlags); +} + +void KConfigGroup::writeXdgListEntry(const char *key, const QStringList &list, WriteConfigFlags flags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writeXdgListEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writeXdgListEntry", "writing to a read-only group"); + + QString value; + value.reserve(4096); + + // XXX List serialization being a separate layer from low-level escaping is + // probably a bug. No affected entries are defined, though. + QStringList::ConstIterator it = list.constBegin(); + const QStringList::ConstIterator end = list.constEnd(); + for (; it != end; ++it) { + QString val(*it); + val.replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char(';'), QLatin1String("\\;")); + value += val; + value += QLatin1Char(';'); + } + + writeEntry(key, value, flags); +} + +void KConfigGroup::writePathEntry(const QString& pKey, const QString & path, WriteConfigFlags pFlags) +{ + writePathEntry(pKey.toUtf8().constData(), path, pFlags); +} + +void KConfigGroup::writePathEntry(const char *pKey, const QString & path, WriteConfigFlags pFlags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group"); + + config()->d_func()->putData(d->fullName(), pKey, translatePath(path).toUtf8(), pFlags, true); +} + +void KConfigGroup::writePathEntry(const QString& pKey, const QStringList &value, WriteConfigFlags pFlags) +{ + writePathEntry(pKey.toUtf8().constData(), value, pFlags); +} + +void KConfigGroup::writePathEntry(const char *pKey, const QStringList &value, WriteConfigFlags pFlags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group"); + + QList<QByteArray> list; + Q_FOREACH(const QString& path, value) + list << translatePath(path).toUtf8(); + + config()->d_func()->putData(d->fullName(), pKey, KConfigGroupPrivate::serializeList(list), pFlags, true); +} + +void KConfigGroup::deleteEntry( const char *key, WriteConfigFlags flags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::deleteEntry", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteEntry", "deleting from a read-only group"); + + config()->d_func()->putData(d->fullName(), key, QByteArray(), flags); +} + +void KConfigGroup::deleteEntry( const QString& key, WriteConfigFlags flags) +{ + deleteEntry(key.toUtf8().constData(), flags); +} + +void KConfigGroup::revertToDefault(const char *key) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::revertToDefault", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::revertToDefault", "writing to a read-only group"); + + config()->d_func()->revertEntry(d->fullName(), key); +} + +void KConfigGroup::revertToDefault(const QString &key) +{ + revertToDefault(key.toUtf8().constData()); +} + +bool KConfigGroup::hasDefault(const char *key) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::hasDefault", "accessing an invalid group"); + + KEntryMap::SearchFlags flags = KEntryMap::SearchDefaults|KEntryMap::SearchLocalized; + + return !config()->d_func()->lookupData(d->fullName(), key, flags).isNull(); +} + +bool KConfigGroup::hasDefault(const QString &key) const +{ + return hasDefault(key.toUtf8().constData()); +} + +bool KConfigGroup::hasKey(const char *key) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::hasKey", "accessing an invalid group"); + + KEntryMap::SearchFlags flags = KEntryMap::SearchLocalized; + if ( config()->readDefaults() ) + flags |= KEntryMap::SearchDefaults; + + return !config()->d_func()->lookupData(d->fullName(), key, flags).isNull(); +} + +bool KConfigGroup::hasKey(const QString &key) const +{ + return hasKey(key.toUtf8().constData()); +} + +bool KConfigGroup::isImmutable() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::isImmutable", "accessing an invalid group"); + + return d->bImmutable; +} + +QStringList KConfigGroup::groupList() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::groupList", "accessing an invalid group"); + + return config()->d_func()->groupList(d->fullName()); +} + +QStringList KConfigGroup::keyList() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::keyList", "accessing an invalid group"); + + return entryMap().keys(); +} + +void KConfigGroup::markAsClean() +{ + Q_ASSERT_X(isValid(), "KConfigGroup::markAsClean", "accessing an invalid group"); + + config()->markAsClean(); +} + +KConfigGroup::AccessMode KConfigGroup::accessMode() const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::accessMode", "accessing an invalid group"); + + return config()->accessMode(); +} + +bool KConfigGroup::hasGroupImpl(const QByteArray & b) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::hasGroupImpl", "accessing an invalid group"); + + return config()->hasGroup(d->fullName(b)); +} + +void KConfigGroup::deleteGroupImpl(const QByteArray &b, WriteConfigFlags flags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroupImpl", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst,"KConfigGroup::deleteGroupImpl", "deleting from a read-only group"); + + config()->deleteGroup(d->fullName(b), flags); +} + +bool KConfigGroup::isGroupImmutableImpl(const QByteArray& b) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::isGroupImmutableImpl", "accessing an invalid group"); + + if (!hasGroupImpl(b)) // group doesn't exist yet + return d->bImmutable; // child groups are immutable if the parent is immutable. + + return config()->isGroupImmutable(d->fullName(b)); +} + +void KConfigGroup::copyTo(KConfigBase* other, WriteConfigFlags pFlags) const +{ + Q_ASSERT_X(isValid(), "KConfigGroup::copyTo", "accessing an invalid group"); + Q_ASSERT(other != 0); + + if (KConfigGroup *otherGroup = dynamic_cast<KConfigGroup*>(other)) { + config()->d_func()->copyGroup(d->fullName(), otherGroup->d->fullName(), otherGroup, pFlags); + } else if (KConfig* otherConfig = dynamic_cast<KConfig*>(other)) { + KConfigGroup newGroup = otherConfig->group(d->fullName()); + otherConfig->d_func()->copyGroup(d->fullName(), d->fullName(), &newGroup, pFlags); + } else { + Q_ASSERT_X(false, "KConfigGroup::copyTo", "unknown type of KConfigBase"); + } +} + +void KConfigGroup::reparent(KConfigBase* parent, WriteConfigFlags pFlags) +{ + Q_ASSERT_X(isValid(), "KConfigGroup::reparent", "accessing an invalid group"); + Q_ASSERT_X(!d->bConst, "KConfigGroup::reparent", "reparenting a read-only group"); + Q_ASSERT_X(!d->bImmutable, "KConfigGroup::reparent", "reparenting an immutable group"); + Q_ASSERT(parent != 0); + + KConfigGroup oldGroup(*this); + + d = KConfigGroupPrivate::create(parent, d->mName, false, false); + oldGroup.copyTo(this, pFlags); + oldGroup.deleteGroup(); // so that the entries with the old group name are deleted on sync +} diff --git a/src/core/kconfiggroup.h b/src/core/kconfiggroup.h new file mode 100644 index 00000000..ce0330be --- /dev/null +++ b/src/core/kconfiggroup.h @@ -0,0 +1,767 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + Copyright (c) 2001 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGGROUP_H +#define KCONFIGGROUP_H + +#include "kconfigbase.h" + +#include <kconfigcore_export.h> + +#include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QVariant> +#include <QtCore/QStringList> + +class KConfig; +class KConfigGroupPrivate; +class KSharedConfig; +typedef QExplicitlySharedDataPointer<KSharedConfig> KSharedConfigPtr; +/** + * \class KConfigGroup kconfiggroup.h <KConfigGroup> + * + * A class for one specific group in a KConfig object. + * + * If you want to access the top-level entries of a KConfig + * object, which are not associated with any group, use an + * empty group name. + * + * A KConfigGroup will be read-only if it is constructed from a + * const config object or from another read-only group. + */ +class KCONFIGCORE_EXPORT KConfigGroup : public KConfigBase +{ +public: + /** + * Constructs an invalid group. + * + * \see isValid + */ + KConfigGroup(); + + /** + * Construct a config group corresponding to @p group in @p master. + * + * This allows the creation of subgroups by passing another + * group as @p master. + * + * @p group is the group name encoded in UTF-8. + */ + KConfigGroup(KConfigBase *master, const QString &group); + /** Overload for KConfigGroup(KConfigBase*,const QString&) */ + KConfigGroup(KConfigBase *master, const char *group); + + /** + * Construct a read-only config group. + * + * A read-only group will silently ignore any attempts to write to it. + * + * This allows the creation of subgroups by passing an existing group + * as @p master. + */ + KConfigGroup(const KConfigBase *master, const QString &group); + /** Overload for KConfigGroup(const KConfigBase*,const QString&) */ + KConfigGroup(const KConfigBase *master, const char *group); + + /** Overload for KConfigGroup(const KConfigBase*,const QString&) */ + KConfigGroup(const KSharedConfigPtr &master, const QString &group); + /** Overload for KConfigGroup(const KConfigBase*,const QString&) */ + KConfigGroup(const KSharedConfigPtr &master, const char *group); + + /** + * Creates a read-only copy of a read-only group. + */ + KConfigGroup(const KConfigGroup &); + KConfigGroup &operator=(const KConfigGroup &); + + ~KConfigGroup(); + + /** + * Whether the group is valid. + * + * A group is invalid if it was constructed without arguments. + * + * You should not call any functions on an invalid group. + * + * @return @c true if the group is valid, @c false if it is invalid. + */ + bool isValid() const; + + /** + * The name of this group. + * + * The root group is named "<default>". + */ + QString name() const; + + /** + * Check whether the containing KConfig object acutally contains a + * group with this name. + */ + bool exists() const; + + /** + * @reimp + * + * Syncs the parent config. + */ + bool sync() Q_DECL_OVERRIDE; + + /// @reimp + void markAsClean(); + + /// @reimp + AccessMode accessMode() const; + + /** + * Return the config object that this group belongs to + */ + KConfig* config(); + /** + * Return the config object that this group belongs to + */ + const KConfig* config() const; + + /** + * Changes the group of the object + * + * @deprecated + * Create another KConfigGroup from the parent of this group instead. + */ +#ifndef KDE_NO_DEPRECATED + KCONFIGCORE_DEPRECATED void changeGroup(const QString &group); +#endif + /** + * Overload for changeGroup(const QString&) + * + * @deprecated + * Create another KConfigGroup from the parent of this group instead. + */ +#ifndef KDE_NO_DEPRECATED + KCONFIGCORE_DEPRECATED void changeGroup(const char *group); +#endif + + /** + * Copies the entries in this group to another configuration object + * + * @note @p other can be either another group or a different file. + * + * @param other the configuration object to copy this group's entries to + * @param pFlags the flags to use when writing the entries to the + * other configuration object + * + * @since 4.1 + */ + void copyTo(KConfigBase *other, WriteConfigFlags pFlags = Normal) const; + + /** + * Changes the configuration object that this group belongs to + * + * @note @p other can be another group, the top-level KConfig object or + * a different KConfig object entirely. + * + * If @p parent is already the parent of this group, this method will have + * no effect. + * + * @param parent the config object to place this group under + * @param pFlags the flags to use in determining which storage source to + * write the data to + * + * @since 4.1 + */ + void reparent(KConfigBase *parent, WriteConfigFlags pFlags = Normal); + + /** + * Returns the group that this group belongs to + * + * @return the parent group, or an invalid group if this is a top-level + * group + * + * @since 4.1 + */ + KConfigGroup parent() const; + + /** + * @reimp + */ + QStringList groupList() const; + + /** + * Returns a list of keys this group contains + */ + QStringList keyList() const; + + /** + * Delete all entries in the entire group + * + * @param pFlags flags passed to KConfig::deleteGroup + * + * @see deleteEntry() + */ + void deleteGroup(WriteConfigFlags pFlags = Normal); + using KConfigBase::deleteGroup; + + /** + * Reads the value of an entry specified by @p pKey in the current group + * + * This template method makes it possible to write + * QString foo = readEntry("...", QString("default")); + * and the same with all other types supported by QVariant. + * + * The return type of the method is simply the same as the type of the default value. + * + * @note readEntry("...", Qt::white) will not compile because Qt::white is an enum. + * You must turn it into readEntry("...", QColor(Qt::white)). + * + * @note Only the following QVariant types are allowed : String, + * StringList, List, Font, Point, Rect, Size, Color, Int, UInt, Bool, + * Double, LongLong, ULongLong, DateTime and Date. + * + * @param key The key to search for + * @param aDefault A default value returned if the key was not found + * @return The value for this key, or @p aDefault. + * + * @see writeEntry(), deleteEntry(), hasKey() + */ + template <typename T> + inline T readEntry(const QString &key, const T &aDefault) const + { return readCheck(key.toUtf8().constData(), aDefault); } + /** Overload for readEntry(const QString&, const T&) const */ + template <typename T> + inline T readEntry(const char *key, const T &aDefault) const + { return readCheck(key, aDefault); } + + /** + * Reads the value of an entry specified by @p key in the current group + * + * @param key the key to search for + * @param aDefault a default value returned if the key was not found + * @return the value for this key, or @p aDefault if the key was not found + * + * @see writeEntry(), deleteEntry(), hasKey() + */ + QVariant readEntry(const QString &key, const QVariant &aDefault) const; + /** Overload for readEntry(const QString&, const QVariant&) */ + QVariant readEntry(const char *key, const QVariant &aDefault) const; + + /** + * Reads the string value of an entry specified by @p key in the current group + * + * If you want to read a path, please use readPathEntry(). + * + * @param key the key to search for + * @param aDefault a default value returned if the key was not found + * @return the value for this key, or @p aDefault if the key was not found + * + * @see readPathEntry(), writeEntry(), deleteEntry(), hasKey() + */ + QString readEntry(const QString &key, const QString &aDefault) const; + /** Overload for readEntry(const QString&, const QString&) */ + QString readEntry(const char *key, const QString &aDefault) const; + + /** Overload for readEntry(const QString&, const QString&) */ + QString readEntry(const QString &key, const char *aDefault = 0) const; + /** Overload for readEntry(const QString&, const QString&) */ + QString readEntry(const char *key, const char *aDefault = 0) const; + + /** + * @copydoc readEntry(const char*, const QStringList&) const + * + * @warning This function doesn't convert the items returned + * to any type. It's actually a list of QVariant::String's. If you + * want the items converted to a specific type use + * readEntry(const char*, const QList<T>&) const + */ + QVariantList readEntry(const QString &key, const QVariantList &aDefault) const; + /** Overload for readEntry(const QString&, const QVariantList&) */ + QVariantList readEntry(const char *key, const QVariantList &aDefault) const; + + /** + * Reads a list of strings from the config object + * + * @param key The key to search for + * @param aDefault The default value to use if the key does not exist + * @return The list, or @p aDefault if @p key does not exist + * + * @see readXdgListEntry(), writeEntry(), deleteEntry(), hasKey() + */ + QStringList readEntry(const QString &key, const QStringList &aDefault) const; + /** Overload for readEntry(const QString&, const QStringList&) */ + QStringList readEntry(const char *key, const QStringList &aDefault) const; + + /** + * Reads a list of values from the config object + * + * @param key the key to search for + * @param aDefault the default value to use if the key does not exist + * @return the list, or @p aDefault if @p key does not exist + * + * @see readXdgListEntry(), writeEntry(), deleteEntry(), hasKey() + */ + template<typename T> + inline QList<T> readEntry(const QString &key, const QList<T> &aDefault) const + { return readListCheck(key.toUtf8().constData(), aDefault); } + /** Overload for readEntry(const QString&, const QList<T>&) */ + template<typename T> + inline QList<T> readEntry(const char *key, const QList<T> &aDefault) const + { return readListCheck(key, aDefault); } + + /** + * Reads a list of strings from the config object, following XDG + * desktop entry spec separator semantics + * + * @param pKey the key to search for + * @param aDefault the default value to use if the key does not exist + * @return the list, or @p aDefault if @p pKey does not exist + * + * @see readEntry(const QString&, const QStringList&) const + */ + QStringList readXdgListEntry(const QString &pKey, const QStringList &aDefault = QStringList()) const; + /** Overload for readXdgListEntry(const QString&, const QStringList&) */ + QStringList readXdgListEntry(const char *pKey, const QStringList &aDefault = QStringList()) const; + + /** + * Reads a path + * + * Read the value of an entry specified by @p pKey in the current group + * and interpret it as a path. This means, dollar expansion is activated + * for this value, so that e.g. $HOME gets expanded. + * + * @param pKey The key to search for. + * @param aDefault A default value returned if the key was not found. + * @return The value for this key. Can be QString() if @p aDefault is null. + */ + QString readPathEntry(const QString &pKey, const QString &aDefault) const; + /** Overload for readPathEntry(const QString&, const QString&) */ + QString readPathEntry(const char *key, const QString &aDefault) const; + + /** + * Reads a list of paths + * + * Read the value of an entry specified by @p pKey in the current group + * and interpret it as a list of paths. This means, dollar expansion is activated + * for this value, so that e.g. $HOME gets expanded. + * + * @param pKey the key to search for + * @param aDefault a default value returned if the key was not found + * @return the list, or @p aDefault if the key does not exist + */ + QStringList readPathEntry(const QString &pKey, const QStringList &aDefault) const; + /** Overload for readPathEntry(const QString&, const QStringList&) */ + QStringList readPathEntry(const char *key, const QStringList &aDefault) const; + + /** + * Reads an untranslated string entry + * + * You should not normally need to use this. + * + * @param pKey the key to search for + * @param aDefault a default value returned if the key was not found + * @return the value for this key, or @p aDefault if the key does not exist + */ + QString readEntryUntranslated(const QString &pKey, + const QString &aDefault = QString()) const; + /** Overload for readEntryUntranslated(const QString&, const QString&) */ + QString readEntryUntranslated(const char *key, + const QString &aDefault = QString()) const; + + /** + * Writes a value to the configuration object. + * + * @param key the key to write to + * @param value the value to write + * @param pFlags the flags to use when writing this entry + * + * @see readEntry(), writeXdgListEntry(), deleteEntry() + */ + void writeEntry(const QString &key, const QVariant &value, + WriteConfigFlags pFlags = Normal); + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const char *key, const QVariant &value, + WriteConfigFlags pFlags = Normal); + + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const QString &key, const QString &value, + WriteConfigFlags pFlags = Normal); + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const char *key, const QString &value, + WriteConfigFlags pFlags = Normal); + + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const QString &key, const QByteArray &value, + WriteConfigFlags pFlags = Normal); + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const char *key, const QByteArray &value, + WriteConfigFlags pFlags = Normal); + + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const QString &key, const char *value, WriteConfigFlags pFlags = Normal); + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags = Normal); + + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + template <typename T> + inline void writeEntry(const char *key, const T &value, WriteConfigFlags pFlags = Normal) + { writeCheck( key, value, pFlags ); } + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + template <typename T> + inline void writeEntry(const QString &key, const T &value, WriteConfigFlags pFlags = Normal) + { writeCheck( key.toUtf8().constData(), value, pFlags ); } + + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const QString &key, const QStringList &value, + WriteConfigFlags pFlags = Normal); + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const char *key, const QStringList &value, + WriteConfigFlags pFlags = Normal); + + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const QString &key, const QVariantList &value, + WriteConfigFlags pFlags = Normal); + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + void writeEntry(const char *key, const QVariantList &value, + WriteConfigFlags pFlags = Normal); + + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + template <typename T> + inline void writeEntry(const QString &key, const QList<T> &value, WriteConfigFlags pFlags = Normal) + { writeListCheck( key.toUtf8().constData(), value, pFlags ); } + /** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */ + template <typename T> + inline void writeEntry(const char *key, const QList<T> &value, WriteConfigFlags pFlags = Normal) + { writeListCheck( key, value, pFlags ); } + + /** + * Writes a list of strings to the config object, following XDG + * desktop entry spec separator semantics + * + * @param pKey the key to write to + * @param value the list to write + * @param pFlags the flags to use when writing this entry + * + * @see writeEntry(), readXdgListEntry() + */ + void writeXdgListEntry(const QString &pKey, const QStringList &value, + WriteConfigFlags pFlags = Normal); + /** Overload for writeXdgListEntry(const QString&, const QStringList&, WriteConfigFlags) */ + void writeXdgListEntry(const char *pKey, const QStringList &value, + WriteConfigFlags pFlags = Normal); + + /** + * Writes a file path to the configuration + * + * If the path is located under $HOME, the user's home directory + * is replaced with $HOME in the persistent storage. + * The path should therefore be read back with readPathEntry() + * + * @param pKey the key to write to + * @param path the path to write + * @param pFlags the flags to use when writing this entry + * + * @see writeEntry(), readPathEntry() + */ + void writePathEntry(const QString &pKey, const QString &path, + WriteConfigFlags pFlags = Normal); + /** Overload for writePathEntry(const QString&, const QString&, WriteConfigFlags) */ + void writePathEntry(const char *pKey, const QString &path, + WriteConfigFlags pFlags = Normal); + + /** + * Writes a list of paths to the configuration + * + * If any of the paths are located under $HOME, the user's home directory + * is replaced with $HOME in the persistent storage. + * The paths should therefore be read back with readPathEntry() + * + * @param pKey the key to write to + * @param value the list to write + * @param pFlags the flags to use when writing this entry + * + * @see writeEntry(), readPathEntry() + */ + void writePathEntry(const QString &pKey, const QStringList &value, + WriteConfigFlags pFlags = Normal); + /** Overload for writePathEntry(const QString&, const QStringList&, WriteConfigFlags) */ + void writePathEntry(const char *pKey, const QStringList &value, + WriteConfigFlags pFlags = Normal); + + /** + * Deletes the entry specified by @p pKey in the current group + * + * This also hides system wide defaults. + * + * @param pKey the key to delete + * @param pFlags the flags to use when deleting this entry + * + * @see deleteGroup(), readEntry(), writeEntry() + */ + void deleteEntry(const QString &pKey, WriteConfigFlags pFlags = Normal); + /** Overload for deleteEntry(const QString&, WriteConfigFlags) */ + void deleteEntry(const char *pKey, WriteConfigFlags pFlags = Normal); + + /** + * Checks whether the key has an entry in this group + * + * Use this to determine if a key is not specified for the current + * group (hasKey() returns false). + * + * If this returns @c false for a key, readEntry() (and its variants) + * will return the default value passed to them. + * + * @param key the key to search for + * @return @c true if the key is defined in this group by any of the + * configuration sources, @c false otherwise + * + * @see readEntry() + */ + bool hasKey(const QString &key) const; + /** Overload for hasKey(const QString&) const */ + bool hasKey(const char *key) const; + + /** + * Whether this group may be changed + * + * @return @c false if the group may be changed, @c true otherwise + */ + bool isImmutable() const; + + /** + * Checks if it is possible to change the given entry + * + * If isImmutable() returns @c true, then this method will return + * @c true for all inputs. + * + * @param key the key to check + * @return @c false if the key may be changed using this configuration + * group object, @c true otherwise + */ + bool isEntryImmutable(const QString &key) const; + /** Overload for isEntryImmutable(const QString&) const */ + bool isEntryImmutable(const char *key) const; + + /** + * Reverts an entry to the default settings. + * + * Reverts the entry with key @p key in the current group in the + * application specific config file to either the system wide (default) + * value or the value specified in the global KDE config file. + * + * To revert entries in the global KDE config file, the global KDE config + * file should be opened explicitly in a separate config object. + * + * @note This is @em not the same as deleting the key, as instead the + * global setting will be copied to the configuration file that this + * object manipulates. + * + * @param key The key of the entry to revert. + */ + void revertToDefault(const QString &key); + /** Overload for revertToDefault(const QString&) */ + void revertToDefault(const char* key); + + /** + * Whether a default is specified for an entry in either the + * system wide configuration file or the global KDE config file + * + * If an application computes a default value at runtime for + * a certain entry, e.g. like: + * \code + * QColor computedDefault = qApp->palette().color(QPalette::Active, QPalette::Text); + * QColor color = group.readEntry(key, computedDefault); + * \endcode + * then it may wish to make the following check before + * writing back changes: + * \code + * if ( (value == computedDefault) && !group.hasDefault(key) ) + * group.revertToDefault(key); + * else + * group.writeEntry(key, value); + * \endcode + * + * This ensures that as long as the entry is not modified to differ from + * the computed default, the application will keep using the computed default + * and will follow changes the computed default makes over time. + * + * @param key the key of the entry to check + * @return @c true if the global or system settings files specify a default + * for @p key in this group, @c false otherwise + */ + bool hasDefault(const QString &key) const; + /** Overload for hasDefault(const QString&) const */ + bool hasDefault(const char *key) const; + + /** + * Returns a map (tree) of entries for all entries in this group + * + * Only the actual entry string is returned, none of the + * other internal data should be included. + * + * @return a map of entries in this group, indexed by key + */ + QMap<QString, QString> entryMap() const; + +protected: + bool hasGroupImpl(const QByteArray &group) const; + KConfigGroup groupImpl(const QByteArray &b); + const KConfigGroup groupImpl(const QByteArray &b) const; + void deleteGroupImpl(const QByteArray &group, WriteConfigFlags flags); + bool isGroupImmutableImpl(const QByteArray &aGroup) const; + +private: + QExplicitlySharedDataPointer<KConfigGroupPrivate> d; + + template<typename T> + inline T readCheck(const char *key, const T &defaultValue) const; + + template<typename T> + inline QList<T> readListCheck(const char *key, const QList<T> &defaultValue) const; + + template<typename T> + inline void writeCheck(const char *key, const T &value, WriteConfigFlags pFlags); + + template<typename T> + inline void writeListCheck(const char *key, const QList<T> &value, WriteConfigFlags pFlags); + + friend class KConfigGroupPrivate; + + /** + * Return the data in @p value converted to a QVariant + * + * @param pKey the name of the entry being converted, this is only used for error + * reporting + * @param value the UTF-8 data to be converted + * @param aDefault the default value if @p pKey is not found + * @return @p value converted to QVariant, or @p aDefault if @p value is invalid or cannot be converted. + */ + static QVariant convertToQVariant(const char *pKey, const QByteArray &value, const QVariant &aDefault); + friend class KServicePrivate; // XXX yeah, ugly^5 +}; + +#define KCONFIGGROUP_ENUMERATOR_ERROR(ENUM) \ +"The Qt MetaObject system does not seem to know about \"" ENUM \ +"\" please use Q_ENUMS or Q_FLAGS to register it." + +/** + * To add support for your own enums in KConfig, you can declare them with Q_ENUMS() + * in a QObject subclass (which will make moc generate the code to turn the + * enum into a string and vice-versa), and then (in the cpp code) + * use the macro + * <code>KCONFIGGROUP_DECLARE_ENUM_QOBJECT(MyClass, MyEnum)</code> + * + * After that, you can use readEntry(group, key, value) and writeEntry(group, key, value[, flags]). + * Note that those are global functions, NOT member functions of KConfigGroup. + * + */ +#define KCONFIGGROUP_DECLARE_ENUM_QOBJECT(Class, Enum) \ +inline Class::Enum readEntry(const KConfigGroup& group, const char* key, const Class::Enum& def) \ +{ \ +const QMetaObject* M_obj = &Class::staticMetaObject; \ +const int M_index = M_obj->indexOfEnumerator(#Enum); \ +if(M_index == -1) qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Enum)); \ +const QMetaEnum M_enum = M_obj->enumerator(M_index); \ +const QByteArray M_data = group.readEntry(key, QByteArray(M_enum.valueToKey(def)));\ +return static_cast<Class::Enum>(M_enum.keyToValue(M_data.constData())); \ +} \ +inline void writeEntry(KConfigGroup& group, const char* key, const Class::Enum& value, KConfigBase::WriteConfigFlags flags = KConfigBase::Normal)\ +{ \ +const QMetaObject* M_obj = &Class::staticMetaObject; \ +const int M_index = M_obj->indexOfEnumerator(#Enum); \ +if(M_index == -1) qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Enum)); \ +const QMetaEnum M_enum = M_obj->enumerator(M_index); \ +group.writeEntry(key, QByteArray(M_enum.valueToKey(value)), flags); \ +} + +/** + * Similar to KCONFIGGROUP_DECLARE_ENUM_QOBJECT but for flags declared with Q_FLAGS() + * (where multiple values can be set at the same time) + */ +#define KCONFIGGROUP_DECLARE_FLAGS_QOBJECT(Class, Flags) \ +inline Class::Flags readEntry(const KConfigGroup& group, const char* key, const Class::Flags& def) \ +{ \ +const QMetaObject* M_obj = &Class::staticMetaObject; \ +const int M_index = M_obj->indexOfEnumerator(#Flags); \ +if(M_index == -1) qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Flags)); \ +const QMetaEnum M_enum = M_obj->enumerator(M_index); \ +const QByteArray M_data = group.readEntry(key, QByteArray(M_enum.valueToKeys(def)));\ +return static_cast<Class::Flags>(M_enum.keysToValue(M_data.constData())); \ +} \ +inline void writeEntry(KConfigGroup& group, const char* key, const Class::Flags& value, KConfigBase::WriteConfigFlags flags = KConfigBase::Normal)\ +{ \ +const QMetaObject* M_obj = &Class::staticMetaObject; \ +const int M_index = M_obj->indexOfEnumerator(#Flags); \ +if(M_index == -1) qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Flags)); \ +const QMetaEnum M_enum = M_obj->enumerator(M_index); \ +group.writeEntry(key, QByteArray(M_enum.valueToKeys(value)), flags); \ +} + +#include "conversion_check.h" + +template <typename T> +T KConfigGroup::readCheck(const char *key, const T &defaultValue) const +{ + ConversionCheck::to_QVariant<T>(); + return qvariant_cast<T>(readEntry(key, qVariantFromValue(defaultValue))); +} + +template <typename T> +QList<T> KConfigGroup::readListCheck(const char *key, const QList<T> &defaultValue) const +{ + ConversionCheck::to_QVariant<T>(); + ConversionCheck::to_QString<T>(); + + QVariantList data; + + Q_FOREACH(const T& value, defaultValue) + data.append(qVariantFromValue(value)); + + QList<T> list; + Q_FOREACH (const QVariant &value, readEntry<QVariantList>(key, data)) { + Q_ASSERT(value.canConvert<T>()); + list.append(qvariant_cast<T>(value)); + } + + return list; +} + +template <typename T> +void KConfigGroup::writeCheck(const char *key, const T &value, + WriteConfigFlags pFlags) +{ + ConversionCheck::to_QVariant<T>(); + writeEntry(key, qVariantFromValue(value), pFlags); +} + +template <typename T> +void KConfigGroup::writeListCheck(const char *key, const QList<T> &list, + WriteConfigFlags pFlags) +{ + ConversionCheck::to_QVariant<T>(); + ConversionCheck::to_QString<T>(); + QVariantList data; + Q_FOREACH(const T &value, list) { + data.append(qVariantFromValue(value)); + } + + writeEntry(key, data, pFlags); +} + +#endif // KCONFIGGROUP_H diff --git a/src/core/kconfiggroup_p.h b/src/core/kconfiggroup_p.h new file mode 100644 index 00000000..c5d4f150 --- /dev/null +++ b/src/core/kconfiggroup_p.h @@ -0,0 +1,42 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2007 Thiago Macieira <thiago@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGGROUP_P_H +#define KCONFIGGROUP_P_H + +#include <QtCore/QVariant> +#include "kconfiggroup.h" + +class KConfigGroup; + +struct KConfigGroupGui +{ + typedef bool (*kReadEntryGui)(const QByteArray& data, const char* key, const QVariant &input, + QVariant &output); + typedef bool (*kWriteEntryGui)(KConfigGroup *, const char* key, const QVariant &input, + KConfigGroup::WriteConfigFlags flags); + + kReadEntryGui readEntryGui; + kWriteEntryGui writeEntryGui; +}; + +extern KCONFIGCORE_EXPORT KConfigGroupGui _kde_internal_KConfigGroupGui; + +#endif diff --git a/src/core/kconfigini.cpp b/src/core/kconfigini.cpp new file mode 100644 index 00000000..f44b2c39 --- /dev/null +++ b/src/core/kconfigini.cpp @@ -0,0 +1,770 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (C) 1997-1999 Matthias Kalle Dalheimer (kalle@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kconfigini_p.h" + +#include "kconfig.h" +#include "kconfigbackend.h" +#include "bufferfragment_p.h" +#include "kconfigdata.h" + +#include <qsavefile.h> +#include <qlockfile.h> +#include <qdatetime.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdebug.h> +#include <qplatformdefs.h> + +#ifndef Q_OS_WIN +#include <unistd.h> // getuid, close +#endif +#include <sys/types.h> // uid_t +#include <fcntl.h> // open + +KCONFIGCORE_EXPORT bool kde_kiosk_exception = false; // flag to disable kiosk restrictions + +QString KConfigIniBackend::warningProlog(const QFile &file, int line) +{ + return QString::fromLatin1("KConfigIni: In file %2, line %1: ") + .arg(line).arg(file.fileName()); +} + +KConfigIniBackend::KConfigIniBackend() + : KConfigBackend(), lockFile(NULL) +{ +} + +KConfigIniBackend::~KConfigIniBackend() +{ +} + +KConfigBackend::ParseInfo + KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap, + ParseOptions options) +{ + return parseConfig(currentLocale, entryMap, options, false); +} + +// merging==true is the merging that happens at the beginning of writeConfig: +// merge changes in the on-disk file with the changes in the KConfig object. +KConfigBackend::ParseInfo +KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap, + ParseOptions options, bool merging) +{ + if (filePath().isEmpty() || !QFile::exists(filePath())) + return ParseOk; + + bool bDefault = options&ParseDefaults; + bool allowExecutableValues = options&ParseExpansions; + + QByteArray currentGroup("<default>"); + + QFile file(filePath()); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) + return ParseOpenError; + + QList<QByteArray> immutableGroups; + + bool fileOptionImmutable = false; + bool groupOptionImmutable = false; + bool groupSkip = false; + + int lineNo = 0; + // on systems using \r\n as end of line, \r will be taken care of by + // trim() below + QByteArray buffer = file.readAll(); + BufferFragment contents(buffer.data(), buffer.size()); + unsigned int len = contents.length(); + unsigned int startOfLine = 0; + + while (startOfLine < len) { + BufferFragment line = contents.split('\n', &startOfLine); + line.trim(); + lineNo++; + + // skip empty lines and lines beginning with '#' + if (line.isEmpty() || line.at(0) == '#') + continue; + + if (line.at(0) == '[') { // found a group + groupOptionImmutable = fileOptionImmutable; + + QByteArray newGroup; + int start = 1, end; + do { + end = start; + for (;;) { + if (end == line.length()) { + qWarning() << warningProlog(file, lineNo) << "Invalid group header."; + // XXX maybe reset the current group here? + goto next_line; + } + if (line.at(end) == ']') + break; + end++; + } + if (end + 1 == line.length() && start + 2 == end && + line.at(start) == '$' && line.at(start + 1) == 'i') + { + if (newGroup.isEmpty()) + fileOptionImmutable = !kde_kiosk_exception; + else + groupOptionImmutable = !kde_kiosk_exception; + } + else { + if (!newGroup.isEmpty()) + newGroup += '\x1d'; + BufferFragment namePart=line.mid(start, end - start); + printableToString(&namePart, file, lineNo); + newGroup += namePart.toByteArray(); + } + } while ((start = end + 2) <= line.length() && line.at(end + 1) == '['); + currentGroup = newGroup; + + groupSkip = entryMap.getEntryOption(currentGroup, 0, 0, KEntryMap::EntryImmutable); + + if (groupSkip && !bDefault) + continue; + + if (groupOptionImmutable) + // Do not make the groups immutable until the entries from + // this file have been added. + immutableGroups.append(currentGroup); + } else { + if (groupSkip && !bDefault) + continue; // skip entry + + BufferFragment aKey; + int eqpos = line.indexOf('='); + if (eqpos < 0) { + aKey = line; + line.clear(); + } else { + BufferFragment temp = line.left(eqpos); + temp.trim(); + aKey = temp; + line.truncateLeft(eqpos + 1); + } + if (aKey.isEmpty()) { + qWarning() << warningProlog(file, lineNo) << "Invalid entry (empty key)"; + continue; + } + + KEntryMap::EntryOptions entryOptions=0; + if (groupOptionImmutable) + entryOptions |= KEntryMap::EntryImmutable; + + BufferFragment locale; + int start; + while ((start = aKey.lastIndexOf('[')) >= 0) { + int end = aKey.indexOf(']', start); + if (end < 0) { + qWarning() << warningProlog(file, lineNo) + << "Invalid entry (missing ']')"; + goto next_line; + } else if (end > start + 1 && aKey.at(start + 1) == '$') { // found option(s) + int i = start + 2; + while (i < end) { + switch (aKey.at(i)) { + case 'i': + if (!kde_kiosk_exception) + entryOptions |= KEntryMap::EntryImmutable; + break; + case 'e': + if (allowExecutableValues) + entryOptions |= KEntryMap::EntryExpansion; + break; + case 'd': + entryOptions |= KEntryMap::EntryDeleted; + aKey = aKey.left(start); + printableToString(&aKey, file, lineNo); + entryMap.setEntry(currentGroup, aKey.toByteArray(), QByteArray(), entryOptions); + goto next_line; + default: + break; + } + i++; + } + } else { // found a locale + if (!locale.isNull()) { + qWarning() << warningProlog(file, lineNo) + << "Invalid entry (second locale!?)"; + goto next_line; + } + + locale = aKey.mid(start + 1,end - start - 1); + } + aKey.truncate(start); + } + if (eqpos < 0) { // Do this here after [$d] was checked + qWarning() << warningProlog(file, lineNo) << "Invalid entry (missing '=')"; + continue; + } + printableToString(&aKey, file, lineNo); + if (!locale.isEmpty()) { + if (locale != currentLocale) { + // backward compatibility. C == en_US + if (locale.at(0) != 'C' || currentLocale != "en_US") { + if (merging) + entryOptions |= KEntryMap::EntryRawKey; + else + goto next_line; // skip this entry if we're not merging + } + } + } + + if (!(entryOptions & KEntryMap::EntryRawKey)) + printableToString(&aKey, file, lineNo); + + if (options&ParseGlobal) + entryOptions |= KEntryMap::EntryGlobal; + if (bDefault) + entryOptions |= KEntryMap::EntryDefault; + if (!locale.isNull()) + entryOptions |= KEntryMap::EntryLocalized; + printableToString(&line, file, lineNo); + if (entryOptions & KEntryMap::EntryRawKey) { + QByteArray rawKey; + rawKey.reserve(aKey.length() + locale.length() + 2); + rawKey.append(aKey.toVolatileByteArray()); + rawKey.append('[').append(locale.toVolatileByteArray()).append(']'); + entryMap.setEntry(currentGroup, rawKey, line.toByteArray(), entryOptions); + } else { + entryMap.setEntry(currentGroup, aKey.toByteArray(), line.toByteArray(), entryOptions); + } + } +next_line: + continue; + } + + // now make sure immutable groups are marked immutable + Q_FOREACH(const QByteArray& group, immutableGroups) { + entryMap.setEntry(group, QByteArray(), QByteArray(), KEntryMap::EntryImmutable); + } + + return fileOptionImmutable ? ParseImmutable : ParseOk; +} + +void KConfigIniBackend::writeEntries(const QByteArray& locale, QIODevice& file, + const KEntryMap& map, bool defaultGroup, bool &firstEntry) +{ + QByteArray currentGroup; + bool groupIsImmutable = false; + const KEntryMapConstIterator end = map.constEnd(); + for (KEntryMapConstIterator it = map.constBegin(); it != end; ++it) { + const KEntryKey& key = it.key(); + + // Either process the default group or all others + if ((key.mGroup != "<default>") == defaultGroup) + continue; // skip + + // the only thing we care about groups is, is it immutable? + if (key.mKey.isNull()) { + groupIsImmutable = it->bImmutable; + continue; // skip + } + + const KEntry& currentEntry = *it; + if (!defaultGroup && currentGroup != key.mGroup) { + if (!firstEntry) + file.putChar('\n'); + currentGroup = key.mGroup; + for (int start = 0, end;; start = end + 1) { + file.putChar('['); + end = currentGroup.indexOf('\x1d', start); + if (end < 0) { + int cgl = currentGroup.length(); + if (currentGroup.at(start) == '$' && cgl - start <= 10) { + for (int i = start + 1; i < cgl; i++) { + char c = currentGroup.at(i); + if (c < 'a' || c > 'z') + goto nope; + } + file.write("\\x24"); + start++; + } + nope: + file.write(stringToPrintable(currentGroup.mid(start), GroupString)); + file.putChar(']'); + if (groupIsImmutable) { + file.write("[$i]", 4); + } + file.putChar('\n'); + break; + } else { + file.write(stringToPrintable(currentGroup.mid(start, end - start), GroupString)); + file.putChar(']'); + } + } + } + + firstEntry = false; + // it is data for a group + + if (key.bRaw) // unprocessed key with attached locale from merge + file.write(key.mKey); + else { + file.write(stringToPrintable(key.mKey, KeyString)); // Key + if (key.bLocal && locale != "C") { // 'C' locale == untranslated + file.putChar('['); + file.write(locale); // locale tag + file.putChar(']'); + } + } + if (currentEntry.bDeleted) { + if (currentEntry.bImmutable) + file.write("[$di]", 5); // Deleted + immutable + else + file.write("[$d]", 4); // Deleted + } else { + if (currentEntry.bImmutable || currentEntry.bExpand) { + file.write("[$", 2); + if (currentEntry.bImmutable) + file.putChar('i'); + if (currentEntry.bExpand) + file.putChar('e'); + file.putChar(']'); + } + file.putChar('='); + file.write(stringToPrintable(currentEntry.mValue, ValueString)); + } + file.putChar('\n'); + } +} + +void KConfigIniBackend::writeEntries(const QByteArray& locale, QIODevice& file, const KEntryMap& map) +{ + bool firstEntry = true; + + // write default group + writeEntries(locale, file, map, true, firstEntry); + + // write all other groups + writeEntries(locale, file, map, false, firstEntry); +} + +bool KConfigIniBackend::writeConfig(const QByteArray& locale, KEntryMap& entryMap, + WriteOptions options) +{ + Q_ASSERT(!filePath().isEmpty()); + + KEntryMap writeMap; + const bool bGlobal = options & WriteGlobal; + + // First, reparse the file on disk, to merge our changes with the ones done by other apps + // Store the result into writeMap. + { + ParseOptions opts = ParseExpansions; + if (bGlobal) + opts |= ParseGlobal; + ParseInfo info = parseConfig(locale, writeMap, opts, true); + if (info != ParseOk) // either there was an error or the file became immutable + return false; + } + + const KEntryMapIterator end = entryMap.end(); + for (KEntryMapIterator it=entryMap.begin(); it != end; ++it) { + if (!it.key().mKey.isEmpty() && !it->bDirty) // not dirty, doesn't overwrite entry in writeMap. skips default entries, too. + continue; + + const KEntryKey& key = it.key(); + + // only write entries that have the same "globality" as the file + if (it->bGlobal == bGlobal) { + if (it->bReverted) { + writeMap.remove(key); + } else if (!it->bDeleted) { + writeMap[key] = *it; + } else { + KEntryKey defaultKey = key; + defaultKey.bDefault = true; + if (!entryMap.contains(defaultKey)) { + writeMap.remove(key); // remove the deleted entry if there is no default + //qDebug() << "Detected as deleted=>removed:" << key.mGroup << key.mKey << "global=" << bGlobal; + } else { + writeMap[key] = *it; // otherwise write an explicitly deleted entry + //qDebug() << "Detected as deleted=>[$d]:" << key.mGroup << key.mKey << "global=" << bGlobal; + } + } + it->bDirty = false; + } + } + + // now writeMap should contain only entries to be written + // so write it out to disk + + // check if file exists + QFile::Permissions fileMode = QFile::ReadUser | QFile::WriteUser; + bool createNew = true; + + QFileInfo fi(filePath()); + if (fi.exists()) + { +#ifdef Q_OS_WIN + //TODO: getuid does not exist on windows, use GetSecurityInfo and GetTokenInformation instead + createNew = false; +#else + if (fi.ownerId() == ::getuid()) + { + // Preserve file mode if file exists and is owned by user. + fileMode = fi.permissions(); + } + else + { + // File is not owned by user: + // Don't create new file but write to existing file instead. + createNew = false; + } +#endif + } + + if (createNew) { + QSaveFile file(filePath()); + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + + file.setTextModeEnabled(true); // to get eol translation + writeEntries(locale, file, writeMap); + + if (!file.size() && (fileMode == (QFile::ReadUser | QFile::WriteUser))) { + // File is empty and doesn't have special permissions: delete it. + file.cancelWriting(); + + if (fi.exists()) { + // also remove the old file in case it existed. this can happen + // when we delete all the entries in an existing config file. + // if we don't do this, then deletions and revertToDefault's + // will mysteriously fail + QFile::remove(filePath()); + } + } else { + // Normal case: Close the file + if (file.commit()) { + QFile::setPermissions(filePath(), fileMode); + return true; + } + // Couldn't write. Disk full? + qWarning() << "Couldn't write" << filePath() << ". Disk full?"; + return false; + } + } else { + // Open existing file. *DON'T* create it if it suddenly does not exist! +#ifdef Q_OS_UNIX + int fd = QT_OPEN(QFile::encodeName(filePath()).constData(), O_WRONLY | O_TRUNC); + if (fd < 0) { + return false; + } + FILE *fp = ::fdopen(fd, "w"); + if (!fp) { + QT_CLOSE(fd); + return false; + } + QFile f; + if (!f.open(fp, QIODevice::WriteOnly)) { + fclose(fp); + return false; + } + writeEntries(locale, f, writeMap); + f.close(); + fclose(fp); +#else + QFile f( filePath() ); + // XXX This is broken - it DOES create the file if it is suddenly gone. + if (!f.open( QIODevice::WriteOnly | QIODevice::Truncate )) { + return false; + } + f.setTextModeEnabled(true); + writeEntries(locale, f, writeMap); +#endif + } + return true; +} + + +bool KConfigIniBackend::isWritable() const +{ + const QString filePath = this->filePath(); + if (!filePath.isEmpty()) { + QFileInfo file(filePath); + if (!file.exists()) { + // If the file does not exist, check if the deepest + // existing dir is writable. + QFileInfo dir(file.absolutePath()); + while (!dir.exists()) { + QString parent = dir.absolutePath(); // Go up. Can't use cdUp() on non-existing dirs. + if (parent == dir.filePath()) { + // no parent + return false; + } + dir.setFile(parent); + } + return dir.isDir() && dir.isWritable(); + } else { + return file.isWritable(); + } + } + + return false; +} + +QString KConfigIniBackend::nonWritableErrorMessage() const +{ + return tr("Configuration file \"%1\" not writable.\n").arg(filePath()); +} + +void KConfigIniBackend::createEnclosing() +{ + const QString file = filePath(); + if (file.isEmpty()) + return; // nothing to do + + // Create the containing dir, maybe it wasn't there + QDir dir; + dir.mkpath(QFileInfo(file).absolutePath()); +} + +void KConfigIniBackend::setFilePath(const QString& file) +{ + if (file.isEmpty()) + return; + + Q_ASSERT(QDir::isAbsolutePath(file)); + + const QFileInfo info(file); + if (info.exists()) { + setLocalFilePath(info.canonicalFilePath()); + setLastModified(info.lastModified()); + setSize(info.size()); + } else { + setLocalFilePath(file); + setSize(0); + QDateTime dummy; + dummy.setTime_t(0); + setLastModified(dummy); + } +} + +KConfigBase::AccessMode KConfigIniBackend::accessMode() const +{ + if (filePath().isEmpty()) + return KConfigBase::NoAccess; + + if (isWritable()) + return KConfigBase::ReadWrite; + + return KConfigBase::ReadOnly; +} + +bool KConfigIniBackend::lock() +{ + Q_ASSERT(!filePath().isEmpty()); + + if (!lockFile) { + lockFile = new QLockFile(filePath() + QLatin1String(".lock")); + } + + // This is a workaround for current QLockFilePrivate::tryLock_sys + // which might crash calling qAppName() if sync() is called after + // the QCoreApplication instance is gone. It might be the case with + // KSharedConfig instances cleanup. + if (!lockFile->tryLock(lockFile->staleLockTime())) { + lockFile->removeStaleLockFile(); + lockFile->lock(); + } + return lockFile->isLocked(); +} + +void KConfigIniBackend::unlock() +{ + lockFile->unlock(); + delete lockFile; + lockFile = NULL; +} + +bool KConfigIniBackend::isLocked() const +{ + return lockFile && lockFile->isLocked(); +} + +QByteArray KConfigIniBackend::stringToPrintable(const QByteArray& aString, StringType type) +{ + static const char nibbleLookup[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + if (aString.isEmpty()) + return aString; + const int l = aString.length(); + + QByteArray result; // Guesstimated that it's good to avoid data() initialization for a length of l*4 + result.resize(l * 4); // Maximum 4x as long as source string due to \x<ab> escape sequences + register const char *s = aString.constData(); + int i = 0; + char *data = result.data(); + char *start = data; + + // Protect leading space + if (s[0] == ' ' && type != GroupString) { + *data++ = '\\'; + *data++ = 's'; + i++; + } + + for (; i < l; ++i/*, r++*/) { + switch (s[i]) { + default: + // The \n, \t, \r cases (all < 32) are handled below; we can ignore them here + if (((unsigned char)s[i]) < 32) + goto doEscape; + *data++ = s[i]; + break; + case '\n': + *data++ = '\\'; + *data++ = 'n'; + break; + case '\t': + *data++ = '\\'; + *data++ = 't'; + break; + case '\r': + *data++ = '\\'; + *data++ = 'r'; + break; + case '\\': + *data++ = '\\'; + *data++ = '\\'; + break; + case '=': + if (type != KeyString) { + *data++ = s[i]; + break; + } + goto doEscape; + case '[': + case ']': + // Above chars are OK to put in *value* strings as plaintext + if (type == ValueString) { + *data++ = s[i]; + break; + } + doEscape: + *data++ = '\\'; + *data++ = 'x'; + *data++ = nibbleLookup[((unsigned char)s[i]) >> 4]; + *data++ = nibbleLookup[((unsigned char)s[i]) & 0x0f]; + break; + } + } + *data = 0; + result.resize(data - start); + + // Protect trailing space + if (result.endsWith(' ') && type != GroupString) { + result.replace(result.length() - 1, 1, "\\s"); + } + result.squeeze(); + + return result; +} + +char KConfigIniBackend::charFromHex(const char *str, const QFile& file, int line) +{ + unsigned char ret = 0; + for (int i = 0; i < 2; i++) { + ret <<= 4; + quint8 c = quint8(str[i]); + + if (c >= '0' && c <= '9') { + ret |= c - '0'; + } else if (c >= 'a' && c <= 'f') { + ret |= c - 'a' + 0x0a; + } else if (c >= 'A' && c <= 'F') { + ret |= c - 'A' + 0x0a; + } else { + QByteArray e(str, 2); + e.prepend("\\x"); + qWarning() << warningProlog(file, line) << "Invalid hex character " << c + << " in \\x<nn>-type escape sequence \"" << e.constData() << "\"."; + return 'x'; + } + } + return char(ret); +} + +void KConfigIniBackend::printableToString(BufferFragment* aString, const QFile& file, int line) +{ + if (aString->isEmpty() || aString->indexOf('\\')==-1) + return; + aString->trim(); + int l = aString->length(); + char *r = aString->data(); + char *str=r; + + for(int i = 0; i < l; i++, r++) { + if (str[i]!= '\\') { + *r=str[i]; + } else { + // Probable escape sequence + i++; + if (i >= l) { // Line ends after backslash - stop. + *r = '\\'; + break; + } + + switch(str[i]) { + case 's': + *r = ' '; + break; + case 't': + *r = '\t'; + break; + case 'n': + *r = '\n'; + break; + case 'r': + *r = '\r'; + break; + case '\\': + *r = '\\'; + break; + case 'x': + if (i + 2 < l) { + *r = charFromHex(str + i + 1, file, line); + i += 2; + } else { + *r = 'x'; + i = l - 1; + } + break; + default: + *r = '\\'; + qWarning() << warningProlog(file, line) + << QString::fromLatin1("Invalid escape sequence \"\\%1\".").arg(str[i]); + } + } + } + aString->truncate(r - aString->constData()); +} diff --git a/src/core/kconfigini_p.h b/src/core/kconfigini_p.h new file mode 100644 index 00000000..368a78fb --- /dev/null +++ b/src/core/kconfigini_p.h @@ -0,0 +1,81 @@ +/* + This file is part of the KDE libraries + Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Portions copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCONFIGINI_P_H +#define KCONFIGINI_P_H + +#include <kconfigcore_export.h> +#include <kconfigbackend.h> + +class QLockFile; +class QIODevice; + +class KConfigIniBackend : public KConfigBackend +{ +private: + class BufferFragment; + + QLockFile *lockFile; +public: + + KConfigIniBackend(); + ~KConfigIniBackend(); + + ParseInfo parseConfig(const QByteArray& locale, + KEntryMap& entryMap, + ParseOptions options); + ParseInfo parseConfig(const QByteArray& locale, + KEntryMap& entryMap, + ParseOptions options, + bool merging); + bool writeConfig(const QByteArray& locale, KEntryMap& entryMap, + WriteOptions options); + + bool isWritable() const; + QString nonWritableErrorMessage() const; + KConfigBase::AccessMode accessMode() const; + void createEnclosing(); + void setFilePath(const QString& path); + bool lock(); + void unlock(); + bool isLocked() const; + +protected: + + enum StringType { + GroupString = 0, + KeyString = 1, + ValueString = 2 + }; + // Warning: this modifies data in-place. Other BufferFragment objects referencing the same buffer + // fragment will get their data modified too. + static void printableToString(BufferFragment* aString, const QFile& file, int line); + static QByteArray stringToPrintable(const QByteArray& aString, StringType type); + static char charFromHex(const char *str, const QFile& file, int line); + static QString warningProlog(const QFile& file, int line); + + void writeEntries(const QByteArray& locale, QIODevice& file, const KEntryMap& map); + void writeEntries(const QByteArray& locale, QIODevice& file, const KEntryMap& map, + bool defaultGroup, bool &firstEntry); +}; + +#endif // KCONFIGINI_P_H diff --git a/src/core/kcoreconfigskeleton.cpp b/src/core/kcoreconfigskeleton.cpp new file mode 100644 index 00000000..691e0b54 --- /dev/null +++ b/src/core/kcoreconfigskeleton.cpp @@ -0,0 +1,1343 @@ +/* + This file is part of KOrganizer. + Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2003 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kcoreconfigskeleton.h" +#include "kcoreconfigskeleton_p.h" + +#include <QUrl> + + +static QString obscuredString(const QString &str) +{ + QString result; + const QChar *unicode = str.unicode(); + for ( int i = 0; i < str.length(); ++i ) + // yes, no typo. can't encode ' ' or '!' because + // they're the unicode BOM. stupid scrambling. stupid. + result += ( unicode[ i ].unicode() <= 0x21 ) ? unicode[ i ] + : QChar( 0x1001F - unicode[ i ].unicode() ); + + return result; +} + +KConfigSkeletonItem::KConfigSkeletonItem(const QString & _group, + const QString & _key) + : mGroup(_group) + , mKey(_key) + , d( new KConfigSkeletonItemPrivate ) +{ +} + +KConfigSkeletonItem::~KConfigSkeletonItem() +{ + delete d; +} + +void KConfigSkeletonItem::setGroup( const QString &_group ) +{ + mGroup = _group; +} + +QString KConfigSkeletonItem::group() const +{ + return mGroup; +} + +void KConfigSkeletonItem::setKey( const QString &_key ) +{ + mKey = _key; +} + +QString KConfigSkeletonItem::key() const +{ + return mKey; +} + +void KConfigSkeletonItem::setName(const QString &_name) +{ + mName = _name; +} + +QString KConfigSkeletonItem::name() const +{ + return mName; +} + +void KConfigSkeletonItem::setLabel( const QString &l ) +{ + d->mLabel = l; +} + +QString KConfigSkeletonItem::label() const +{ + return d->mLabel; +} + +void KConfigSkeletonItem::setToolTip( const QString &t ) +{ + d->mToolTip = t; +} + +QString KConfigSkeletonItem::toolTip() const +{ + return d->mToolTip; +} + +void KConfigSkeletonItem::setWhatsThis( const QString &w ) +{ + d->mWhatsThis = w; +} + +QString KConfigSkeletonItem::whatsThis() const +{ + return d->mWhatsThis; +} + +QVariant KConfigSkeletonItem::minValue() const +{ + return QVariant(); +} + +QVariant KConfigSkeletonItem::maxValue() const +{ + return QVariant(); +} + +bool KConfigSkeletonItem::isImmutable() const +{ + return d->mIsImmutable; +} + +void KConfigSkeletonItem::readImmutability( const KConfigGroup &group ) +{ + d->mIsImmutable = group.isEntryImmutable( mKey ); +} + + +KCoreConfigSkeleton::ItemString::ItemString( const QString &_group, const QString &_key, + QString &reference, + const QString &defaultValue, + Type type ) + : KConfigSkeletonGenericItem<QString>( _group, _key, reference, defaultValue ), + mType( type ) +{ +} + +void KCoreConfigSkeleton::ItemString::writeConfig( KConfig *config ) +{ + if ( mReference != mLoadedValue ) // WABA: Is this test needed? + { + KConfigGroup cg(config, mGroup ); + if ((mDefault == mReference) && !cg.hasDefault( mKey)) + cg.revertToDefault( mKey ); + else if ( mType == Path ) + cg.writePathEntry( mKey, mReference ); + else if ( mType == Password ) + cg.writeEntry( mKey, obscuredString( mReference ) ); + else + cg.writeEntry( mKey, mReference ); + } +} + + +void KCoreConfigSkeleton::ItemString::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + + if ( mType == Path ) + { + mReference = cg.readPathEntry( mKey, mDefault ); + } + else if ( mType == Password ) + { + QString val = cg.readEntry( mKey, obscuredString( mDefault ) ); + mReference = obscuredString( val ); + } + else + { + mReference = cg.readEntry( mKey, mDefault ); + } + + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemString::setProperty(const QVariant & p) +{ + mReference = p.toString(); +} + +bool KCoreConfigSkeleton::ItemString::isEqual(const QVariant &v) const +{ + return mReference == v.toString(); +} + +QVariant KCoreConfigSkeleton::ItemString::property() const +{ + return QVariant(mReference); +} + +KCoreConfigSkeleton::ItemPassword::ItemPassword( const QString &_group, const QString &_key, + QString &reference, + const QString &defaultValue) + : ItemString( _group, _key, reference, defaultValue, Password ) +{ +} + +KCoreConfigSkeleton::ItemPath::ItemPath( const QString &_group, const QString &_key, + QString &reference, + const QString &defaultValue) + : ItemString( _group, _key, reference, defaultValue, Path ) +{ +} + +KCoreConfigSkeleton::ItemUrl::ItemUrl( const QString &_group, const QString &_key, + QUrl &reference, + const QUrl &defaultValue ) + : KConfigSkeletonGenericItem<QUrl>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemUrl::writeConfig( KConfig *config ) +{ + if ( mReference != mLoadedValue ) // WABA: Is this test needed? + { + KConfigGroup cg(config, mGroup ); + if ((mDefault == mReference) && !cg.hasDefault( mKey)) + cg.revertToDefault( mKey ); + else + cg.writeEntry<QString>( mKey, mReference.toString() ); + } +} + +void KCoreConfigSkeleton::ItemUrl::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + + mReference = QUrl( cg.readEntry<QString>( mKey, mDefault.toString() ) ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemUrl::setProperty(const QVariant & p) +{ + mReference = qvariant_cast<QUrl>(p); +} + +bool KCoreConfigSkeleton::ItemUrl::isEqual(const QVariant &v) const +{ + return mReference == qvariant_cast<QUrl>(v); +} + +QVariant KCoreConfigSkeleton::ItemUrl::property() const +{ + return qVariantFromValue<QUrl>(mReference); +} + +KCoreConfigSkeleton::ItemProperty::ItemProperty( const QString &_group, + const QString &_key, + QVariant &reference, + const QVariant &defaultValue ) + : KConfigSkeletonGenericItem<QVariant>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemProperty::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemProperty::setProperty(const QVariant & p) +{ + mReference = p; +} + +bool KCoreConfigSkeleton::ItemProperty::isEqual(const QVariant &v) const +{ + //this might cause problems if the QVariants are not of default types + return mReference == v; +} + +QVariant KCoreConfigSkeleton::ItemProperty::property() const +{ + return mReference; +} + +KCoreConfigSkeleton::ItemBool::ItemBool( const QString &_group, const QString &_key, + bool &reference, bool defaultValue ) + : KConfigSkeletonGenericItem<bool>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemBool::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemBool::setProperty(const QVariant & p) +{ + mReference = p.toBool(); +} + +bool KCoreConfigSkeleton::ItemBool::isEqual(const QVariant &v) const +{ + return mReference == v.toBool(); +} + +QVariant KCoreConfigSkeleton::ItemBool::property() const +{ + return QVariant( mReference ); +} + + +KCoreConfigSkeleton::ItemInt::ItemInt( const QString &_group, const QString &_key, + qint32 &reference, qint32 defaultValue ) + : KConfigSkeletonGenericItem<qint32>( _group, _key, reference, defaultValue ) + ,mHasMin(false), mHasMax(false) +{ +} + +void KCoreConfigSkeleton::ItemInt::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + if (mHasMin) + mReference = qMax(mReference, mMin); + if (mHasMax) + mReference = qMin(mReference, mMax); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemInt::setProperty(const QVariant & p) +{ + mReference = p.toInt(); +} + +bool KCoreConfigSkeleton::ItemInt::isEqual(const QVariant &v) const +{ + return mReference == v.toInt(); +} + +QVariant KCoreConfigSkeleton::ItemInt::property() const +{ + return QVariant(mReference); +} + +QVariant KCoreConfigSkeleton::ItemInt::minValue() const +{ + if (mHasMin) + return QVariant(mMin); + return QVariant(); +} + +QVariant KCoreConfigSkeleton::ItemInt::maxValue() const +{ + if (mHasMax) + return QVariant(mMax); + return QVariant(); +} + +void KCoreConfigSkeleton::ItemInt::setMinValue(qint32 v) +{ + mHasMin = true; + mMin = v; +} + +void KCoreConfigSkeleton::ItemInt::setMaxValue(qint32 v) +{ + mHasMax = true; + mMax = v; +} + + +KCoreConfigSkeleton::ItemLongLong::ItemLongLong( const QString &_group, const QString &_key, + qint64 &reference, qint64 defaultValue ) + : KConfigSkeletonGenericItem<qint64>( _group, _key, reference, defaultValue ) + ,mHasMin(false), mHasMax(false) +{ +} + +void KCoreConfigSkeleton::ItemLongLong::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + if (mHasMin) + mReference = qMax(mReference, mMin); + if (mHasMax) + mReference = qMin(mReference, mMax); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemLongLong::setProperty(const QVariant & p) +{ + mReference = p.toLongLong(); +} + +bool KCoreConfigSkeleton::ItemLongLong::isEqual(const QVariant &v) const +{ + return mReference == v.toLongLong(); +} + +QVariant KCoreConfigSkeleton::ItemLongLong::property() const +{ + return QVariant(mReference); +} + +QVariant KCoreConfigSkeleton::ItemLongLong::minValue() const +{ + if (mHasMin) + return QVariant(mMin); + return QVariant(); +} + +QVariant KCoreConfigSkeleton::ItemLongLong::maxValue() const +{ + if (mHasMax) + return QVariant(mMax); + return QVariant(); +} + +void KCoreConfigSkeleton::ItemLongLong::setMinValue(qint64 v) +{ + mHasMin = true; + mMin = v; +} + +void KCoreConfigSkeleton::ItemLongLong::setMaxValue(qint64 v) +{ + mHasMax = true; + mMax = v; +} + +KCoreConfigSkeleton::ItemEnum::ItemEnum( const QString &_group, const QString &_key, + qint32 &reference, + const QList<Choice> &choices, + qint32 defaultValue ) + : ItemInt( _group, _key, reference, defaultValue ), mChoices(choices) +{ +} + +void KCoreConfigSkeleton::ItemEnum::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + if (!cg.hasKey(mKey)) + { + mReference = mDefault; + } + else + { + int i = 0; + mReference = -1; + QString tmp = cg.readEntry( mKey, QString() ).toLower(); + for(QList<Choice>::ConstIterator it = mChoices.constBegin(); + it != mChoices.constEnd(); ++it, ++i) + { + if ((*it).name.toLower() == tmp) + { + mReference = i; + break; + } + } + if (mReference == -1) + mReference = cg.readEntry( mKey, mDefault ); + } + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemEnum::writeConfig( KConfig *config ) +{ + if ( mReference != mLoadedValue ) // WABA: Is this test needed? + { + KConfigGroup cg(config, mGroup ); + if ((mDefault == mReference) && !cg.hasDefault( mKey)) + cg.revertToDefault( mKey ); + else if ((mReference >= 0) && (mReference < (int) mChoices.count())) + cg.writeEntry( mKey, mChoices[mReference].name ); + else + cg.writeEntry( mKey, mReference ); + } +} + +QList<KCoreConfigSkeleton::ItemEnum::Choice> KCoreConfigSkeleton::ItemEnum::choices() const +{ + return mChoices; +} + +QList<KCoreConfigSkeleton::ItemEnum::Choice> KCoreConfigSkeleton::ItemEnum::choices2() const +{ + return mChoices; +} + +KCoreConfigSkeleton::ItemUInt::ItemUInt( const QString &_group, const QString &_key, + quint32 &reference, + quint32 defaultValue ) + : KConfigSkeletonGenericItem<quint32>( _group, _key, reference, defaultValue ) + ,mHasMin(false), mHasMax(false) +{ +} + +void KCoreConfigSkeleton::ItemUInt::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + if (mHasMin) + mReference = qMax(mReference, mMin); + if (mHasMax) + mReference = qMin(mReference, mMax); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemUInt::setProperty(const QVariant & p) +{ + mReference = p.toUInt(); +} + +bool KCoreConfigSkeleton::ItemUInt::isEqual(const QVariant &v) const +{ + return mReference == v.toUInt(); +} + +QVariant KCoreConfigSkeleton::ItemUInt::property() const +{ + return QVariant(mReference); +} + +QVariant KCoreConfigSkeleton::ItemUInt::minValue() const +{ + if (mHasMin) + return QVariant(mMin); + return QVariant(); +} + +QVariant KCoreConfigSkeleton::ItemUInt::maxValue() const +{ + if (mHasMax) + return QVariant(mMax); + return QVariant(); +} + +void KCoreConfigSkeleton::ItemUInt::setMinValue(quint32 v) +{ + mHasMin = true; + mMin = v; +} + +void KCoreConfigSkeleton::ItemUInt::setMaxValue(quint32 v) +{ + mHasMax = true; + mMax = v; +} + + +KCoreConfigSkeleton::ItemULongLong::ItemULongLong( const QString &_group, const QString &_key, + quint64 &reference, quint64 defaultValue ) + : KConfigSkeletonGenericItem<quint64>( _group, _key, reference, defaultValue ) + ,mHasMin(false), mHasMax(false) +{ +} + +void KCoreConfigSkeleton::ItemULongLong::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + if (mHasMin) + mReference = qMax(mReference, mMin); + if (mHasMax) + mReference = qMin(mReference, mMax); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemULongLong::setProperty(const QVariant & p) +{ + mReference = p.toULongLong(); +} + +bool KCoreConfigSkeleton::ItemULongLong::isEqual(const QVariant &v) const +{ + return mReference == v.toULongLong(); +} + +QVariant KCoreConfigSkeleton::ItemULongLong::property() const +{ + return QVariant(mReference); +} + +QVariant KCoreConfigSkeleton::ItemULongLong::minValue() const +{ + if (mHasMin) + return QVariant(mMin); + return QVariant(); +} + +QVariant KCoreConfigSkeleton::ItemULongLong::maxValue() const +{ + if (mHasMax) + return QVariant(mMax); + return QVariant(); +} + +void KCoreConfigSkeleton::ItemULongLong::setMinValue(quint64 v) +{ + mHasMin = true; + mMin = v; +} + +void KCoreConfigSkeleton::ItemULongLong::setMaxValue(quint64 v) +{ + mHasMax = true; + mMax = v; +} + +KCoreConfigSkeleton::ItemDouble::ItemDouble( const QString &_group, const QString &_key, + double &reference, double defaultValue ) + : KConfigSkeletonGenericItem<double>( _group, _key, reference, defaultValue ) + ,mHasMin(false), mHasMax(false) +{ +} + +void KCoreConfigSkeleton::ItemDouble::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + if (mHasMin) + mReference = qMax(mReference, mMin); + if (mHasMax) + mReference = qMin(mReference, mMax); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemDouble::setProperty(const QVariant & p) +{ + mReference = p.toDouble(); +} + +bool KCoreConfigSkeleton::ItemDouble::isEqual(const QVariant &v) const +{ + return mReference == v.toDouble(); +} + +QVariant KCoreConfigSkeleton::ItemDouble::property() const +{ + return QVariant(mReference); +} + +QVariant KCoreConfigSkeleton::ItemDouble::minValue() const +{ + if (mHasMin) + return QVariant(mMin); + return QVariant(); +} + +QVariant KCoreConfigSkeleton::ItemDouble::maxValue() const +{ + if (mHasMax) + return QVariant(mMax); + return QVariant(); +} + +void KCoreConfigSkeleton::ItemDouble::setMinValue(double v) +{ + mHasMin = true; + mMin = v; +} + +void KCoreConfigSkeleton::ItemDouble::setMaxValue(double v) +{ + mHasMax = true; + mMax = v; +} + + +KCoreConfigSkeleton::ItemRect::ItemRect( const QString &_group, const QString &_key, + QRect &reference, + const QRect &defaultValue ) + : KConfigSkeletonGenericItem<QRect>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemRect::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemRect::setProperty(const QVariant & p) +{ + mReference = p.toRect(); +} + +bool KCoreConfigSkeleton::ItemRect::isEqual(const QVariant &v) const +{ + return mReference == v.toRect(); +} + +QVariant KCoreConfigSkeleton::ItemRect::property() const +{ + return QVariant(mReference); +} + + +KCoreConfigSkeleton::ItemPoint::ItemPoint( const QString &_group, const QString &_key, + QPoint &reference, + const QPoint &defaultValue ) + : KConfigSkeletonGenericItem<QPoint>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemPoint::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemPoint::setProperty(const QVariant & p) +{ + mReference = p.toPoint(); +} + +bool KCoreConfigSkeleton::ItemPoint::isEqual(const QVariant &v) const +{ + return mReference == v.toPoint(); +} + +QVariant KCoreConfigSkeleton::ItemPoint::property() const +{ + return QVariant(mReference); +} + + +KCoreConfigSkeleton::ItemSize::ItemSize( const QString &_group, const QString &_key, + QSize &reference, + const QSize &defaultValue ) + : KConfigSkeletonGenericItem<QSize>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemSize::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemSize::setProperty(const QVariant & p) +{ + mReference = p.toSize(); +} + +bool KCoreConfigSkeleton::ItemSize::isEqual(const QVariant &v) const +{ + return mReference == v.toSize(); +} + +QVariant KCoreConfigSkeleton::ItemSize::property() const +{ + return QVariant(mReference); +} + + +KCoreConfigSkeleton::ItemDateTime::ItemDateTime( const QString &_group, const QString &_key, + QDateTime &reference, + const QDateTime &defaultValue ) + : KConfigSkeletonGenericItem<QDateTime>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemDateTime::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + mReference = cg.readEntry( mKey, mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemDateTime::setProperty(const QVariant & p) +{ + mReference = p.toDateTime(); +} + +bool KCoreConfigSkeleton::ItemDateTime::isEqual(const QVariant &v) const +{ + return mReference == v.toDateTime(); +} + +QVariant KCoreConfigSkeleton::ItemDateTime::property() const +{ + return QVariant(mReference); +} + + +KCoreConfigSkeleton::ItemStringList::ItemStringList( const QString &_group, const QString &_key, + QStringList &reference, + const QStringList &defaultValue ) + : KConfigSkeletonGenericItem<QStringList>( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemStringList::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + if ( !cg.hasKey( mKey ) ) + mReference = mDefault; + else + mReference = cg.readEntry( mKey, mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemStringList::setProperty(const QVariant & p) +{ + mReference = p.toStringList(); +} + +bool KCoreConfigSkeleton::ItemStringList::isEqual(const QVariant &v) const +{ + return mReference == v.toStringList(); +} + +QVariant KCoreConfigSkeleton::ItemStringList::property() const +{ + return QVariant(mReference); +} + + +KCoreConfigSkeleton::ItemPathList::ItemPathList( const QString &_group, const QString &_key, + QStringList &reference, + const QStringList &defaultValue ) + : ItemStringList( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemPathList::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + if ( !cg.hasKey( mKey ) ) + mReference = mDefault; + else + mReference = cg.readPathEntry( mKey, QStringList() ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemPathList::writeConfig( KConfig *config ) +{ + if ( mReference != mLoadedValue ) // WABA: Is this test needed? + { + KConfigGroup cg(config, mGroup ); + if ((mDefault == mReference) && !cg.hasDefault( mKey)) + cg.revertToDefault( mKey ); + else { + QStringList sl = mReference; + cg.writePathEntry( mKey, sl ); + } + } +} + +KCoreConfigSkeleton::ItemUrlList::ItemUrlList( const QString &_group, const QString &_key, + QList<QUrl> &reference, + const QList<QUrl> &defaultValue ) + : KConfigSkeletonGenericItem<QList<QUrl> >( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemUrlList::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + if ( !cg.hasKey( mKey ) ) + mReference = mDefault; + else { + QStringList strList; + Q_FOREACH (const QUrl& url, mDefault) { + strList.append(url.toString()); + } + mReference.clear(); + const QStringList readList = cg.readEntry<QStringList>(mKey, strList); + Q_FOREACH (const QString& str, readList) { + mReference.append(QUrl(str)); + } + } + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemUrlList::writeConfig( KConfig *config ) +{ + if ( mReference != mLoadedValue ) // WABA: Is this test needed? + { + KConfigGroup cg(config, mGroup ); + if ((mDefault == mReference) && !cg.hasDefault( mKey)) + cg.revertToDefault( mKey ); + else { + QStringList strList; + Q_FOREACH (const QUrl& url, mReference) { + strList.append(url.toString()); + } + cg.writeEntry<QStringList>(mKey, strList); + } + } +} + +void KCoreConfigSkeleton::ItemUrlList::setProperty(const QVariant & p) +{ + mReference = qvariant_cast<QList<QUrl> >(p); +} + +bool KCoreConfigSkeleton::ItemUrlList::isEqual(const QVariant &v) const +{ + return mReference == qvariant_cast<QList<QUrl> >(v); +} + +QVariant KCoreConfigSkeleton::ItemUrlList::property() const +{ + return qVariantFromValue<QList<QUrl> >(mReference); +} + + +KCoreConfigSkeleton::ItemIntList::ItemIntList( const QString &_group, const QString &_key, + QList<int> &reference, + const QList<int> &defaultValue ) + : KConfigSkeletonGenericItem<QList<int> >( _group, _key, reference, defaultValue ) +{ +} + +void KCoreConfigSkeleton::ItemIntList::readConfig( KConfig *config ) +{ + KConfigGroup cg(config, mGroup ); + if ( !cg.hasKey( mKey ) ) + mReference = mDefault; + else + mReference = cg.readEntry( mKey , mDefault ); + mLoadedValue = mReference; + + readImmutability( cg ); +} + +void KCoreConfigSkeleton::ItemIntList::setProperty(const QVariant &p) +{ + mReference = qvariant_cast< QList<int> >(p); +} + +bool KCoreConfigSkeleton::ItemIntList::isEqual(const QVariant &v) const +{ + return mReference == qvariant_cast< QList<int> >(v); +} + +QVariant KCoreConfigSkeleton::ItemIntList::property() const +{ + return qVariantFromValue< QList<int> >(mReference); +} + +//static int kCoreConfigSkeletionDebugArea() { static int s_area = KDebug::registerArea("kdecore (KConfigSkeleton)"); return s_area; } + +KCoreConfigSkeleton::KCoreConfigSkeleton(const QString &configname, QObject* parent) + : QObject(parent), + d( new Private ) +{ + //qDebug() << "Creating KCoreConfigSkeleton (" << (void *)this << ")"; + + d->mConfig = KSharedConfig::openConfig( configname ); +} + +KCoreConfigSkeleton::KCoreConfigSkeleton(KSharedConfig::Ptr pConfig, QObject* parent) + : QObject(parent), + d( new Private ) +{ + //qDebug() << "Creating KCoreConfigSkeleton (" << (void *)this << ")"; + d->mConfig = pConfig; +} + + +KCoreConfigSkeleton::~KCoreConfigSkeleton() +{ + delete d; +} + +void KCoreConfigSkeleton::setCurrentGroup( const QString &group ) +{ + d->mCurrentGroup = group; +} + +QString KCoreConfigSkeleton::currentGroup() const +{ + return d->mCurrentGroup; +} + +KConfig *KCoreConfigSkeleton::config() +{ + return d->mConfig.data(); +} + +const KConfig *KCoreConfigSkeleton::config() const +{ + return d->mConfig.data(); +} + +void KCoreConfigSkeleton::setSharedConfig(KSharedConfig::Ptr pConfig) +{ + d->mConfig = pConfig; +} + +KConfigSkeletonItem::List KCoreConfigSkeleton::items() const +{ + return d->mItems; +} + +bool KCoreConfigSkeleton::useDefaults(bool b) +{ + if (b == d->mUseDefaults) + return d->mUseDefaults; + + d->mUseDefaults = b; + KConfigSkeletonItem::List::ConstIterator it; + for( it = d->mItems.constBegin(); it != d->mItems.constEnd(); ++it ) + { + (*it)->swapDefault(); + } + usrUseDefaults(b); + return !d->mUseDefaults; +} + +void KCoreConfigSkeleton::setDefaults() +{ + KConfigSkeletonItem::List::ConstIterator it; + for( it = d->mItems.constBegin(); it != d->mItems.constEnd(); ++it ) { + (*it)->setDefault(); + } + usrSetDefaults(); +} + +void KCoreConfigSkeleton::readConfig() +{ + // qDebug(); + d->mConfig->reparseConfiguration(); + KConfigSkeletonItem::List::ConstIterator it; + for( it = d->mItems.constBegin(); it != d->mItems.constEnd(); ++it ) + { + (*it)->readConfig( d->mConfig.data() ); + } + usrReadConfig(); +} + +bool KCoreConfigSkeleton::writeConfig() +{ + //qDebug(); + KConfigSkeletonItem::List::ConstIterator it; + for( it = d->mItems.constBegin(); it != d->mItems.constEnd(); ++it ) + { + (*it)->writeConfig( d->mConfig.data() ); + } + if (!usrWriteConfig()) + return false; + + if (d->mConfig->isDirty()) { + if (!d->mConfig->sync()) + return false; + readConfig(); + emit configChanged(); + } + return true; +} + +bool KCoreConfigSkeleton::usrUseDefaults(bool) +{ + return false; +} + +void KCoreConfigSkeleton::usrSetDefaults() +{ +} + +void KCoreConfigSkeleton::usrReadConfig() +{ +} + +bool KCoreConfigSkeleton::usrWriteConfig() +{ + return true; +} + +void KCoreConfigSkeleton::addItem( KConfigSkeletonItem *item, const QString &name ) +{ + if (d->mItems.contains(item)) { + if (item->name() == name || + (name.isEmpty() && item->name() == item->key())) { + // nothing to do -> it is already in our collection + // and the name isn't changing + return; + } + + d->mItemDict.remove(item->name()); + } else { + d->mItems.append( item ); + } + + item->setName(name.isEmpty() ? item->key() : name); + d->mItemDict.insert(item->name(), item); + item->readDefault(d->mConfig.data()); + item->readConfig(d->mConfig.data()); +} + +void KCoreConfigSkeleton::removeItem(const QString &name) +{ + KConfigSkeletonItem *item = d->mItemDict.value(name); + if (item) { + d->mItems.removeAll(item); + d->mItemDict.remove(item->name()); + delete item; + } +} + +void KCoreConfigSkeleton::clearItems() +{ + KConfigSkeletonItem::List items = d->mItems; + d->mItems.clear(); + d->mItemDict.clear(); + qDeleteAll(items); +} + +KCoreConfigSkeleton::ItemString *KCoreConfigSkeleton::addItemString( const QString &name, QString &reference, + const QString &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemString *item; + item = new KCoreConfigSkeleton::ItemString( d->mCurrentGroup, key.isEmpty() ? name : key, + reference, defaultValue, + KCoreConfigSkeleton::ItemString::Normal ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemPassword *KCoreConfigSkeleton::addItemPassword( const QString &name, QString &reference, + const QString &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemPassword *item; + item = new KCoreConfigSkeleton::ItemPassword( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemPath *KCoreConfigSkeleton::addItemPath( const QString &name, QString &reference, + const QString &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemPath *item; + item = new KCoreConfigSkeleton::ItemPath( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemProperty *KCoreConfigSkeleton::addItemProperty( const QString &name, QVariant &reference, + const QVariant &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemProperty *item; + item = new KCoreConfigSkeleton::ItemProperty( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemBool *KCoreConfigSkeleton::addItemBool( const QString &name, bool &reference, + bool defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemBool *item; + item = new KCoreConfigSkeleton::ItemBool( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemInt *KCoreConfigSkeleton::addItemInt( const QString &name, qint32 &reference, + qint32 defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemInt *item; + item = new KCoreConfigSkeleton::ItemInt( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemUInt *KCoreConfigSkeleton::addItemUInt( const QString &name, quint32 &reference, + quint32 defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemUInt *item; + item = new KCoreConfigSkeleton::ItemUInt( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemLongLong *KCoreConfigSkeleton::addItemLongLong( const QString &name, qint64 &reference, + qint64 defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemLongLong *item; + item = new KCoreConfigSkeleton::ItemLongLong( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +#ifndef KDE_NO_DEPRECATED +KCoreConfigSkeleton::ItemLongLong *KCoreConfigSkeleton::addItemInt64( + const QString& name, + qint64 &reference, + qint64 defaultValue, + const QString & key) +{ + return addItemLongLong(name, reference, defaultValue, key); +} +#endif + +KCoreConfigSkeleton::ItemULongLong *KCoreConfigSkeleton::addItemULongLong( const QString &name, quint64 &reference, + quint64 defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemULongLong *item; + item = new KCoreConfigSkeleton::ItemULongLong( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +#ifndef KDE_NO_DEPRECATED +KCoreConfigSkeleton::ItemULongLong *KCoreConfigSkeleton::addItemUInt64( + const QString & name, + quint64 &reference, + quint64 defaultValue, + const QString & key) +{ + return addItemULongLong(name, reference, defaultValue, key); +} +#endif + +KCoreConfigSkeleton::ItemDouble *KCoreConfigSkeleton::addItemDouble( const QString &name, double &reference, + double defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemDouble *item; + item = new KCoreConfigSkeleton::ItemDouble( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemRect *KCoreConfigSkeleton::addItemRect( const QString &name, QRect &reference, + const QRect &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemRect *item; + item = new KCoreConfigSkeleton::ItemRect( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemPoint *KCoreConfigSkeleton::addItemPoint( const QString &name, QPoint &reference, + const QPoint &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemPoint *item; + item = new KCoreConfigSkeleton::ItemPoint( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemSize *KCoreConfigSkeleton::addItemSize( const QString &name, QSize &reference, + const QSize &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemSize *item; + item = new KCoreConfigSkeleton::ItemSize( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemDateTime *KCoreConfigSkeleton::addItemDateTime( const QString &name, QDateTime &reference, + const QDateTime &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemDateTime *item; + item = new KCoreConfigSkeleton::ItemDateTime( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemStringList *KCoreConfigSkeleton::addItemStringList( const QString &name, QStringList &reference, + const QStringList &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemStringList *item; + item = new KCoreConfigSkeleton::ItemStringList( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +KCoreConfigSkeleton::ItemIntList *KCoreConfigSkeleton::addItemIntList( const QString &name, QList<int> &reference, + const QList<int> &defaultValue, const QString &key ) +{ + KCoreConfigSkeleton::ItemIntList *item; + item = new KCoreConfigSkeleton::ItemIntList( d->mCurrentGroup, key.isNull() ? name : key, + reference, defaultValue ); + addItem( item, name ); + return item; +} + +bool KCoreConfigSkeleton::isImmutable(const QString &name) const +{ + KConfigSkeletonItem *item = findItem(name); + return !item || item->isImmutable(); +} + +KConfigSkeletonItem *KCoreConfigSkeleton::findItem(const QString &name) const +{ + return d->mItemDict.value(name); +} + diff --git a/src/core/kcoreconfigskeleton.h b/src/core/kcoreconfigskeleton.h new file mode 100644 index 00000000..75f6fa28 --- /dev/null +++ b/src/core/kcoreconfigskeleton.h @@ -0,0 +1,1407 @@ +/* + * This file is part of KDE. + * + * Copyright (c) 2001,2002,2003 Cornelius Schumacher <schumacher@kde.org> + * Copyright (c) 2003 Waldo Bastian <bastian@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCORECONFIGSKELETON_H +#define KCORECONFIGSKELETON_H + +#include <kconfigcore_export.h> + +#include <ksharedconfig.h> +#include <kconfiggroup.h> + +#include <QtCore/QDate> +#include <QtCore/QHash> +#include <QtCore/QRect> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtCore/QUrl> + + class KConfigSkeletonItemPrivate; + /** + * \class KConfigSkeletonItem kcoreconfigskeleton.h <KConfigSkeletonItem> + * + * @short Class for storing a preferences setting + * @author Cornelius Schumacher + * @see KCoreConfigSkeleton + * + * This class represents one preferences setting as used by @ref KCoreConfigSkeleton. + * Subclasses of KConfigSkeletonItem implement storage functions for a certain type of + * setting. Normally you don't have to use this class directly. Use the special + * addItem() functions of KCoreConfigSkeleton instead. If you subclass this class you will + * have to register instances with the function KCoreConfigSkeleton::addItem(). + */ + class KCONFIGCORE_EXPORT KConfigSkeletonItem + { + public: + typedef QList < KConfigSkeletonItem * >List; + typedef QHash < QString, KConfigSkeletonItem* > Dict; + typedef QHash < QString, KConfigSkeletonItem* >::Iterator DictIterator; + + /** + * Constructor. + * + * @param _group Config file group. + * @param _key Config file key. + */ + KConfigSkeletonItem(const QString & _group, const QString & _key); + + /** + * Destructor. + */ + virtual ~KConfigSkeletonItem(); + + /** + * Set config file group. + */ + void setGroup( const QString &_group ); + + /** + * Return config file group. + */ + QString group() const; + + /** + * Set config file key. + */ + void setKey( const QString &_key ); + + /** + * Return config file key. + */ + QString key() const; + + /** + * Set internal name of entry. + */ + void setName(const QString &_name); + + /** + * Return internal name of entry. + */ + QString name() const; + + /** + Set label providing a translated one-line description of the item. + */ + void setLabel( const QString &l ); + + /** + Return label of item. See setLabel(). + */ + QString label() const; + + /** + Set ToolTip description of item. + @since 4.2 + */ + void setToolTip( const QString &t ); + + /** + Return ToolTip description of item. See setToolTip(). + @since 4.2 + */ + QString toolTip() const; + + /** + Set WhatsThis description of item. + */ + void setWhatsThis( const QString &w ); + + /** + Return WhatsThis description of item. See setWhatsThis(). + */ + QString whatsThis() const; + + /** + * This function is called by @ref KCoreConfigSkeleton to read the value for this setting + * from a config file. + */ + virtual void readConfig(KConfig *) = 0; + + /** + * This function is called by @ref KCoreConfigSkeleton to write the value of this setting + * to a config file. + */ + virtual void writeConfig(KConfig *) = 0; + + /** + * Read global default value. + */ + virtual void readDefault(KConfig *) = 0; + + /** + * Set item to @p p + */ + virtual void setProperty(const QVariant &p) = 0; + + /** + * Check whether the item is equal to p. + * + * Use this function to compare items that use custom types, + * because QVariant::operator== will not work for those. + * + * @param p QVariant to compare to + * @return true if the item is equal to p, false otherwise + */ + virtual bool isEqual(const QVariant &p) const = 0; + + /** + * Return item as property + */ + virtual QVariant property() const = 0; + + /** + * Return minimum value of item or invalid if not specified + */ + virtual QVariant minValue() const; + + /** + * Return maximum value of item or invalid if not specified + */ + virtual QVariant maxValue() const; + + /** + * Sets the current value to the default value. + */ + virtual void setDefault() = 0; + + /** + * Exchanges the current value with the default value + * Used by KCoreConfigSkeleton::useDefaults(bool); + */ + virtual void swapDefault() = 0; + + /** + * Return if the entry can be modified. + */ + bool isImmutable() const; + + protected: + /** + * sets mIsImmutable to true if mKey in config is immutable + * @param group KConfigGroup to check if mKey is immutable in + */ + void readImmutability(const KConfigGroup &group); + + QString mGroup; ///< The group name for this item + QString mKey; ///< The config key for this item + QString mName; ///< The name of this item + + private: + KConfigSkeletonItemPrivate * const d; + }; + + +/** + * \class KConfigSkeletonGenericItem kcoreconfigskeleton.h <KConfigSkeletonGenericItem> + */ +template < typename T > class KConfigSkeletonGenericItem:public KConfigSkeletonItem + { + public: + /** @copydoc KConfigSkeletonItem(const QString&, const QString&) + @param reference The initial value to hold in the item + @param defaultValue The default value for the item + */ + KConfigSkeletonGenericItem(const QString & _group, const QString & _key, T & reference, + T defaultValue) + : KConfigSkeletonItem(_group, _key), mReference(reference), + mDefault(defaultValue), mLoadedValue(defaultValue) + { + } + + /** + * Set value of this KConfigSkeletonItem. + */ + void setValue(const T & v) + { + mReference = v; + } + + /** + * Return value of this KConfigSkeletonItem. + */ + T & value() + { + return mReference; + } + + /** + * Return const value of this KConfigSkeletonItem. + */ + const T & value() const + { + return mReference; + } + + /** + Set default value for this item. + */ + virtual void setDefaultValue( const T &v ) + { + mDefault = v; + } + + /** + Set the value for this item to the default value + */ + virtual void setDefault() + { + mReference = mDefault; + } + + /** @copydoc KConfigSkeletonItem::writeConfig(KConfig *) */ + virtual void writeConfig(KConfig * config) + { + if ( mReference != mLoadedValue ) // Is this needed? + { + KConfigGroup cg(config, mGroup); + if ((mDefault == mReference) && !cg.hasDefault( mKey)) + cg.revertToDefault( mKey ); + else + cg.writeEntry(mKey, mReference); + } + } + + /** @copydoc KConfigSkeletonItem::readDefault(KConfig*) */ + void readDefault(KConfig * config) + { + config->setReadDefaults(true); + readConfig(config); + config->setReadDefaults(false); + mDefault = mReference; + } + + /** @copydoc KConfigSkeletonItem::swapDefault() */ + void swapDefault() + { + T tmp = mReference; + mReference = mDefault; + mDefault = tmp; + } + + protected: + T & mReference; ///< Stores the value for this item + T mDefault; ///< The default value for this item + T mLoadedValue; + }; + + /** + * \class KCoreConfigSkeleton kcoreconfigskeleton.h <KCoreConfigSkeleton> + * + * @short Class for handling preferences settings for an application. + * @author Cornelius Schumacher + * @see KConfigSkeletonItem + * + * This class provides an interface to preferences settings. Preferences items + * can be registered by the addItem() function corresponding to the data type of + * the setting. KCoreConfigSkeleton then handles reading and writing of config files and + * setting of default values. + * + * Normally you will subclass KCoreConfigSkeleton, add data members for the preferences + * settings and register the members in the constructor of the subclass. + * + * Example: + * \code + * class MyPrefs : public KCoreConfigSkeleton + * { + * public: + * MyPrefs() + * { + * setCurrentGroup("MyGroup"); + * addItemBool("MySetting1", mMyBool, false); + * addItemPoint("MySetting2", mMyPoint, QPoint(100, 200)); + * + * setCurrentGroup("MyOtherGroup"); + * addItemDouble("MySetting3", mMyDouble, 3.14); + * } + * + * bool mMyBool; + * QPoint mMyPoint; + * double mMyDouble; + * } + * \endcode + * + * It might be convenient in many cases to make this subclass of KCoreConfigSkeleton a + * singleton for global access from all over the application without passing + * references to the KCoreConfigSkeleton object around. + * + * You can write the data to the configuration file by calling @ref writeConfig() + * and read the data from the configuration file by calling @ref readConfig(). + * If you want to watch for config changes, use @ref configChanged() signal. + * + * If you have items, which are not covered by the existing addItem() functions + * you can add customized code for reading, writing and default setting by + * implementing the functions @ref usrUseDefaults(), @ref usrReadConfig() and + * @ref usrWriteConfig(). + * + * Internally preferences settings are stored in instances of subclasses of + * @ref KConfigSkeletonItem. You can also add KConfigSkeletonItem subclasses + * for your own types and call the generic @ref addItem() to register them. + * + * In many cases you don't have to write the specific KCoreConfigSkeleton + * subclasses yourself, but you can use \ref kconfig_compiler to automatically + * generate the C++ code from an XML description of the configuration options. + * + * Use KConfigSkeleton if you need GUI types as well. + */ +class KCONFIGCORE_EXPORT KCoreConfigSkeleton : public QObject +{ + Q_OBJECT +public: + /** + * Class for handling a string preferences item. + */ + class KCONFIGCORE_EXPORT ItemString:public KConfigSkeletonGenericItem < QString > + { + public: + enum Type { Normal, Password, Path }; + + /** @enum Type + The type of string that is held in this item + + @var ItemString::Type ItemString::Normal + A normal string + + @var ItemString::Type ItemString::Password + A password string + + @var ItemString::Type ItemString::Path + A path to a file or directory + */ + + + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem + @param type The type of string held by the item + */ + ItemString(const QString & _group, const QString & _key, + QString & reference, + const QString & defaultValue = QLatin1String(""), // NOT QString() !! + Type type = Normal); + + /** @copydoc KConfigSkeletonItem::writeConfig(KConfig*) */ + void writeConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() const */ + QVariant property() const; + + private: + Type mType; + }; + + /** + * Class for handling a password preferences item. + */ + class KCONFIGCORE_EXPORT ItemPassword:public ItemString + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemPassword(const QString & _group, const QString & _key, + QString & reference, + const QString & defaultValue = QLatin1String("")); // NOT QString() !! + }; + + /** + * Class for handling a path preferences item. + */ + class KCONFIGCORE_EXPORT ItemPath:public ItemString + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemPath(const QString & _group, const QString & _key, + QString & reference, + const QString & defaultValue = QString()); + }; + + /** + * Class for handling a url preferences item. + */ + class KCONFIGCORE_EXPORT ItemUrl:public KConfigSkeletonGenericItem < QUrl > + { + public: + + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem + */ + ItemUrl(const QString & _group, const QString & _key, + QUrl & reference, + const QUrl & defaultValue = QUrl()); + + /** @copydoc KConfigSkeletonItem::writeConfig(KConfig*) */ + void writeConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() const */ + QVariant property() const; + }; + + /** + * Class for handling a QVariant preferences item. + */ + class KCONFIGCORE_EXPORT ItemProperty:public KConfigSkeletonGenericItem < QVariant > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemProperty(const QString & _group, const QString & _key, + QVariant & reference, const QVariant & defaultValue = 0); + + void readConfig(KConfig * config); + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() const */ + QVariant property() const; + }; + + + /** + * Class for handling a bool preferences item. + */ + class KCONFIGCORE_EXPORT ItemBool:public KConfigSkeletonGenericItem < bool > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemBool(const QString & _group, const QString & _key, bool & reference, + bool defaultValue = true); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() const */ + QVariant property() const; + }; + + + /** + * Class for handling a 32-bit integer preferences item. + */ + class KCONFIGCORE_EXPORT ItemInt:public KConfigSkeletonGenericItem < qint32 > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemInt(const QString & _group, const QString & _key, qint32 &reference, + qint32 defaultValue = 0); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + + /** Get the minimum value that is allowed to be stored in this item */ + QVariant minValue() const; + + /** Get the maximum value this is allowed to be stored in this item */ + QVariant maxValue() const; + + /** Set the minimum value for the item + @sa minValue() + */ + void setMinValue(qint32); + + /** Set the maximum value for the item + @sa maxValue + */ + void setMaxValue(qint32); + + private: + bool mHasMin : 1; + bool mHasMax : 1; + qint32 mMin; + qint32 mMax; + }; + + /** + * Class for handling a 64-bit integer preferences item. + */ + class KCONFIGCORE_EXPORT ItemLongLong:public KConfigSkeletonGenericItem < qint64 > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemLongLong(const QString & _group, const QString & _key, qint64 &reference, + qint64 defaultValue = 0); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + + /** @copydoc ItemInt::minValue() */ + QVariant minValue() const; + + /** @copydoc ItemInt::maxValue() */ + QVariant maxValue() const; + + /** @copydoc ItemInt::setMinValue(qint32) */ + void setMinValue(qint64); + + /** @copydoc ItemInt::setMaxValue(qint32) */ + void setMaxValue(qint64); + + private: + bool mHasMin : 1; + bool mHasMax : 1; + qint64 mMin; + qint64 mMax; + }; +#ifndef KDE_NO_DEPRECATED + typedef KCONFIGCORE_DEPRECATED ItemLongLong ItemInt64; +#endif + + /** + * Class for handling enums. + */ + class KCONFIGCORE_EXPORT ItemEnum:public ItemInt + { + public: + struct Choice + { + QString name; + QString label; + QString toolTip; + QString whatsThis; + }; + + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem + @param choices The list of enums that can be stored in this item + */ + ItemEnum(const QString & _group, const QString & _key, qint32 &reference, + const QList<Choice> &choices, qint32 defaultValue = 0); + + QList<Choice> choices() const; + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::writeConfig(KConfig*) */ + void writeConfig(KConfig * config); + + // Source compatibility with 4.x + typedef Choice Choice2; + QList<Choice> choices2() const; + + private: + QList<Choice> mChoices; + }; + + + /** + * Class for handling an unsigned 32-bit integer preferences item. + */ + class KCONFIGCORE_EXPORT ItemUInt:public KConfigSkeletonGenericItem < quint32 > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemUInt(const QString & _group, const QString & _key, + quint32 &reference, quint32 defaultValue = 0); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + + /** @copydoc ItemInt::minValue() */ + QVariant minValue() const; + + /** @copydoc ItemInt::maxValue() */ + QVariant maxValue() const; + + /** @copydoc ItemInt::setMinValue(qint32) */ + void setMinValue(quint32); + + /** @copydoc ItemInt::setMaxValue(qint32) */ + void setMaxValue(quint32); + + private: + bool mHasMin : 1; + bool mHasMax : 1; + quint32 mMin; + quint32 mMax; + }; + + /** + * Class for handling unsigned 64-bit integer preferences item. + */ + class KCONFIGCORE_EXPORT ItemULongLong:public KConfigSkeletonGenericItem < quint64 > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemULongLong(const QString & _group, const QString & _key, quint64 &reference, + quint64 defaultValue = 0); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + + /** @copydoc ItemInt::minValue() */ + QVariant minValue() const; + + /** @copydoc ItemInt::maxValue() */ + QVariant maxValue() const; + + /** @copydoc ItemInt::setMinValue(qint32) */ + void setMinValue(quint64); + + /** @copydoc ItemInt::setMaxValue(qint32) */ + void setMaxValue(quint64); + + private: + bool mHasMin : 1; + bool mHasMax : 1; + quint64 mMin; + quint64 mMax; + }; +#ifndef KDE_NO_DEPRECATED + typedef KCONFIGCORE_DEPRECATED ItemULongLong ItemUInt64; +#endif + + /** + * Class for handling a floating point preference item. + */ + class KCONFIGCORE_EXPORT ItemDouble:public KConfigSkeletonGenericItem < double > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemDouble(const QString & _group, const QString & _key, + double &reference, double defaultValue = 0); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + + /** @copydoc ItemInt::minValue() */ + QVariant minValue() const; + + /** @copydoc ItemInt::maxValue() */ + QVariant maxValue() const; + + /** @copydoc ItemInt::setMinValue() */ + void setMinValue(double); + + /** @copydoc ItemInt::setMaxValue() */ + void setMaxValue(double); + + private: + bool mHasMin : 1; + bool mHasMax : 1; + double mMin; + double mMax; + }; + + + /** + * Class for handling a QRect preferences item. + */ + class KCONFIGCORE_EXPORT ItemRect:public KConfigSkeletonGenericItem < QRect > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemRect(const QString & _group, const QString & _key, QRect & reference, + const QRect & defaultValue = QRect()); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + }; + + + /** + * Class for handling a QPoint preferences item. + */ + class KCONFIGCORE_EXPORT ItemPoint:public KConfigSkeletonGenericItem < QPoint > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemPoint(const QString & _group, const QString & _key, QPoint & reference, + const QPoint & defaultValue = QPoint()); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + }; + + + /** + * Class for handling a QSize preferences item. + */ + class KCONFIGCORE_EXPORT ItemSize:public KConfigSkeletonGenericItem < QSize > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemSize(const QString & _group, const QString & _key, QSize & reference, + const QSize & defaultValue = QSize()); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + }; + + + /** + * Class for handling a QDateTime preferences item. + */ + class KCONFIGCORE_EXPORT ItemDateTime:public KConfigSkeletonGenericItem < QDateTime > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemDateTime(const QString & _group, const QString & _key, + QDateTime & reference, + const QDateTime & defaultValue = QDateTime()); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + }; + + + /** + * Class for handling a string list preferences item. + */ + class KCONFIGCORE_EXPORT ItemStringList:public KConfigSkeletonGenericItem < QStringList > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemStringList(const QString & _group, const QString & _key, + QStringList & reference, + const QStringList & defaultValue = QStringList()); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + }; + + + /** + * Class for handling a path list preferences item. + */ + class KCONFIGCORE_EXPORT ItemPathList:public ItemStringList + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemPathList(const QString & _group, const QString & _key, + QStringList & reference, + const QStringList & defaultValue = QStringList()); + + /** @copydoc KConfigSkeletonItem::readConfig */ + void readConfig(KConfig * config); + /** @copydoc KConfigSkeletonItem::writeConfig */ + void writeConfig(KConfig * config); + }; + + /** + * Class for handling a url list preferences item. + */ + class KCONFIGCORE_EXPORT ItemUrlList:public KConfigSkeletonGenericItem < QList<QUrl> > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemUrlList(const QString & _group, const QString & _key, + QList<QUrl> & reference, + const QList<QUrl> & defaultValue = QList<QUrl>()); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::writeConfig(KConfig*) */ + void writeConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + }; + + /** + * Class for handling an integer list preferences item. + */ + class KCONFIGCORE_EXPORT ItemIntList:public KConfigSkeletonGenericItem < QList < int > > + { + public: + /** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */ + ItemIntList(const QString & _group, const QString & _key, + QList < int >&reference, + const QList < int >&defaultValue = QList < int >()); + + /** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */ + void readConfig(KConfig * config); + + /** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */ + void setProperty(const QVariant & p); + + /** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) const */ + bool isEqual(const QVariant &p) const; + + /** @copydoc KConfigSkeletonItem::property() */ + QVariant property() const; + }; + + +public: + /** + * Constructor. + * + * @param configname name of config file. If no name is given, the default + * config file as returned by KSharedConfig::openConfig() is used + * @param parent the parent object (see QObject documentation) + */ + explicit KCoreConfigSkeleton(const QString & configname = QString(), QObject* parent = 0); + + /** + * Constructor. + * + * @param config configuration object to use + * @param parent the parent object (see QObject documentation) + */ + explicit KCoreConfigSkeleton(KSharedConfig::Ptr config, QObject* parent = 0); + + /** + * Destructor + */ + virtual ~KCoreConfigSkeleton(); + + /** + * Set all registered items to their default values. + * This method calls usrSetDefaults() after setting the defaults for the + * registered items. You can overridde usrSetDefaults() in derived classes + * if you have special requirements. + * If you need more fine-grained control of setting the default values of + * the registered items you can override setDefaults() in a derived class. + */ + virtual void setDefaults(); + + /** + * Read preferences from config file. All registered items are set to the + * values read from disk. + * This method calls usrReadConfig() after reading the settings of the + * registered items from the KConfig. You can overridde usrReadConfig() + * in derived classes if you have special requirements. + * If you need more fine-grained control of storing the settings from + * the registered items you can override readConfig() in a derived class. + */ + virtual void readConfig(); + + /** + * Set the config file group for subsequent addItem() calls. It is valid + * until setCurrentGroup() is called with a new argument. Call this before + * you add any items. The default value is "No Group". + */ + void setCurrentGroup(const QString & group); + + /** + * Returns the current group used for addItem() calls. + */ + QString currentGroup() const; + + /** + * Register a custom @ref KConfigSkeletonItem with a given name. + * + * If the name parameter is null, take the name from KConfigSkeletonItem::key(). + * Note that all names must be unique but that multiple entries can have + * the same key if they reside in different groups. + * + * KCoreConfigSkeleton takes ownership of the KConfigSkeletonItem. + */ + void addItem(KConfigSkeletonItem *, const QString & name = QString() ); + + /** + * Register an item of type QString. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemString *addItemString(const QString & name, QString & reference, + const QString & defaultValue = QLatin1String(""), // NOT QString() !! + const QString & key = QString()); + + /** + * Register a password item of type QString. The string value is written + * encrypted to the config file. Note that the current encryption scheme + * is very weak. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemPassword *addItemPassword(const QString & name, QString & reference, + const QString & defaultValue = QLatin1String(""), + const QString & key = QString()); + + /** + * Register a path item of type QString. The string value is interpreted + * as a path. This means, dollar expension is activated for this value, so + * that e.g. $HOME gets expanded. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemPath *addItemPath(const QString & name, QString & reference, + const QString & defaultValue = QLatin1String(""), + const QString & key = QString()); + + /** + * Register a property item of type QVariant. Note that only the following + * QVariant types are allowed: String, StringList, Font, Point, Rect, Size, + * Color, Int, UInt, Bool, Double, DateTime and Date. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemProperty *addItemProperty(const QString & name, QVariant & reference, + const QVariant & defaultValue = QVariant(), + const QString & key = QString()); + /** + * Register an item of type bool. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemBool *addItemBool(const QString & name, bool & reference, + bool defaultValue = false, + const QString & key = QString()); + + /** + * Register an item of type qint32. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemInt *addItemInt(const QString & name, qint32 &reference, qint32 defaultValue = 0, + const QString & key = QString()); + + /** + * Register an item of type quint32. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemUInt *addItemUInt(const QString & name, quint32 &reference, + quint32 defaultValue = 0, + const QString & key = QString()); + + /** + * Register an item of type qint64. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemLongLong *addItemLongLong(const QString & name, qint64 &reference, + qint64 defaultValue = 0, + const QString & key = QString()); + + /** + * @deprecated + * Use addItemLongLong(). + */ +#ifndef KDE_NO_DEPRECATED + KCONFIGCORE_DEPRECATED ItemLongLong *addItemInt64( const QString& name, qint64 &reference, + qint64 defaultValue = 0, + const QString & key = QString()); +#endif + + /** + * Register an item of type quint64 + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemULongLong *addItemULongLong(const QString & name, quint64 &reference, + quint64 defaultValue = 0, + const QString & key = QString()); + + /** + * @deprecated + * Use addItemULongLong(). + */ +#ifndef KDE_NO_DEPRECATED + KCONFIGCORE_DEPRECATED ItemULongLong *addItemUInt64(const QString & name, quint64 &reference, + quint64 defaultValue = 0, + const QString & key = QString()); +#endif + + /** + * Register an item of type double. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemDouble *addItemDouble(const QString & name, double &reference, + double defaultValue = 0.0, + const QString & key = QString()); + + /** + * Register an item of type QRect. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemRect *addItemRect(const QString & name, QRect & reference, + const QRect & defaultValue = QRect(), + const QString & key = QString()); + + /** + * Register an item of type QPoint. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemPoint *addItemPoint(const QString & name, QPoint & reference, + const QPoint & defaultValue = QPoint(), + const QString & key = QString()); + + /** + * Register an item of type QSize. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemSize *addItemSize(const QString & name, QSize & reference, + const QSize & defaultValue = QSize(), + const QString & key = QString()); + + /** + * Register an item of type QDateTime. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemDateTime *addItemDateTime(const QString & name, QDateTime & reference, + const QDateTime & defaultValue = QDateTime(), + const QString & key = QString()); + + /** + * Register an item of type QStringList. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemStringList *addItemStringList(const QString & name, QStringList & reference, + const QStringList & defaultValue = QStringList(), + const QString & key = QString()); + + /** + * Register an item of type QList<int>. + * + * @param name Name used to identify this setting. Names must be unique. + * @param reference Pointer to the variable, which is set by readConfig() + * calls and read by writeConfig() calls. + * @param defaultValue Default value, which is used when the config file + * does not yet contain the key of this item. + * @param key Key used in config file. If key is null, name is used as key. + * @return The created item + */ + ItemIntList *addItemIntList(const QString & name, QList < int >&reference, + const QList < int >&defaultValue = + QList < int >(), + const QString & key = QString()); + + /** + * Return the @ref KConfig object used for reading and writing the settings. + */ + KConfig *config(); + + /** + * Return the @ref KConfig object used for reading and writing the settings. + */ + const KConfig *config() const; + + /** + * Set the @ref KSharedConfig object used for reading and writing the settings. + */ + void setSharedConfig(KSharedConfig::Ptr pConfig); + + /** + * Return list of items managed by this KCoreConfigSkeleton object. + */ + KConfigSkeletonItem::List items() const; + + /** + * Removes and deletes an item by name + * @arg name the name of the item to remove + */ + void removeItem(const QString &name); + + /** + * Removes and deletes all items + */ + void clearItems(); + + /** + * Return whether a certain item is immutable + * @since 4.4 + */ + bool isImmutable(const QString & name) const; + + /** + * Lookup item by name + * @since 4.4 + */ + KConfigSkeletonItem * findItem(const QString & name) const; + + /** + * Specify whether this object should reflect the actual values or the + * default values. + * This method is implemented by usrUseDefaults(), which can be overridden + * in derived classes if you have special requirements and can call + * usrUseDefaults() directly. + * If you don't have control whether useDefaults() or usrUseDefaults() is + * called override useDefaults() directly. + * @param b true to make this object reflect the default values, + * false to make it reflect the actual values. + * @return The state prior to this call + */ + virtual bool useDefaults(bool b); + +public Q_SLOTS: + /** + * Write preferences to config file. The values of all registered items are + * written to disk. + * This method calls usrWriteConfig() after writing the settings from the + * registered items to the KConfig. You can overridde usrWriteConfig() + * in derived classes if you have special requirements. + * If you need more fine-grained control of storing the settings from + * the registered items you can override writeConfig() in a derived class. + */ + virtual bool writeConfig(); + +Q_SIGNALS: + /** + * This signal is emitted when the configuration change. + */ + void configChanged(); + +protected: + /** + * Implemented by subclasses that use special defaults. + * It replaces the default values with the actual values and + * vice versa. Called from @ref useDefaults() + * @param b true to make this object reflect the default values, + * false to make it reflect the actual values. + * @return The state prior to this call + */ + virtual bool usrUseDefaults(bool b); + + /** + * Perform the actual setting of default values. + * Override in derived classes to set special default values. + * Called from @ref setDefaults() + */ + virtual void usrSetDefaults(); + + /** + * Perform the actual reading of the configuration file. + * Override in derived classes to read special config values. + * Called from @ref readConfig() + */ + virtual void usrReadConfig(); + + /** + * Perform the actual writing of the configuration file. + * Override in derived classes to write special config values. + * Called from @ref writeConfig() + */ + virtual bool usrWriteConfig(); + +private: + class Private; + Private * const d; + friend class KConfigSkeleton; + +}; + +#endif diff --git a/src/core/kcoreconfigskeleton_p.h b/src/core/kcoreconfigskeleton_p.h new file mode 100644 index 00000000..0912019c --- /dev/null +++ b/src/core/kcoreconfigskeleton_p.h @@ -0,0 +1,64 @@ +/* + This file is part of KOrganizer. + Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2003 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCORECONFIGSKELETON_P_H +#define KCORECONFIGSKELETON_P_H + +#include "kcoreconfigskeleton.h" + +class KCoreConfigSkeleton::Private +{ +public: + Private() + : mCurrentGroup( QLatin1String("No Group") ), mUseDefaults( false ) + {} + ~Private() + { + KConfigSkeletonItem::List::ConstIterator it; + for( it = mItems.constBegin(); it != mItems.constEnd(); ++it ) + { + delete *it; + } + } + QString mCurrentGroup; + + KSharedConfig::Ptr mConfig; // pointer to KConfig object + + KConfigSkeletonItem::List mItems; + KConfigSkeletonItem::Dict mItemDict; + + bool mUseDefaults; +}; + +class KConfigSkeletonItemPrivate +{ +public: + KConfigSkeletonItemPrivate() + : mIsImmutable(true) + {} + bool mIsImmutable; ///< Indicates this item is immutable + + QString mLabel; ///< The label for this item + QString mToolTip; ///< The ToolTip text for this item + QString mWhatsThis; ///< The What's This text for this item +}; + +#endif diff --git a/src/core/kdesktopfile.cpp b/src/core/kdesktopfile.cpp new file mode 100644 index 00000000..0ebbb4b0 --- /dev/null +++ b/src/core/kdesktopfile.cpp @@ -0,0 +1,366 @@ +/* + This file is part of the KDE libraries + Copyright (c) 1999 Pietro Iglio <iglio@kde.org> + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Qt5 TODO: re-enable. No point in doing it before, it breaks on QString::fromUtf8(QByteArray), which exists in qt5. +#undef QT_NO_CAST_FROM_BYTEARRAY + +#include "kdesktopfile.h" + +#ifndef Q_OS_WIN +#include <unistd.h> +#endif + +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QUrl> +#include <qstandardpaths.h> + +#include "kauthorized.h" +#include "kconfig_p.h" +#include "kconfiggroup.h" +#include "kconfigini_p.h" + +class KDesktopFilePrivate : public KConfigPrivate +{ + public: + KDesktopFilePrivate(QStandardPaths::StandardLocation resourceType, const QString &fileName); + KConfigGroup desktopGroup; +}; + +KDesktopFilePrivate::KDesktopFilePrivate(QStandardPaths::StandardLocation resourceType, const QString &fileName) + : KConfigPrivate(KConfig::NoGlobals, resourceType) +{ + mBackend = new KConfigIniBackend(); + bDynamicBackend = false; + changeFileName(fileName); +} + +KDesktopFile::KDesktopFile(QStandardPaths::StandardLocation resourceType, const QString &fileName) + : KConfig(*new KDesktopFilePrivate(resourceType, fileName)) +{ + Q_D(KDesktopFile); + reparseConfiguration(); + d->desktopGroup = KConfigGroup(this, "Desktop Entry"); +} + +KDesktopFile::KDesktopFile(const QString &fileName) + : KConfig(*new KDesktopFilePrivate(QStandardPaths::ApplicationsLocation, fileName)) +{ + Q_D(KDesktopFile); + reparseConfiguration(); + d->desktopGroup = KConfigGroup(this, "Desktop Entry"); +} + +KDesktopFile::~KDesktopFile() +{ +} + +KConfigGroup KDesktopFile::desktopGroup() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup; +} + +QString KDesktopFile::locateLocal(const QString &path) +{ + QString relativePath; + // Relative to config? (e.g. for autostart) + Q_FOREACH(const QString& dir, QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation)) { + if (path.startsWith(dir) + '/') { + relativePath = dir.mid(path.length() + 1); + return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + relativePath; + } + } + // Relative to xdg data dir? (much more common) + Q_FOREACH(const QString& dir, QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) { + if (path.startsWith(dir) + '/') + relativePath = dir.mid(path.length() + 1); + } + if (relativePath.isEmpty()) { + // What now? The desktop file doesn't come from XDG_DATA_DIRS. Use filename only and hope for the best. + relativePath = path.mid(path.lastIndexOf(QLatin1Char('/'))+1); + } + return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + relativePath; +} + +bool KDesktopFile::isDesktopFile(const QString& path) +{ + return (path.length() > 8 + && path.endsWith(QLatin1String(".desktop"))); +} + +bool KDesktopFile::isAuthorizedDesktopFile(const QString& path) +{ + if (path.isEmpty()) + return false; // Empty paths are not ok. + + if (QDir::isRelativePath(path)) + return true; // Relative paths are ok. + + const QString realPath = QFileInfo(path).canonicalFilePath(); + if (realPath.isEmpty()) + return false; // File doesn't exist. + +#ifndef Q_OS_WIN + const Qt::CaseSensitivity sensitivity = Qt::CaseSensitive; +#else + const Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive; +#endif + + // Check if the .desktop file is installed as part of KDE or XDG. + const QStringList appsDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); + Q_FOREACH (const QString &prefix, appsDirs) { + if (QDir(prefix).exists() && realPath.startsWith(QFileInfo(prefix).canonicalFilePath(), sensitivity)) + return true; + } + const QString servicesDir = QLatin1String("kde5/services/"); // KGlobal::dirs()->xdgDataRelativePath("services") + Q_FOREACH (const QString &xdgDataPrefix, QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) { + if (QDir(xdgDataPrefix).exists()) { + const QString prefix = QFileInfo(xdgDataPrefix).canonicalFilePath(); + if (realPath.startsWith(prefix + QLatin1Char('/') + servicesDir, sensitivity)) + return true; + } + } + const QString autostartDir = QLatin1String("autostart/"); + Q_FOREACH (const QString &xdgDataPrefix, QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation)) { + if (QDir(xdgDataPrefix).exists()) { + const QString prefix = QFileInfo(xdgDataPrefix).canonicalFilePath(); + if (realPath.startsWith(prefix + QLatin1Char('/') + autostartDir, sensitivity)) + return true; + } + } + + // Forbid desktop files outside of standard locations if kiosk is set so + if (!KAuthorized::authorize(QLatin1String("run_desktop_files"))) { + qWarning() << "Access to '" << path << "' denied because of 'run_desktop_files' restriction." << endl; + return false; + } + + // Not otherwise permitted, so only allow if the file is executable, or if + // owned by root (uid == 0) + QFileInfo entryInfo( path ); + if (entryInfo.isExecutable() || entryInfo.ownerId() == 0) + return true; + + qWarning() << "Access to '" << path << "' denied, not owned by root, executable flag not set." << endl; + return false; +} + +QString KDesktopFile::readType() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readEntry("Type", QString()); +} + +QString KDesktopFile::readIcon() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readEntry("Icon", QString()); +} + +QString KDesktopFile::readName() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readEntry("Name", QString()); +} + +QString KDesktopFile::readComment() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readEntry("Comment", QString()); +} + +QString KDesktopFile::readGenericName() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readEntry("GenericName", QString()); +} + +QString KDesktopFile::readPath() const +{ + Q_D(const KDesktopFile); + // NOT readPathEntry, it is not XDG-compliant. Path entries written by + // KDE4 will be still treated as such, though. + return d->desktopGroup.readEntry("Path", QString()); +} + +QString KDesktopFile::readDevice() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readEntry("Dev", QString()); +} + +QString KDesktopFile::readUrl() const +{ + Q_D(const KDesktopFile); + if (hasDeviceType()) { + return d->desktopGroup.readEntry("MountPoint", QString()); + } else { + // NOT readPathEntry (see readPath()) + QString url = d->desktopGroup.readEntry("URL", QString()); + if ( !url.isEmpty() && !QDir::isRelativePath(url) ) + { + // Handle absolute paths as such (i.e. we need to escape them) + return QUrl::fromLocalFile(url).toString(); + } + return url; + } +} + +QStringList KDesktopFile::readActions() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readXdgListEntry("Actions"); +} + +KConfigGroup KDesktopFile::actionGroup(const QString &group) +{ + return KConfigGroup(this, QLatin1String("Desktop Action ") + group); +} + +const KConfigGroup KDesktopFile::actionGroup(const QString& group) const +{ + return const_cast<KDesktopFile*>(this)->actionGroup(group); +} + +bool KDesktopFile::hasActionGroup(const QString &group) const +{ + return hasGroup(QString(QLatin1String("Desktop Action ") + group).toUtf8().constData()); +} + +bool KDesktopFile::hasLinkType() const +{ + return readType() == QLatin1String("Link"); +} + +bool KDesktopFile::hasApplicationType() const +{ + return readType() == QLatin1String("Application"); +} + +bool KDesktopFile::hasDeviceType() const +{ + return readType() == QLatin1String("FSDevice"); +} + +bool KDesktopFile::tryExec() const +{ + Q_D(const KDesktopFile); + // Test for TryExec and "X-KDE-AuthorizeAction" + // NOT readPathEntry (see readPath()) + QString te = d->desktopGroup.readEntry("TryExec", QString()); + + if (!te.isEmpty()) { + return !QStandardPaths::findExecutable(te).isEmpty(); + } + const QStringList list = d->desktopGroup.readEntry("X-KDE-AuthorizeAction", QStringList()); + if (!list.isEmpty()) + { + for(QStringList::ConstIterator it = list.begin(); + it != list.end(); + ++it) + { + if (!KAuthorized::authorize((*it).trimmed())) + return false; + } + } + + // See also KService::username() + bool su = d->desktopGroup.readEntry("X-KDE-SubstituteUID", false); + if (su) + { + QString user = d->desktopGroup.readEntry("X-KDE-Username", QString()); + if (user.isEmpty()) + user = QString::fromLocal8Bit(qgetenv("ADMIN_ACCOUNT")); + if (user.isEmpty()) + user = QString::fromLatin1("root"); + if (!KAuthorized::authorize(QString::fromLatin1("user/")+user)) + return false; + } + + return true; +} + +/** + * @return the filename as passed to the constructor. + */ +//QString KDesktopFile::fileName() const { return backEnd->fileName(); } + +/** + * @return the resource type as passed to the constructor. + */ +//QString +//KDesktopFile::resource() const { return backEnd->resource(); } + +QStringList +KDesktopFile::sortOrder() const +{ + Q_D(const KDesktopFile); + return d->desktopGroup.readEntry("SortOrder", QStringList()); +} + +//void KDesktopFile::virtual_hook( int id, void* data ) +//{ KConfig::virtual_hook( id, data ); } + +QString KDesktopFile::readDocPath() const +{ + Q_D(const KDesktopFile); + //legacy entry in kde3 apps + if(d->desktopGroup.hasKey( "DocPath" )) + return d->desktopGroup.readPathEntry( "DocPath", QString() ); + return d->desktopGroup.readPathEntry( "X-DocPath", QString() ); +} + +KDesktopFile* KDesktopFile::copyTo(const QString &file) const +{ + KDesktopFile *config = new KDesktopFile(QString()); + this->KConfig::copyTo(file, config); +// config->setDesktopGroup(); + return config; +} + +QStandardPaths::StandardLocation KDesktopFile::resource() const +{ + Q_D(const KDesktopFile); + return d->resourceType; +} + +QString KDesktopFile::fileName() const +{ + return name(); +} + +bool KDesktopFile::noDisplay() const +{ + Q_D(const KDesktopFile); + if (d->desktopGroup.readEntry("NoDisplay", false)) { + return true; + } + if (d->desktopGroup.hasKey("OnlyShowIn")) { + if (!d->desktopGroup.readXdgListEntry("OnlyShowIn").contains(QLatin1String("KDE"))) + return true; + } + if (d->desktopGroup.hasKey("NotShowIn")) { + if (d->desktopGroup.readXdgListEntry("NotShowIn").contains(QLatin1String("KDE"))) + return true; + } + return false; +} diff --git a/src/core/kdesktopfile.h b/src/core/kdesktopfile.h new file mode 100644 index 00000000..f3c5fe8f --- /dev/null +++ b/src/core/kdesktopfile.h @@ -0,0 +1,252 @@ +/* This file is part of the KDE libraries + Copyright (c) 1999 Pietro Iglio <iglio@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KDESKTOPFILE_H +#define KDESKTOPFILE_H + +#include <kconfig.h> + +class KConfigGroup; +class KDesktopFilePrivate; + +/** + * \class KDesktopFile kdesktopfile.h <KDesktopFile> + * + * %KDE Desktop File Management. + * This class implements %KDE's support for the freedesktop.org + * <em>Desktop Entry Spec</em>. + * + * @author Pietro Iglio <iglio@kde.org> + * @see KConfigBase KConfig + * @see <a href="http://standards.freedesktop.org/desktop-entry-spec/latest/">Desktop Entry Spec</a> + */ +class KCONFIGCORE_EXPORT KDesktopFile : public KConfig +{ +public: + /** + * Constructs a KDesktopFile object. + * + * See QStandardPaths for more information on resources. + * + * @param resourceType Allows you to change what sort of resource + * to search for if @p fileName is not absolute. + * For instance, you might want to specify GenericConfigLocation. + * @param fileName The name or path of the desktop file. If it + * is not absolute, it will be located + * using the resource type @p resType. + */ + explicit KDesktopFile(QStandardPaths::StandardLocation resourceType, const QString &fileName); + + /** + * Constructs a KDesktopFile object. + * + * See QStandardPaths for more information on resources. + * + * @param fileName The name or path of the desktop file. If it + * is not absolute, it will be located + * using the resource type ApplicationsLocation + */ + explicit KDesktopFile(const QString &fileName); + + /** + * Destructs the KDesktopFile object. + * + * Writes back any dirty configuration entries. + */ + virtual ~KDesktopFile(); + + /** + * Checks whether this is really a desktop file. + * + * The check is performed looking at the file extension (the file is not + * opened). + * Currently, the only valid extension is ".desktop". + * @param path the path of the file to check + * @return true if the file appears to be a desktop file. + */ + static bool isDesktopFile(const QString& path); + + /** + * Checks whether the user is authorized to run this desktop file. + * By default users are authorized to run all desktop files but + * the KIOSK framework can be used to activate certain restrictions. + * See README.kiosk for more information. + * + * Note: Since KDE 4.3, there are more restrictions on authorized + * desktop files to prevent users from inadvertently running trojan + * desktop files. Your application launchers should have the executable + * bit set to prevent issues. To see if a restriction is due to + * KIOSK, see KAuthorized. + * + * @param path the file to check + * @return true if the user is authorized to run the file + */ + static bool isAuthorizedDesktopFile(const QString& path); + + /** + * Returns the location where changes for the .desktop file @p path + * should be written to. + */ + static QString locateLocal(const QString &path); + + KConfigGroup desktopGroup() const; + + /** + * Returns the value of the "Type=" entry. + * @return the type or QString() if not specified + */ + QString readType() const; + + /** + * Returns the value of the "Icon=" entry. + * @return the icon or QString() if not specified + */ + QString readIcon() const; + + /** + * Returns the value of the "Name=" entry. + * @return the name or QString() if not specified + */ + QString readName() const; + + /** + * Returns the value of the "Comment=" entry. + * @return the comment or QString() if not specified + */ + QString readComment() const; + + /** + * Returns the value of the "GenericName=" entry. + * @return the generic name or QString() if not specified + */ + QString readGenericName() const; + + /** + * Returns the value of the "Path=" entry. + * @return the path or QString() if not specified + */ + QString readPath() const; + + /** + * Returns the value of the "Dev=" entry. + * @return the device or QString() if not specified + */ + QString readDevice() const; + + /** + * Returns the value of the "URL=" entry. + * @return the URL or QString() if not specified + */ + QString readUrl() const; + + /** + * Returns a list of the "Actions=" entries. + * @return the list of actions + */ + QStringList readActions() const; + + /** + * Sets the desktop action group. + * @param group the new action group + */ + KConfigGroup actionGroup(const QString &group); + + const KConfigGroup actionGroup(const QString &group) const; + + /** + * Returns true if the action group exists, false otherwise + * @param group the action group to test + * @return true if the action group exists + */ + bool hasActionGroup(const QString &group) const; + + /** + * Checks whether there is a "Type=Link" entry. + * + * The link points to the "URL=" entry. + * @return true if there is a "Type=Link" entry + */ + bool hasLinkType() const; + + /** + * Checks whether there is an entry "Type=Application". + * @return true if there is a "Type=Application" entry + */ + bool hasApplicationType() const; + + /** + * Checks whether there is an entry "Type=FSDevice". + * @return true if there is a "Type=FSDevice" entry + */ + bool hasDeviceType() const; + + /** + * Checks whether the TryExec field contains a binary + * which is found on the local system. + * @return true if TryExec contains an existing binary + */ + bool tryExec() const; + + /** + * Returns the value of the "X-DocPath=" Or "DocPath=" entry. + * @return The value of the "X-DocPath=" Or "DocPath=" entry. + */ + QString readDocPath() const; + + /** + * Returns the entry of the "SortOrder=" entry. + * @return the value of the "SortOrder=" entry. + */ + QStringList sortOrder() const; + + /** + * Whether the entry should be suppressed in menus. + * This handles the NoDisplay key, but also OnlyShowIn / NotShowIn. + * @return true to suppress this service + * @since 4.1 + */ + bool noDisplay() const; + + /** + * Copies all entries from this config object to a new + * KDesktopFile object that will save itself to @p file. + * + * Actual saving to @p file happens when the returned object is + * destructed or when sync() is called upon it. + * + * @param file the new KDesktopFile object it will save itself to. + */ + KDesktopFile* copyTo(const QString &file) const; + + QString fileName() const; + + QStandardPaths::StandardLocation resource() const; + +protected: + /** Virtual hook, used to add new "virtual" functions while maintaining + binary compatibility. Unused in this class. + */ +// virtual void virtual_hook( int id, void* data ); +private: + + Q_DISABLE_COPY(KDesktopFile) + + Q_DECLARE_PRIVATE(KDesktopFile) +}; + +#endif diff --git a/src/core/kemailsettings.cpp b/src/core/kemailsettings.cpp new file mode 100644 index 00000000..6a1f9448 --- /dev/null +++ b/src/core/kemailsettings.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2000 Alex Zepeda <zipzippy@sonic.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kemailsettings.h" + +#include <kconfig.h> +#include <kconfiggroup.h> + +class KEMailSettingsPrivate { +public: + KEMailSettingsPrivate() : m_pConfig( 0 ) {} + ~KEMailSettingsPrivate() { delete m_pConfig; } + KConfig *m_pConfig; + QStringList profiles; + QString m_sDefaultProfile, m_sCurrentProfile; +}; + +QString KEMailSettings::defaultProfileName() const +{ + return p->m_sDefaultProfile; +} + +QString KEMailSettings::getSetting(KEMailSettings::Setting s) const +{ + KConfigGroup cg(p->m_pConfig, QStringLiteral("PROFILE_") + p->m_sCurrentProfile); + switch (s) { + case ClientProgram: { + return cg.readEntry("EmailClient"); + break; + } + case ClientTerminal: { + return cg.readEntry("TerminalClient", QVariant(false)).toString(); + break; + } + case RealName: { + return cg.readEntry("FullName"); + break; + } + case EmailAddress: { + return cg.readEntry("EmailAddress"); + break; + } + case ReplyToAddress: { + return cg.readEntry("ReplyAddr"); + break; + } + case Organization: { + return cg.readEntry("Organization"); + break; + } + case OutServer: { + return cg.readEntry("OutgoingServer"); + break; + } + case OutServerLogin: { + return cg.readEntry("OutgoingUserName"); + break; + } + case OutServerPass: { + return cg.readEntry("OutgoingPassword"); + break; + } + case OutServerType: { + return cg.readEntry("OutgoingServerType"); + break; + } + case OutServerCommand: { + return cg.readEntry("OutgoingCommand"); + break; + } + case OutServerTLS: { + return cg.readEntry("OutgoingServerTLS", QVariant(false)).toString(); + break; + } + case InServer: { + return cg.readEntry("IncomingServer"); + break; + } + case InServerLogin: { + return cg.readEntry("IncomingUserName"); + break; + } + case InServerPass: { + return cg.readEntry("IncomingPassword"); + break; + } + case InServerType: { + return cg.readEntry("IncomingServerType"); + break; + } + case InServerMBXType: { + return cg.readEntry("IncomingServerMBXType"); + break; + } + case InServerTLS: { + return cg.readEntry("IncomingServerTLS", QVariant(false)).toString(); + break; + } + }; + return QString(); +} +void KEMailSettings::setSetting(KEMailSettings::Setting s, const QString &v) +{ + KConfigGroup cg(p->m_pConfig, QStringLiteral("PROFILE_") + p->m_sCurrentProfile); + switch (s) { + case ClientProgram: { + cg.writePathEntry("EmailClient", v); + break; + } + case ClientTerminal: { + cg.writeEntry("TerminalClient", (v == QLatin1String("true"))); + break; + } + case RealName: { + cg.writeEntry("FullName", v); + break; + } + case EmailAddress: { + cg.writeEntry("EmailAddress", v); + break; + } + case ReplyToAddress: { + cg.writeEntry("ReplyAddr", v); + break; + } + case Organization: { + cg.writeEntry("Organization", v); + break; + } + case OutServer: { + cg.writeEntry("OutgoingServer", v); + break; + } + case OutServerLogin: { + cg.writeEntry("OutgoingUserName", v); + break; + } + case OutServerPass: { + cg.writeEntry("OutgoingPassword", v); + break; + } + case OutServerType: { + cg.writeEntry("OutgoingServerType", v); + break; + } + case OutServerCommand: { + cg.writeEntry("OutgoingCommand", v); + break; + } + case OutServerTLS: { + cg.writeEntry("OutgoingServerTLS", (v == QLatin1String("true"))); + break; + } + case InServer: { + cg.writeEntry("IncomingServer", v); + break; + } + case InServerLogin: { + cg.writeEntry("IncomingUserName", v); + break; + } + case InServerPass: { + cg.writeEntry("IncomingPassword", v); + break; + } + case InServerType: { + cg.writeEntry("IncomingServerType", v); + break; + } + case InServerMBXType: { + cg.writeEntry("IncomingServerMBXType", v); + break; + } + case InServerTLS: { + cg.writeEntry("IncomingServerTLS", (v == QLatin1String("true"))); + break; + } + }; + cg.sync(); +} + +void KEMailSettings::setDefault(const QString &s) +{ + p->m_pConfig->group("Defaults").writeEntry("Profile", s); + p->m_pConfig->sync(); + p->m_sDefaultProfile=s; + +} + +void KEMailSettings::setProfile (const QString &s) +{ + QString groupname = QStringLiteral("PROFILE_"); + groupname.append(s); + p->m_sCurrentProfile=s; + if (!p->m_pConfig->hasGroup(groupname)) { // Create a group if it doesn't exist + KConfigGroup cg(p->m_pConfig, groupname); + cg.writeEntry("ServerType", QString()); + p->profiles+=s; + } +} + +#ifndef KDE_NO_DEPRECATED +QString KEMailSettings::currentProfileName() const +{ + return p->m_sCurrentProfile; +} +#endif + +QStringList KEMailSettings::profiles() const +{ + return p->profiles; +} + +KEMailSettings::KEMailSettings() + :p(new KEMailSettingsPrivate()) +{ + p->m_sCurrentProfile.clear(); + + p->m_pConfig = new KConfig(QStringLiteral("emaildefaults")); + + const QStringList groups = p->m_pConfig->groupList(); + for (QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it) { + if ( (*it).startsWith( QLatin1String( "PROFILE_" ) ) ) + p->profiles+= (*it).mid(8, (*it).length()); + } + + KConfigGroup cg( p->m_pConfig, "Defaults"); + p->m_sDefaultProfile = cg.readEntry("Profile", tr("Default")); + if (!p->m_sDefaultProfile.isNull()) { + if (!p->m_pConfig->hasGroup(QStringLiteral("PROFILE_") + p->m_sDefaultProfile)) + setDefault(tr("Default")); + else + setDefault(p->m_sDefaultProfile); + } else { + if (p->profiles.count()) { + setDefault(p->profiles[0]); + } else + setDefault(tr("Default")); + } + setProfile(defaultProfileName()); +} + +KEMailSettings::~KEMailSettings() +{ + delete p; +} diff --git a/src/core/kemailsettings.h b/src/core/kemailsettings.h new file mode 100644 index 00000000..32610d61 --- /dev/null +++ b/src/core/kemailsettings.h @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2000 Alex Zepeda <zipzippy@sonic.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _KEMAILSETTINGS_H +#define _KEMAILSETTINGS_H + +#include <QtCore/QCoreApplication> // Q_DECLARE_TR_FUNCTIONS +#include <QtCore/QStringList> + +#include <kconfigcore_export.h> + +class KEMailSettingsPrivate; + + +/** + * This is just a small class to facilitate accessing e-mail settings in + * a sane way, and allowing any program to manage multiple e-mail + * profiles effortlessly + * + * The default profile is automatically selected in the constructor. + * + * @author Alex Zepeda zipzippy@sonic.net + **/ +class KCONFIGCORE_EXPORT KEMailSettings { + Q_DECLARE_TR_FUNCTIONS(KEMailSettings) +public: + /** + * The list of settings that I thought of when I wrote this + * class. Any extra settings thought of later can be accessed + * easily with getExtendedSetting and setExtendedSetting. + * @see getSetting() + * @see setSetting() + * @see getExtendedSetting() + * @see setExtendedSetting() + **/ + enum Setting { + ClientProgram, + ClientTerminal, + RealName, + EmailAddress, + ReplyToAddress, + Organization, + OutServer, + OutServerLogin, + OutServerPass, +#ifndef KDE_NO_DEPRECATED + /** + * @deprecated since Frameworks 5.0 + */ + OutServerType, + /** + * @deprecated since Frameworks 5.0 + */ + OutServerCommand, + /** + * @deprecated since Frameworks 5.0 + */ + OutServerTLS, +#endif + InServer, + InServerLogin, + InServerPass, +#ifndef KDE_NO_DEPRECATED + /** + * @deprecated since Frameworks 5.0 + */ + InServerType, + /** + * @deprecated since Frameworks 5.0 + */ + InServerMBXType, + /** + * @deprecated since Frameworks 5.0 + */ + InServerTLS +#endif + }; + + /** + * The various extensions allowed. + **/ + enum Extension { + POP3, + SMTP, + OTHER + }; + + /** + * Default constructor, just sets things up and sets the default profile + * as the current profile + **/ + KEMailSettings(); + + /** + * Default destructor, nothing to see here. + **/ + ~KEMailSettings(); + + /** + * List of profiles available. + * @return the list of profiles + **/ + QStringList profiles() const; + +#ifndef KDE_NO_DEPRECATED + /** + * @deprecated since Frameworks 5.0 + * Returns the name of the current profile. + * @returns what profile we're currently using + **/ + KCONFIGCORE_DEPRECATED QString currentProfileName() const; +#endif + + /** + * Change the current profile. + * @param s the name of the new profile + **/ + void setProfile (const QString &s); + + /** + * Returns the name of the default profile. + * @returns the name of the one that's currently default QString() if none + **/ + QString defaultProfileName() const; + + /** + * Sets a new default. + * @param def the new default + **/ + void setDefault(const QString &def); + + /** + * Get one of the predefined "basic" settings. + * @param s the setting to get + * @return the value of the setting, or QString() if not + * set + **/ + QString getSetting(KEMailSettings::Setting s) const; + + /** + * Set one of the predefined "basic" settings. + * @param s the setting to set + * @param v the new value of the setting, or QString() to + * unset + **/ + void setSetting(KEMailSettings::Setting s, const QString &v); + +private: + KEMailSettingsPrivate* const p; +}; + +#endif diff --git a/src/core/ksharedconfig.cpp b/src/core/ksharedconfig.cpp new file mode 100644 index 00000000..0e5d1959 --- /dev/null +++ b/src/core/ksharedconfig.cpp @@ -0,0 +1,122 @@ +/* + This file is part of the KDE libraries + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (C) 1997-1999 Matthias Kalle Dalheimer (kalle@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "ksharedconfig.h" +#include "kconfigbackend.h" +#include "kconfiggroup.h" +#include "kconfig_p.h" +#include <QCoreApplication> + +void _k_globalMainConfigSync(); + +class GlobalSharedConfigList : public QList<KSharedConfig*> +{ +public: + GlobalSharedConfigList() + { + // We want to force the sync() before the QCoreApplication + // instance is gone. Otherwise we trigger a QLockFile::lock() + // after QCoreApplication is gone, calling qAppName() for a non + // existent app... + qAddPostRoutine(&_k_globalMainConfigSync); + } + + // in addition to the list, we need to hold the main config, + // so that it's not created and destroyed all the time. + KSharedConfigPtr mainConfig; +}; + + +Q_GLOBAL_STATIC(GlobalSharedConfigList, globalSharedConfigList) + +void _k_globalMainConfigSync() +{ + if (globalSharedConfigList->mainConfig) + globalSharedConfigList->mainConfig->sync(); +} + + +KSharedConfigPtr KSharedConfig::openConfig(const QString& _fileName, + OpenFlags flags, + QStandardPaths::StandardLocation resType) +{ + QString fileName(_fileName); + GlobalSharedConfigList *list = globalSharedConfigList(); + if (fileName.isEmpty() && !flags.testFlag(KConfig::SimpleConfig)) { + // Determine the config file name that KConfig will make up (see KConfigPrivate::changeFileName) + fileName = KConfig::mainConfigName(); + } + + if (list) { + for(QList<KSharedConfig*>::ConstIterator it = list->constBegin(); it != list->constEnd(); ++it) { + if ( (*it)->name() == fileName && + (*it)->d_ptr->openFlags == flags && + (*it)->locationType() == resType +// (*it)->backEnd()->type() == backEnd + ) { + return KSharedConfigPtr(*it); + } + } + } + KSharedConfigPtr ptr(new KSharedConfig(fileName, flags, resType)); + if (_fileName.isEmpty() && flags == FullConfig && resType == QStandardPaths::GenericConfigLocation) { + list->mainConfig = ptr; + + static bool userWarned = false; + if (!userWarned) { + userWarned = true; + QByteArray readOnly = qgetenv("KDE_HOME_READONLY"); + if (readOnly.isEmpty() && QCoreApplication::applicationName() != QLatin1String("kdialog")) { + if (ptr->group("General").readEntry(QLatin1String("warn_unwritable_config"), true)) + ptr->isConfigWritable(true); + } + } + } + + return ptr; +} + + +KSharedConfig::KSharedConfig(const QString &fileName, + OpenFlags flags, + QStandardPaths::StandardLocation resType) + : KConfig(fileName, flags, resType) +{ + globalSharedConfigList()->append(this); +} + +KSharedConfig::~KSharedConfig() +{ + if (!globalSharedConfigList.isDestroyed()) + globalSharedConfigList()->removeAll(this); +} + +KConfigGroup KSharedConfig::groupImpl(const QByteArray &groupName) +{ + KSharedConfigPtr ptr(this); + return KConfigGroup( ptr, groupName.constData()); +} + +const KConfigGroup KSharedConfig::groupImpl(const QByteArray &groupName) const +{ + const KSharedConfigPtr ptr(const_cast<KSharedConfig*>(this)); + return KConfigGroup( ptr, groupName.constData()); +} diff --git a/src/core/ksharedconfig.h b/src/core/ksharedconfig.h new file mode 100644 index 00000000..42f7440e --- /dev/null +++ b/src/core/ksharedconfig.h @@ -0,0 +1,88 @@ +/* + This file is part of the KDE libraries + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (C) 1997-1999 Matthias Kalle Dalheimer (kalle@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KSHAREDCONFIG_H +#define KSHAREDCONFIG_H + +#include <kconfig.h> +#include <QExplicitlySharedDataPointer> + +/** + * \class KSharedConfig ksharedconfig.h <KSharedConfig> + * + * KConfig variant using shared memory + * + * KSharedConfig provides a reference counted, shared memory variant + * of KConfig. This allows you to use manipulate the same configuration + * files from different places in your code without worrying about + * accidentally overwriting changes. + * + * Note that, as with most of kdelibs, this is @b NOT threadsafe. + */ +class KCONFIGCORE_EXPORT KSharedConfig : public KConfig, public QSharedData //krazy:exclude=dpointer (only for refcounting) +{ +public: + typedef QExplicitlySharedDataPointer<KSharedConfig> Ptr; + +public: + /** + * Creates a KSharedConfig object to manipulate a configuration file + * + * If an absolute path is specified for @p fileName, that file will be used + * as the store for the configuration settings. If a non-absolute path + * is provided, the file will be looked for in the standard directory + * specified by resourceType. If no path is provided, a default + * configuration file will be used based on the name of the main + * application component. + * + * @p mode determines whether the user or global settings will be allowed + * to influence the values returned by this object. See KConfig::OpenFlags for + * more details. + * + * @param fileName the configuration file to open. If empty, it will be determined + * automatically (from --config on the command line, otherwise + * from the application name + "rc") + * @param mode how global settings should affect the configuration + * options exposed by this KConfig object + * @param resourceType The standard directory to look for the configuration + * file in (see KStandardDirs) + * + * @sa KConfig + */ + static KSharedConfig::Ptr openConfig(const QString& fileName = QString(), + OpenFlags mode = FullConfig, + QStandardPaths::StandardLocation type = QStandardPaths::GenericConfigLocation); + + virtual ~KSharedConfig(); + +private: + Q_DISABLE_COPY(KSharedConfig) + virtual KConfigGroup groupImpl(const QByteArray& aGroup); + virtual const KConfigGroup groupImpl(const QByteArray& aGroup) const; + + KSharedConfig(const QString& file, OpenFlags mode, + QStandardPaths::StandardLocation resourceType); + +}; + +typedef KSharedConfig::Ptr KSharedConfigPtr; + +#endif // multiple inclusion guard |