Add locations AppDataLocation, AppLocalDataLocation to QStandardPaths.

On Windows, DataLocation currently returns the value obtained by
passing CSIDL_COMMON_APPDATA to SHGetSpecialFolderPath(). This is
the local non-roaming path. For actually storing settings, the roaming
path should be used (CSIDL_APPDATA). Introduce new AppDataLocation to
return that path and AppLocalDataLocation for the local path and deprecate
DataLocation.

[ChangeLog][QtCore][QStandardPaths] QStandardPaths now has new
enumeration values AppDataLocation, AppLocalDataLocation to be able
to differentiate between roaming and local paths on the Windows
operating system. DataLocation is deprecated in favor of AppDataLocation.

Task-number: QTBUG-38483
Change-Id: Ib1de8c7031a863ed5eac10c747de6f7ff1a090c7
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Friedemann Kleint 2014-04-24 16:32:49 +02:00 committed by The Qt Project
parent 32893fa9c4
commit f3bc9f5c5c
11 changed files with 92 additions and 47 deletions

View File

@ -117,10 +117,9 @@ QT_BEGIN_NAMESPACE
\value HomeLocation Returns the user's home directory (the same as QDir::homePath()). On Unix
systems, this is equal to the HOME environment variable. This value might be
generic or application-specific, but the returned path is never empty.
\value DataLocation Returns a directory location where persistent
application data can be stored. This is an application-specific directory. To obtain a
path to store data to be shared with other applications, use
QStandardPaths::GenericDataLocation. The returned path is never empty.
\value DataLocation Returns the same value as AppLocalDataLocation. This enumeration value
is deprecated. Using AppDataLocation is preferable since on Windows, the roaming path is
recommended.
\value CacheLocation Returns a directory location where user-specific
non-essential (cached) data should be written. This is an application-specific directory.
The returned path is never empty.
@ -142,6 +141,15 @@ QT_BEGIN_NAMESPACE
\value GenericConfigLocation Returns a directory location where user-specific
configuration files shared between multiple applications should be written.
This is a generic value and the returned path is never empty.
\value AppDataLocation Returns a directory location where persistent
application data can be stored. This is an application-specific directory.
To obtain a path to store data to be shared with other applications, use
QStandardPaths::GenericDataLocation. The returned path is never empty.
On the Windows operating system, this returns the roaming path.
This enum value was added in Qt 5.4.
\value AppLocalDataLocation Returns the local settings path on the Windows operating
system. On all other platforms, it returns the same value as AppDataLocation.
This enum value was added in Qt 5.4.
The following table gives examples of paths on different operating systems.
The first path is the writable path (unless noted). Other, additional
@ -200,6 +208,12 @@ QT_BEGIN_NAMESPACE
\row \li GenericCacheLocation
\li "~/Library/Caches", "/Library/Caches"
\li "C:/Users/<USER>/AppData/Local/cache"
\row \li AppDataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
\li "C:/Users/<USER>/AppData/Roaming/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
\row \li AppLocalDataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
\li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
\endtable
\table
@ -255,6 +269,12 @@ QT_BEGIN_NAMESPACE
\row \li GenericCacheLocation
\li "<APPROOT>/data/Cache" (there is no shared cache)
\li "~/.cache"
\row \li AppDataLocation
\li "<APPROOT>/data", "<APPROOT>/app/native/assets"
\li "~/.local/share/<APPNAME>", "/usr/local/share/<APPNAME>", "/usr/share/<APPNAME>"
\row \li AppLocalDataLocation
\li "<APPROOT>/data", "<APPROOT>/app/native/assets"
\li "~/.local/share/<APPNAME>", "/usr/local/share/<APPNAME>", "/usr/share/<APPNAME>"
\endtable
\table
@ -293,6 +313,8 @@ QT_BEGIN_NAMESPACE
\li "<USER>/Downloads", "<USER>/<APPNAME>/Downloads"
\row \li GenericCacheLocation
\li "<APPROOT>/cache" (there is no shared cache)
\row \li AppDataLocation
\li "<APPROOT>/files", "<USER>/<APPNAME>/files"
\endtable
In the table above, \c <APPNAME> is usually the organization name, the
@ -534,8 +556,6 @@ QString QStandardPaths::displayName(StandardLocation type)
return QCoreApplication::translate("QStandardPaths", "Temporary Directory");
case HomeLocation:
return QCoreApplication::translate("QStandardPaths", "Home");
case DataLocation:
return QCoreApplication::translate("QStandardPaths", "Application Data");
case CacheLocation:
return QCoreApplication::translate("QStandardPaths", "Cache");
case GenericDataLocation:
@ -550,6 +570,9 @@ QString QStandardPaths::displayName(StandardLocation type)
return QCoreApplication::translate("QStandardPaths", "Shared Cache");
case DownloadLocation:
return QCoreApplication::translate("QStandardPaths", "Download");
case AppDataLocation:
case AppLocalDataLocation:
return QCoreApplication::translate("QStandardPaths", "Application Data");
}
// not reached
return QString();

