diff options
Diffstat (limited to 'src/corelib/io')
-rw-r--r-- | src/corelib/io/io.pri | 7 | ||||
-rw-r--r-- | src/corelib/io/qfileselector.cpp | 383 | ||||
-rw-r--r-- | src/corelib/io/qfileselector.h | 72 | ||||
-rw-r--r-- | src/corelib/io/qfileselector_p.h | 84 |
4 files changed, 544 insertions, 2 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 3688642bcb..dfaaad51ba 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -47,7 +47,9 @@ HEADERS += \ io/qfilesystementry_p.h \ io/qfilesystemengine_p.h \ io/qfilesystemmetadata_p.h \ - io/qfilesystemiterator_p.h + io/qfilesystemiterator_p.h \ + io/qfileselector.h \ + io/qfileselector_p.h SOURCES += \ io/qabstractfileengine.cpp \ @@ -83,7 +85,8 @@ SOURCES += \ io/qfilesystemwatcher.cpp \ io/qfilesystemwatcher_polling.cpp \ io/qfilesystementry.cpp \ - io/qfilesystemengine.cpp + io/qfilesystemengine.cpp \ + io/qfileselector.cpp win32 { SOURCES += io/qsettings_win.cpp diff --git a/src/corelib/io/qfileselector.cpp b/src/corelib/io/qfileselector.cpp new file mode 100644 index 0000000000..6e91497817 --- /dev/null +++ b/src/corelib/io/qfileselector.cpp @@ -0,0 +1,383 @@ +/*************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfileselector.h" +#include "qfileselector_p.h" + +#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QMutex> +#include <QtCore/QMutexLocker> +#include <QtCore/QUrl> +#include <QtCore/QFileInfo> +#include <QtCore/QLocale> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +//Environment variable to allow tooling full control of file selectors +static const char env_override[] = "QT_NO_BUILTIN_SELECTORS"; + +static const ushort selectorIndicator = '+'; + +Q_GLOBAL_STATIC(QFileSelectorSharedData, sharedData); +static QBasicMutex sharedDataMutex; + +QFileSelectorPrivate::QFileSelectorPrivate() + : QObjectPrivate() +{ +} + +/*! + \class QFileSelector + \inmodule QtCore + \brief QFileSelector provides a convenient way of selecting file variants + \since 5.2 + + QFileSelector is a convenience for selecting file variants based on platform or device + characteristics. This allows you to develop and deploy one codebase containing all the + different variants more easily in some circumstances, such as when the correct variant cannot + be determined during the deploy step. + + Consider the following example usage, where you want to use different settings files on + different locales. You might select code between locales like this: + + \code + QString defaultsBasePath = "data/"; + QString defaultsPath = defaultsBasePath + "defaults.conf"; + QString localizedPath = defaultsBasePath + + QString("%1/defaults.conf").arg(QLocale::system().name()); + if (QFile::exists(localizedPath)) + defaultsPath = localizedPath; + QFile defaults(defaultsPath); + \endcode + + Similarly, if you want to pick a different data file based on target platform, + your code might look something like this: + \code + QString defaultsPath = "data/defaults.conf"; +#if defined(Q_OS_LINUX_ANDROID) + defaultsPath = "data/android/defaults.conf"; +#elif defined(Q_OS_BLACKBERRY) + defaultsPath = "data/blackberry/defaults.conf"; +#elif defined(Q_OS_IOS) + defaultsPath = "data/ios/defaults.conf"; +#endif + QFile defaults(defaultsPath); + \endcode + + QFileSelector provides a convenient alternative to writing such boilerplate code, and in the + latter case it allows you to start using an platform-specific configuration without a recompile. + QFileSelector also allows for chaining of multiple selectors in a convenient way, for example + selecting a different file only on certain combinations of platform and locale. For example, to + select based on platform and/or locale, the code is as follows: + + \code + QFileSelector selector; + QFile defaultsFile(selector.select("data/defaults.conf")); + \endcode + + The files to be selected are placed in directories named with a '+' and a selector name. In the above + example you could have the platform configurations selected by placing them in the following locations: + \code + data/defaults.conf + data/+android/defaults.conf + data/+blackberry/defaults.conf + data/+ios/+en_GB/defaults.conf + \endcode + + If you always use the same file you do not need to use QFileSelector. + + To find selected files, QFileSelector looks in the same directory as the base file. If there are + any directories of the form +<selector> with an active selector, QFileSelector will prefer a file + with the same file name from that directory over the base file. These directories can be nested to + check against multiple selectors, for example: + \code + images/background.png + images/+android/+en_GB/background.png + images/+blackberry/+en_GB/background.png + \endcode + With those files available, you would select a different file on android and blackberry platforms, + but only if the locale was en_GB. + + Selectors normally available are + \list + \li platform, one of: android, blackberry, ios, windows, mac, linux, generic_unix or unknown. + \li locale, same as QLocale::system().name(). + \endlist + + Further selectors will be added from the QT_FILE_SELECTORS environment variable, which when + set should be a set of colon (semi-colon on windows) separated selectors. Note that this + variable will only be read once, selectors may not update if the variable changes while the + application is running. + + The initial set of selectors are evaluated only once, on first use. In a future + version, some may be marked as deploy-time static and be moved during the deployment step as an + optimization. As selectors come with a performance cost, it is recommended to avoid their use + in circumstances involving performance-critical code. + + Additionally you can add extra selectors at runtime to customize behavior further. These will + be used in any future calls to select(). If the extra selectors list has been changed, calls to + select() will use the new list and may return differently. + + When multiple selectors could be applied to the same file, the first matching selector is chosen. + The order selectors are checked in are: + + 1. Selectors set via setExtraSelectors(), in the order they are in the list + 2. Selectors in the QT_FILE_SELECTORS environment variable, from left to right + 3. Locale + 4. Platform + + Here is an example involving multiple selectors matching at the same time. It uses platform + selectors, plus an extra selector named "admin" is set by the application based on user + credentials. The example is sorted so that the lowest matching file would be chosen if all + selectors were present: + + \code + images/background.png + images/+linux/background.png + images/+windows/background.png + images/+admin/background.png + images/+admin/+linux/background.png + \endcode + + Because extra selectors are checked before platform the +admin/background.png will be chosen + on windows when the admin selector is set, and +windows/background.png will be chosen on + windows when the admin selector is not set. On linux, the +admin/+linux/background.png will be + chosen when admin is set, and the +linux/background.png when it is not. + + QFileSelector will not attempt to select if the base file does not exist. For error handling in + the case no valid selectors are present, it is recommended to have a default or error-handling + file in the base file location even if you expect selectors to be present for all deployments. +*/ + +/*! + Create a QFileSelector instance. This instance will have the same static selectors as other + QFileSelector instances, but its own set of extra selectors. + + If supplied, it will have the given QObject \a parent. +*/ +QFileSelector::QFileSelector(QObject *parent) + : QObject(*(new QFileSelectorPrivate()), parent) +{ +} + +/*! + Destroys this selector instance. +*/ +QFileSelector::~QFileSelector() +{ +} + +/*! + This function returns the selected version of the path, based on the conditions at runtime. + If no selectable files are present, returns the original \a filePath. + + If the original file does not exist, the original \a filePath is returned. This means that you + must have a base file to fall back on, you cannot have only files in selectable sub-directories. + + See the class overview for the selection algorithm. +*/ +QString QFileSelector::select(const QString &filePath) const +{ + Q_D(const QFileSelector); + return d->select(filePath); +} + +static QString qrcScheme() +{ + return QStringLiteral("qrc"); +} + +/*! + This is a convenience version of select operating on QUrls. If the scheme is not file or qrc, + \a filePath is returned immediately. Otherwise selection is applied to the path of \a filePath + and a QUrl is returned with the selected path and other QUrl parts the same as \a filePath. + + See the class overview for the selection algorithm. +*/ +QUrl QFileSelector::select(const QUrl &filePath) const +{ + Q_D(const QFileSelector); + if (filePath.scheme() != qrcScheme() && !filePath.isLocalFile()) + return filePath; + QUrl ret(filePath); + if (filePath.scheme() == qrcScheme()) { + QString equivalentPath = QLatin1Char(':') + filePath.path(); + QString selectedPath = d->select(equivalentPath); + ret.setPath(selectedPath.remove(0, 1)); + } else { + ret = QUrl::fromLocalFile(d->select(ret.toLocalFile())); + } + return ret; +} + +static QString selectionHelper(const QString &path, const QString &fileName, const QStringList &selectors) +{ + /* selectionHelper does a depth-first search of possible selected files. Because there is strict + selector ordering in the API, we can stop checking as soon as we find the file in a directory + which does not contain any other valid selector directories. + */ + Q_ASSERT(path.isEmpty() || path.endsWith(QLatin1Char('/'))); + + foreach (const QString &s, selectors) { + QString prospectiveBase = path + QLatin1Char(selectorIndicator) + s + QLatin1Char('/'); + QStringList remainingSelectors = selectors; + remainingSelectors.removeAll(s); + if (!QDir(prospectiveBase).exists()) + continue; + QString prospectiveFile = selectionHelper(prospectiveBase, fileName, remainingSelectors); + if (!prospectiveFile.isEmpty()) + return prospectiveFile; + } + + // If we reach here there were no successful files found at a lower level in this branch, so we + // should check this level as a potential result. + if (!QFile::exists(path + fileName)) + return QString(); + return path + fileName; +} + +QString QFileSelectorPrivate::select(const QString &filePath) const +{ + Q_Q(const QFileSelector); + QFileInfo fi(filePath); + // If file doesn't exist, don't select + if (!fi.exists()) + return filePath; + + QString ret = selectionHelper(fi.path().isEmpty() ? QString() : fi.path() + QLatin1Char('/'), + fi.fileName(), q->allSelectors()); + + if (!ret.isEmpty()) + return ret; + return filePath; +} + +/*! + Returns the list of extra selectors which have been added programmatically to this instance. +*/ +QStringList QFileSelector::extraSelectors() const +{ + Q_D(const QFileSelector); + return d->extras; +} + +/*! + Sets the \a list of extra selectors which have been added programmatically to this instance. + + These selectors have priority over any which have been automatically picked up. +*/ +void QFileSelector::setExtraSelectors(const QStringList &list) +{ + Q_D(QFileSelector); + d->extras = list; +} + +/*! + Returns the complete, ordered list of selectors used by this instance +*/ +QStringList QFileSelector::allSelectors() const +{ + Q_D(const QFileSelector); + QMutexLocker locker(&sharedDataMutex); + QFileSelectorPrivate::updateSelectors(); + return d->extras + sharedData->staticSelectors; +} + +void QFileSelectorPrivate::updateSelectors() +{ + if (!sharedData->staticSelectors.isEmpty()) + return; //Already loaded + +#if defined(Q_OS_WIN) + QLatin1Char pathSep(';'); +#else + QLatin1Char pathSep(':'); +#endif + QStringList envSelectors = QString::fromLatin1(qgetenv("QT_FILE_SELECTORS")) + .split(pathSep, QString::SkipEmptyParts); + if (envSelectors.count()) + sharedData->staticSelectors << envSelectors; + + if (!qgetenv(env_override).isEmpty()) + return; + + sharedData->staticSelectors << sharedData->preloadedStatics; //Potential for static selectors from other modules + + // TODO: Update on locale changed? + sharedData->staticSelectors << QLocale::system().name(); + + sharedData->staticSelectors << platformSelectors(); +} + +QStringList QFileSelectorPrivate::platformSelectors() +{ + QStringList ret; +#if defined(Q_OS_LINUX_ANDROID) + ret << QStringLiteral("android"); +#elif defined(Q_OS_BLACKBERRY) + ret << QStringLiteral("blackberry"); +#elif defined(Q_OS_IOS) + ret << QStringLiteral("ios"); +#elif defined(Q_OS_WINCE) + ret << QStringLiteral("wince"); +#elif defined(Q_OS_WIN) + ret << QStringLiteral("windows"); +#elif defined(Q_OS_LINUX) + ret << QStringLiteral("linux"); +#elif defined(Q_OS_MAC) + ret << QStringLiteral("mac"); +#elif defined(Q_OS_UNIX) + ret << QStringLiteral("generic_unix"); +#endif + return ret; +} + +void QFileSelectorPrivate::addStatics(const QStringList &statics) +{ + QMutexLocker locker(&sharedDataMutex); + sharedData->preloadedStatics << statics; +} + +QT_END_NAMESPACE + +#include "moc_qfileselector.cpp" diff --git a/src/corelib/io/qfileselector.h b/src/corelib/io/qfileselector.h new file mode 100644 index 0000000000..9afd985757 --- /dev/null +++ b/src/corelib/io/qfileselector.h @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESELECTOR_H +#define QFILESELECTOR_H + +#include <QtCore/QObject> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +class QFileSelectorPrivate; +class Q_CORE_EXPORT QFileSelector : public QObject +{ + Q_OBJECT +public: + QFileSelector(QObject *parent = 0); + ~QFileSelector(); + + QString select(const QString &filePath) const; + QUrl select(const QUrl &filePath) const; + + QStringList extraSelectors() const; + void setExtraSelectors(const QStringList &list); + + QStringList allSelectors() const; + +private: + Q_DECLARE_PRIVATE(QFileSelector) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/io/qfileselector_p.h b/src/corelib/io/qfileselector_p.h new file mode 100644 index 0000000000..db118545c4 --- /dev/null +++ b/src/corelib/io/qfileselector_p.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESELECTOR_P_H +#define QFILESELECTOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QString> +#include <QtCore/QFileSelector> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +struct QFileSelectorSharedData //Not QSharedData because currently is just a global store +{ + QStringList staticSelectors; + QStringList preloadedStatics; +}; + +class Q_CORE_EXPORT QFileSelectorPrivate : QObjectPrivate //Exported for use in other modules (like QtGui) +{ + Q_DECLARE_PUBLIC(QFileSelector) +public: + static void updateSelectors(); + static QStringList platformSelectors(); + static void addStatics(const QStringList &); //For loading GUI statics from other Qt modules + QFileSelectorPrivate(); + QString select(const QString &filePath) const; + + QStringList extras; +}; + +QT_END_NAMESPACE + +#endif + |