View File

@ -70,7 +70,9 @@ public:
ConfigLocation,
DownloadLocation,
GenericCacheLocation,
GenericConfigLocation
GenericConfigLocation,
AppDataLocation,
AppLocalDataLocation = DataLocation
};
static QString writableLocation(StandardLocation type);

View File

@ -244,7 +244,8 @@ QString QStandardPaths::writableLocation(StandardLocation type)
return getFilesDir() + testDir() + QLatin1String("/settings");
case QStandardPaths::GenericDataLocation:
return getExternalStorageDirectory() + testDir();
case QStandardPaths::DataLocation:
case QStandardPaths::AppDataLocation:
case QStandardPaths::AppLocalDataLocation:
return getFilesDir() + testDir();
case QStandardPaths::GenericCacheLocation:
case QStandardPaths::RuntimeLocation:
@ -301,7 +302,7 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
<< getExternalFilesDir("DIRECTORY_DOWNLOADS");
}
if (type == DataLocation) {
if (type == AppDataLocation || type == AppLocalDataLocation) {
return QStringList() << writableLocation(type)
<< getExternalFilesDir();
}

View File

@ -63,7 +63,8 @@ QString QStandardPaths::writableLocation(StandardLocation type)
const QString sharedRoot = sharedDir.absolutePath();
switch (type) {
case DataLocation:
case AppDataLocation:
case AppLocalDataLocation:
return QDir::homePath() + testModeInsert();
case DesktopLocation:
case HomeLocation:
@ -108,7 +109,7 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
if (type == FontsLocation)
return QStringList(QLatin1String("/base/usr/fonts"));
if (type == DataLocation)
if (type == AppDataLocation || type == AppLocalDataLocation)
dirs.append(QDir::homePath() + testModeInsert() + QLatin1String("native/assets"));
const QString localDir = writableLocation(type);

View File

@ -90,7 +90,8 @@ QString QStandardPaths::writableLocation(StandardLocation type)
case HomeLocation:
location = bundlePath();
break;
case DataLocation:
case AppDataLocation:
case AppLocalDataLocation:
case GenericDataLocation:
location = pathForDirectory(NSDocumentDirectory);
break;

View File

@ -83,7 +83,8 @@ OSType translateLocation(QStandardPaths::StandardLocation type)
return kTemporaryFolderType;
case QStandardPaths::GenericDataLocation:
case QStandardPaths::RuntimeLocation:
case QStandardPaths::DataLocation:
case QStandardPaths::AppDataLocation:
case QStandardPaths::AppLocalDataLocation:
return kApplicationSupportFolderType;
case QStandardPaths::GenericCacheLocation:
case QStandardPaths::CacheLocation:
@ -128,7 +129,7 @@ static QString macLocation(QStandardPaths::StandardLocation type, short domain)
QString path = getFullPath(ref);
if (type == QStandardPaths::DataLocation || type == QStandardPaths::CacheLocation)
if (type == QStandardPaths::AppDataLocation || type == QStandardPaths::AppLocalDataLocation || type == QStandardPaths::CacheLocation)
appendOrganizationAndApp(path);
return path;
}
@ -140,9 +141,10 @@ QString QStandardPaths::writableLocation(StandardLocation type)
QString path;
switch (type) {
case GenericDataLocation:
case DataLocation:
case AppDataLocation:
case AppLocalDataLocation:
path = qttestDir + QLatin1String("/Application Support");
if (type == DataLocation)
if (type != GenericDataLocation)
appendOrganizationAndApp(path);
return path;
case GenericCacheLocation:
@ -165,7 +167,7 @@ QString QStandardPaths::writableLocation(StandardLocation type)
case TempLocation:
return QDir::tempPath();
case GenericDataLocation:
case DataLocation:
case AppLocalDataLocation:
case GenericCacheLocation:
case CacheLocation:
case RuntimeLocation:
@ -179,13 +181,13 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
{
QStringList dirs;
if (type == GenericDataLocation || type == DataLocation || type == GenericCacheLocation || type == CacheLocation) {
if (type == GenericDataLocation || type == AppDataLocation || type == AppLocalDataLocation || type == GenericCacheLocation || type == CacheLocation) {
const QString path = macLocation(type, kOnAppropriateDisk);
if (!path.isEmpty())
dirs.append(path);
}
if (type == DataLocation) {
if (type == AppDataLocation || type == AppLocalDataLocation) {
CFBundleRef mainBundle = CFBundleGetMainBundle();
if (mainBundle) {
CFURLRef bundleUrl = CFBundleCopyBundleURL(mainBundle);

View File

@ -90,7 +90,8 @@ QString QStandardPaths::writableLocation(StandardLocation type)
appendOrganizationAndApp(xdgCacheHome);
return xdgCacheHome;
}
case DataLocation:
case AppDataLocation:
case AppLocalDataLocation:
case GenericDataLocation:
{
QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
@ -98,7 +99,7 @@ QString QStandardPaths::writableLocation(StandardLocation type)
xdgDataHome = QDir::homePath() + QLatin1String("/.qttest/share");
if (xdgDataHome.isEmpty())
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
if (type == QStandardPaths::DataLocation)
if (type == AppDataLocation || type == AppLocalDataLocation)
appendOrganizationAndApp(xdgDataHome);
return xdgDataHome;
}
@ -305,7 +306,8 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
for (int i = 0; i < dirs.count(); ++i)
dirs[i].append(QLatin1String("/applications"));
break;
case DataLocation:
case AppDataLocation:
case AppLocalDataLocation:
dirs = xdgDataDirs();
for (int i = 0; i < dirs.count(); ++i)
appendOrganizationAndApp(dirs[i]);

View File

@ -81,6 +81,18 @@ static QString convertCharArray(const wchar_t *path)
return QDir::fromNativeSeparators(QString::fromWCharArray(path));
}
static inline int clsidForAppDataLocation(QStandardPaths::StandardLocation type)
{
#ifndef Q_OS_WINCE
return type == QStandardPaths::AppDataLocation ?
CSIDL_APPDATA : // "Roaming" path
CSIDL_LOCAL_APPDATA; // Local path
#else
Q_UNUSED(type)
return CSIDL_APPDATA;
#endif
}
QString QStandardPaths::writableLocation(StandardLocation type)
{
QString result;
@ -92,15 +104,12 @@ QString QStandardPaths::writableLocation(StandardLocation type)
wchar_t path[MAX_PATH];
switch (type) {
case ConfigLocation: // same as DataLocation, on Windows (oversight, but too late to fix it)
case GenericConfigLocation: // same as GenericDataLocation, on Windows
case DataLocation:
case ConfigLocation: // same as AppLocalDataLocation, on Windows
case GenericConfigLocation: // same as GenericDataLocation on Windows
case AppDataLocation:
case AppLocalDataLocation:
case GenericDataLocation:
#if defined Q_OS_WINCE
if (SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, FALSE))
#else
if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE))
#endif
if (SHGetSpecialFolderPath(0, path, clsidForAppDataLocation(type), FALSE))
result = convertCharArray(path);
if (isTestModeEnabled())
result += QLatin1String("/qttest");
@ -165,7 +174,7 @@ QString QStandardPaths::writableLocation(StandardLocation type)
// Although Microsoft has a Cache key it is a pointer to IE's cache, not a cache
// location for everyone. Most applications seem to be using a
// cache directory located in their AppData directory
return writableLocation(DataLocation) + QLatin1String("/cache");
return writableLocation(AppLocalDataLocation) + QLatin1String("/cache");
case GenericCacheLocation:
return writableLocation(GenericDataLocation) + QLatin1String("/cache");
@ -192,11 +201,12 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
{
wchar_t path[MAX_PATH];
switch (type) {
case ConfigLocation: // same as DataLocation, on Windows (oversight, but too late to fix it)
case ConfigLocation: // same as AppLocalDataLocation, on Windows (oversight, but too late to fix it)
case GenericConfigLocation: // same as GenericDataLocation, on Windows
case DataLocation:
case AppDataLocation:
case AppLocalDataLocation:
case GenericDataLocation:
if (SHGetSpecialFolderPath(0, path, CSIDL_COMMON_APPDATA, FALSE)) {
if (SHGetSpecialFolderPath(0, path, clsidForAppDataLocation(type), FALSE)) {
QString result = convertCharArray(path);
if (type != GenericDataLocation && type != GenericConfigLocation) {
#ifndef QT_BOOTSTRAPPED

View File

@ -73,9 +73,10 @@ QString QStandardPaths::writableLocation(StandardLocation type)
QString result;
switch (type) {
case ConfigLocation: // same as DataLocation, on Windows
case ConfigLocation: // same as AppLocalDataLocation, on Windows
case GenericConfigLocation: // same as GenericDataLocation, on Windows
case DataLocation:
case AppDataLocation:
case AppLocalDataLocation:
case GenericDataLocation: {
ComPtr<IApplicationDataStatics> applicationDataStatics;
if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics)))
@ -98,7 +99,7 @@ QString QStandardPaths::writableLocation(StandardLocation type)
break;
}
case CacheLocation:
return writableLocation(DataLocation) + QLatin1String("/cache");
return writableLocation(AppLocalDataLocation) + QLatin1String("/cache");
case GenericCacheLocation:
return writableLocation(GenericDataLocation) + QLatin1String("/cache");

View File

@ -314,7 +314,7 @@ extern Q_CORE_EXPORT QString qt_applicationName_noFallback();
QString QDesktopServices::storageLocationImpl(QStandardPaths::StandardLocation type)
{
if (type == QStandardPaths::DataLocation) {
if (type == QStandardPaths::AppLocalDataLocation) {
// Preserve Qt 4 compatibility:
// * QCoreApplication::applicationName() must default to empty
// * Unix data location is under the "data/" subdirectory

View File

@ -56,7 +56,7 @@
#define Q_XDG_PLATFORM
#endif
static const int MaxStandardLocation = QStandardPaths::GenericConfigLocation;
static const int MaxStandardLocation = QStandardPaths::AppDataLocation;
class tst_qstandardpaths : public QObject
{
@ -129,7 +129,8 @@ static const char * const enumNames[MaxStandardLocation + 1 - int(QStandardPaths
"ConfigLocation",
"DownloadLocation",
"GenericCacheLocation",
"GenericConfigLocation"
"GenericConfigLocation",
"AppDataLocation"
};
void tst_qstandardpaths::dump()
@ -238,7 +239,8 @@ void tst_qstandardpaths::enableTestMode()
// Check this for locations where test programs typically write. Not desktop, download, music etc...
typedef QHash<QStandardPaths::StandardLocation, QString> LocationHash;
LocationHash testLocations;
testLocations.insert(QStandardPaths::DataLocation, QStandardPaths::writableLocation(QStandardPaths::DataLocation));
testLocations.insert(QStandardPaths::AppDataLocation, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
testLocations.insert(QStandardPaths::AppLocalDataLocation, QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
testLocations.insert(QStandardPaths::GenericDataLocation, QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation));
testLocations.insert(QStandardPaths::ConfigLocation, QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
testLocations.insert(QStandardPaths::GenericConfigLocation, QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
@ -294,18 +296,18 @@ void tst_qstandardpaths::testDataLocation()
// applications are sandboxed.
#if !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT)
const QString base = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::DataLocation), base + "/tst_qstandardpaths");
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), base + "/tst_qstandardpaths");
QCoreApplication::instance()->setOrganizationName("Qt");
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::DataLocation), base + "/Qt/tst_qstandardpaths");
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), base + "/Qt/tst_qstandardpaths");
QCoreApplication::instance()->setApplicationName("QtTest");
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::DataLocation), base + "/Qt/QtTest");
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), base + "/Qt/QtTest");
#endif
#ifdef Q_XDG_PLATFORM
setDefaultLocations();
const QString expectedAppDataDir = QDir::homePath() + QString::fromLatin1("/.local/share/Qt/QtTest");
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::DataLocation), expectedAppDataDir);
const QStringList appDataDirs = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), expectedAppDataDir);
const QStringList appDataDirs = QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation);
QCOMPARE(appDataDirs.count(), 3);
QCOMPARE(appDataDirs.at(0), expectedAppDataDir);
QCOMPARE(appDataDirs.at(1), QString::fromLatin1("/usr/local/share/Qt/QtTest"));
@ -463,7 +465,7 @@ void tst_qstandardpaths::testAllWritableLocations_data()
QTest::newRow("PicturesLocation") << QStandardPaths::PicturesLocation;
QTest::newRow("TempLocation") << QStandardPaths::TempLocation;
QTest::newRow("HomeLocation") << QStandardPaths::HomeLocation;
QTest::newRow("DataLocation") << QStandardPaths::DataLocation;
QTest::newRow("AppLocalDataLocation") << QStandardPaths::AppLocalDataLocation;
QTest::newRow("DownloadLocation") << QStandardPaths::DownloadLocation;
}