QStandardPaths: add Config and GenericData, add methods
authorDavid Faure <faure@kde.org>
Fri, 21 Oct 2011 18:54:35 +0000 (20:54 +0200)
committerQt by Nokia <qt-info@nokia.com>
Sat, 22 Oct 2011 22:56:39 +0000 (00:56 +0200)
New methods: standardLocations, locate, locateAll.

Change-Id: I60bc90f8df53727a72c4b1839ea4d1d88a204e29
Reviewed-by: Thiago Macieira (Intel) <thiago.macieira@intel.com>
src/corelib/io/qstandardpaths.cpp
src/corelib/io/qstandardpaths.h
src/corelib/io/qstandardpaths_mac.cpp
src/corelib/io/qstandardpaths_unix.cpp
src/corelib/io/qstandardpaths_win.cpp
tests/auto/corelib/io/io.pro

index e441a21..58a6ab1 100644 (file)
@@ -65,7 +65,8 @@ QT_BEGIN_NAMESPACE
     \enum QStandardPaths::StandardLocation
 
     This enum describes the different locations that can be queried using
-    methods such as QStandardPaths::storageLocation and QStandardPaths::displayName.
+    methods such as QStandardPaths::storageLocation, QStandardPaths::standardLocations,
+    and QStandardPaths::displayName.
 
     \value DesktopLocation Returns the user's desktop directory.
     \value DocumentsLocation Returns the user's document.
@@ -77,19 +78,24 @@ QT_BEGIN_NAMESPACE
     \value TempLocation Returns the system's temporary directory.
     \value HomeLocation Returns the user's home directory.
     \value DataLocation Returns a directory location where persistent
-           application data can be stored. QCoreApplication::applicationName
-           and QCoreApplication::organizationName should work on all
-           platforms.
+           application data can be stored. QCoreApplication::organizationName
+           and QCoreApplication::applicationName are appended to the directory location
+           returned for GenericDataLocation.
     \value CacheLocation Returns a directory location where user-specific
            non-essential (cached) data should be written.
+    \value GenericDataLocation Returns a directory location where persistent
+           data shared across applications can be stored.
+    \value ConfigLocation Returns a directory location where user-specific
+           configuration files should be written.
 
-    \sa storageLocation() displayName()
+
+    \sa storageLocation() standardLocations() displayName() locate() locateAll()
 */
 
 /*!
     \fn QString QStandardPaths::storageLocation(StandardLocation type)
 
-    Returns the default system directory where files of \a type belong, or an empty string
+    Returns the directory where files of \a type should be written to, or an empty string
     if the location cannot be determined.
 
     \note The storage location returned can be a directory that does not exist; i.e., it
@@ -102,6 +108,74 @@ QT_BEGIN_NAMESPACE
     that if executable is in ROM the folder from C drive is returned.
 */
 
+
+/*!
+   \fn QStringList QStandardPaths::standardLocations(StandardLocation type)
+
+   Returns all the directories where files of \a type belong.
+
+   Much like the PATH variable, it returns the directories in order of priority,
+   starting with the user-specific storageLocation() for the \a type.
+ */
+
+// TODO add XDG_RUNTIME_DIR?
+
+/*!
+    \enum QStandardPaths::LocateOption
+
+    This enum describes the different flags that can be used for
+    controlling the behavior of QStandardPaths::locate and
+    QStandardPaths::locateAll.
+
+    \value LocateFile return only files
+    \value LocateDirectory return only directories
+*/
+
+static bool existsAsSpecified(const QString &path, QStandardPaths::LocateOptions options)
+{
+    if (options & QStandardPaths::LocateDirectory)
+        return QDir(path).exists();
+    return QFileInfo(path).isFile();
+}
+
+/*!
+   Tries to find a file or directory called \a fileName in the standard locations
+   for \a type.
+
+   The full path to the first file or directory (depending on \a options) found is returned.
+   If no such file or directory can be found, an empty string is returned.
+ */
+QString QStandardPaths::locate(StandardLocation type, const QString &fileName, LocateOptions options)
+{
+    const QStringList &dirs = standardLocations(type);
+    for (QStringList::const_iterator dir = dirs.constBegin(); dir != dirs.constEnd(); ++dir) {
+        const QString path = *dir + QLatin1Char('/') + fileName;
+        if (existsAsSpecified(path, options))
+            return path;
+    }
+    return QString();
+}
+
+/*!
+   Tries to find all files or directories called \a fileName in the standard locations
+   for \a type.
+
+   The \a options flag allows to specify whether to look for files or directories.
+
+   Returns the list of all the files that were found.
+ */
+QStringList QStandardPaths::locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
+{
+    const QStringList &dirs = standardLocations(type);
+    QStringList result;
+    for (QStringList::const_iterator dir = dirs.constBegin(); dir != dirs.constEnd(); ++dir) {
+        const QString path = *dir + QLatin1Char('/') + fileName;
+        if (existsAsSpecified(path, options))
+            result.append(path);
+    }
+    return result;
+}
+
 /*!
     \fn QString QStandardPaths::displayName(StandardLocation type)
 
index 5b1bb82..1688e16 100644 (file)
@@ -69,11 +69,28 @@ public:
         TempLocation,
         HomeLocation,
         DataLocation,
-        CacheLocation
+        CacheLocation,
+        GenericDataLocation,
+        ConfigLocation
     };
 
     static QString storageLocation(StandardLocation type);
+    static QStringList standardLocations(StandardLocation type);
+
+    enum LocateOption {
+        LocateFile = 0x0,
+        LocateDirectory = 0x1
+    };
+    Q_DECLARE_FLAGS(LocateOptions, LocateOption)
+
+    static QString locate(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile);
+    static QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile);
     static QString displayName(StandardLocation type);
+
+private:
+    // prevent construction
+    QStandardPaths();
+    ~QStandardPaths();
 };
 
 #endif // QT_NO_STANDARDPATHS
index b90cc7c..a13162c 100644 (file)
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
 OSType translateLocation(QStandardPaths::StandardLocation type)
 {
     switch (type) {
+    case QStandardPaths::ConfigLocation:
+        return kPreferencesFolderType;
     case QStandardPaths::DesktopLocation:
         return kDesktopFolderType;
     case QStandardPaths::DocumentsLocation:
@@ -74,6 +76,7 @@ OSType translateLocation(QStandardPaths::StandardLocation type)
         return kPictureDocumentsFolderType;
     case QStandardPaths::TempLocation:
         return kTemporaryFolderType;
+    case QStandardPaths::GenericDataLocation:
     case QStandardPaths::DataLocation:
         return kApplicationSupportFolderType;
     case QStandardPaths::CacheLocation:
@@ -94,35 +97,54 @@ static QString getFullPath(const FSRef &ref)
     return QString();
 }
 
-QString QStandardPaths::storageLocation(StandardLocation type)
+static QString macLocation(QStandardPaths::StandardLocation type, short domain)
 {
-     if (type == HomeLocation)
-        return QDir::homePath();
-
-     if (type == TempLocation)
-         return QDir::tempPath();
+    // http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
+    FSRef ref;
+    OSErr err = FSFindFolder(domain, translateLocation(type), false, &ref);
+    if (err)
+       return QString();
 
-    short domain = kOnAppropriateDisk;
+   QString path = getFullPath(ref);
 
-    if (type == DataLocation || type == CacheLocation)
-        domain = kUserDomain;
+   if (type == QStandardPaths::DataLocation || type == QStandardPaths::CacheLocation) {
+       if (!QCoreApplication::organizationName().isEmpty())
+           path += QLatin1Char('/') + QCoreApplication::organizationName();
+       if (!QCoreApplication::applicationName().isEmpty())
+           path += QLatin1Char('/') + QCoreApplication::applicationName();
+   }
+   return path;
+}
 
-     // http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
-     FSRef ref;
-     OSErr err = FSFindFolder(domain, translateLocation(type), false, &ref);
-     if (err)
-        return QString();
+QString QStandardPaths::storageLocation(StandardLocation type)
+{
+    switch (type) {
+    case HomeLocation:
+        return QDir::homePath();
+    case TempLocation:
+        return QDir::tempPath();
+    case GenericDataLocation:
+    case DataLocation:
+    case CacheLocation:
+        return macLocation(type, kUserDomain);
+    default:
+        return macLocation(type, kOnAppropriateDisk);
+    }
+}
 
-    QString path = getFullPath(ref);
+QStringList QStandardPaths::standardLocations(StandardLocation type)
+{
+    QStringList dirs;
 
-    if (type == DataLocation || type == CacheLocation) {
-        if (QCoreApplication::organizationName().isEmpty() == false)
-            path += QLatin1Char('/') + QCoreApplication::organizationName();
-        if (QCoreApplication::applicationName().isEmpty() == false)
-            path += QLatin1Char('/') + QCoreApplication::applicationName();
+    if (type == GenericDataLocation || type == DataLocation || type == CacheLocation) {
+        const QString path = macLocation(type, kOnAppropriateDisk);
+        if (!path.isEmpty())
+            dirs.append(path);
     }
 
-    return path;
+    const QString localDir = storageLocation(type);
+    dirs.prepend(localDir);
+    return dirs;
 }
 
 QString QStandardPaths::displayName(StandardLocation type)
index f0c5005..5a52c25 100644 (file)
@@ -54,30 +54,48 @@ QT_BEGIN_NAMESPACE
 
 QString QStandardPaths::storageLocation(StandardLocation type)
 {
-    if (type == QStandardPaths::HomeLocation)
+    switch (type) {
+    case HomeLocation:
         return QDir::homePath();
-    if (type == QStandardPaths::TempLocation)
+    case TempLocation:
         return QDir::tempPath();
-
-    // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
-    if (type == QStandardPaths::CacheLocation) {
-        QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME"));
+    case CacheLocation:
+    {
+        // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
+        QString xdgCacheHome = QFile::decodeName(qgetenv("XDG_CACHE_HOME"));
         if (xdgCacheHome.isEmpty())
             xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
-        xdgCacheHome += QLatin1Char('/') + QCoreApplication::organizationName()
-                    + QLatin1Char('/') + QCoreApplication::applicationName();
+        if (!QCoreApplication::organizationName().isEmpty())
+            xdgCacheHome += QLatin1Char('/') + QCoreApplication::organizationName();
+        if (!QCoreApplication::applicationName().isEmpty())
+            xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName();
         return xdgCacheHome;
     }
-
-    if (type == QStandardPaths::DataLocation) {
-        QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME"));
+    case DataLocation:
+    case GenericDataLocation:
+    {
+        QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
         if (xdgDataHome.isEmpty())
             xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
-        xdgDataHome += QLatin1String("/data/")
-                    + QCoreApplication::organizationName() + QLatin1Char('/')
-                    + QCoreApplication::applicationName();
+        if (type == QStandardPaths::DataLocation) {
+            if (!QCoreApplication::organizationName().isEmpty())
+                xdgDataHome += QLatin1Char('/') + QCoreApplication::organizationName();
+            if (!QCoreApplication::applicationName().isEmpty())
+                xdgDataHome += QLatin1Char('/') + QCoreApplication::applicationName();
+        }
         return xdgDataHome;
     }
+    case ConfigLocation:
+    {
+        // http://standards.freedesktop.org/basedir-spec/latest/
+        QString xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
+        if (xdgConfigHome.isEmpty())
+            xdgConfigHome = QDir::homePath() + QLatin1String("/.config");
+        return xdgConfigHome;
+    }
+    default:
+        break;
+    }
 
     // http://www.freedesktop.org/wiki/Software/xdg-user-dirs
     QString xdgConfigHome = QLatin1String(qgetenv("XDG_CONFIG_HOME"));
@@ -90,7 +108,7 @@ QString QStandardPaths::storageLocation(StandardLocation type)
         // Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop"
         QRegExp exp(QLatin1String("^XDG_(.*)_DIR=(.*)$"));
         while (!stream.atEnd()) {
-            QString line = stream.readLine();
+            const QString &line = stream.readLine();
             if (exp.indexIn(line) != -1) {
                 const QStringList lst = exp.capturedTexts();
                 const QString key = lst.at(1);
@@ -106,23 +124,35 @@ QString QStandardPaths::storageLocation(StandardLocation type)
 
         QString key;
         switch (type) {
-        case DesktopLocation: key = QLatin1String("DESKTOP"); break;
-        case DocumentsLocation: key = QLatin1String("DOCUMENTS"); break;
-        case PicturesLocation: key = QLatin1String("PICTURES"); break;
-        case MusicLocation: key = QLatin1String("MUSIC"); break;
-        case MoviesLocation: key = QLatin1String("VIDEOS"); break;
-        default: break;
+        case DesktopLocation:
+            key = QLatin1String("DESKTOP");
+            break;
+        case DocumentsLocation:
+            key = QLatin1String("DOCUMENTS");
+            break;
+        case PicturesLocation:
+            key = QLatin1String("PICTURES");
+            break;
+        case MusicLocation:
+            key = QLatin1String("MUSIC");
+            break;
+        case MoviesLocation:
+            key = QLatin1String("VIDEOS");
+            break;
+        default:
+            break;
         }
-        if (!key.isEmpty() && lines.contains(key)) {
-            QString value = lines[key];
-            // value can start with $HOME
-            if (value.startsWith(QLatin1String("$HOME")))
-                value = QDir::homePath() + value.mid(5);
-            return value;
+        if (!key.isEmpty()) {
+            QString value = lines.value(key);
+            if (!value.isEmpty()) {
+                // value can start with $HOME
+                if (value.startsWith(QLatin1String("$HOME")))
+                    value = QDir::homePath() + value.mid(5);
+                return value;
+            }
         }
     }
 
-    QDir emptyDir;
     QString path;
     switch (type) {
     case DesktopLocation:
@@ -148,6 +178,9 @@ QString QStandardPaths::storageLocation(StandardLocation type)
         break;
 
     case ApplicationsLocation:
+        path = storageLocation(GenericDataLocation) + QLatin1String("/applications");
+        break;
+
     default:
         break;
     }
@@ -155,6 +188,30 @@ QString QStandardPaths::storageLocation(StandardLocation type)
     return path;
 }
 
+QStringList QStandardPaths::standardLocations(StandardLocation type)
+{
+    QStringList dirs;
+    if (type == ConfigLocation) {
+        // http://standards.freedesktop.org/basedir-spec/latest/
+        QString xdgConfigDirs = QFile::decodeName(qgetenv("XDG_CONFIG_DIRS"));
+        if (xdgConfigDirs.isEmpty())
+            dirs.append(QString::fromLatin1("/etc/xdg"));
+        else
+            dirs = xdgConfigDirs.split(QLatin1Char(':'));
+    } else if (type == GenericDataLocation) {
+        // http://standards.freedesktop.org/basedir-spec/latest/
+        QString xdgConfigDirs = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
+        if (xdgConfigDirs.isEmpty()) {
+            dirs.append(QString::fromLatin1("/usr/local/share"));
+            dirs.append(QString::fromLatin1("/usr/share"));
+        } else
+            dirs = xdgConfigDirs.split(QLatin1Char(':'));
+    }
+    const QString localDir = storageLocation(type);
+    dirs.prepend(localDir);
+    return dirs;
+}
+
 QString QStandardPaths::displayName(StandardLocation type)
 {
     Q_UNUSED(type);
index 7d7fbd0..497e09e 100644 (file)
 
 QT_BEGIN_NAMESPACE
 
-QString QStandardPaths::storageLocation(StandardLocation type)
+typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPWSTR, int, BOOL);
+static GetSpecialFolderPath resolveGetSpecialFolderPath()
 {
-    QString result;
-
+    static GetSpecialFolderPath gsfp = 0;
+    if (!gsfp) {
 #ifndef Q_OS_WINCE
         QSystemLibrary library(QLatin1String("shell32"));
 #else
         QSystemLibrary library(QLatin1String("coredll"));
 #endif // Q_OS_WINCE
-    typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPWSTR, int, BOOL);
-    static GetSpecialFolderPath SHGetSpecialFolderPath =
-            (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW");
+        gsfp = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW");
+    }
+    return gsfp;
+}
+
+static QString convertCharArray(const wchar_t *path)
+{
+    return QDir::fromNativeSeparators(QString::fromWCharArray(path));
+}
+
+QString QStandardPaths::storageLocation(StandardLocation type)
+{
+    QString result;
+
+    static GetSpecialFolderPath SHGetSpecialFolderPath = resolveGetSpecialFolderPath();
     if (!SHGetSpecialFolderPath)
         return QString();
 
     wchar_t path[MAX_PATH];
 
     switch (type) {
+    case ConfigLocation: // same as DataLocation, on Windows
     case DataLocation:
+    case GenericDataLocation:
 #if defined Q_OS_WINCE
         if (SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, FALSE))
 #else
         if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE))
 #endif
-            result = QString::fromWCharArray(path);
-        if (!QCoreApplication::organizationName().isEmpty())
-            result = result + QLatin1String("\\") + QCoreApplication::organizationName();
-        if (!QCoreApplication::applicationName().isEmpty())
-            result = result + QLatin1String("\\") + QCoreApplication::applicationName();
+            result = convertCharArray(path);
+        if (type != GenericDataLocation) {
+            if (!QCoreApplication::organizationName().isEmpty())
+                result += QLatin1Char('/') + QCoreApplication::organizationName();
+            if (!QCoreApplication::applicationName().isEmpty())
+                result += QLatin1Char('/') + QCoreApplication::applicationName();
+        }
         break;
 
     case DesktopLocation:
         if (SHGetSpecialFolderPath(0, path, CSIDL_DESKTOPDIRECTORY, FALSE))
-            result = QString::fromWCharArray(path);
+            result = convertCharArray(path);
         break;
 
     case DocumentsLocation:
         if (SHGetSpecialFolderPath(0, path, CSIDL_PERSONAL, FALSE))
-            result = QString::fromWCharArray(path);
+            result = convertCharArray(path);
         break;
 
     case FontsLocation:
         if (SHGetSpecialFolderPath(0, path, CSIDL_FONTS, FALSE))
-            result = QString::fromWCharArray(path);
+            result = convertCharArray(path);
         break;
 
     case ApplicationsLocation:
         if (SHGetSpecialFolderPath(0, path, CSIDL_PROGRAMS, FALSE))
-            result = QString::fromWCharArray(path);
+            result = convertCharArray(path);
         break;
 
     case MusicLocation:
         if (SHGetSpecialFolderPath(0, path, CSIDL_MYMUSIC, FALSE))
-            result = QString::fromWCharArray(path);
+            result = convertCharArray(path);
         break;
 
     case MoviesLocation:
         if (SHGetSpecialFolderPath(0, path, CSIDL_MYVIDEO, FALSE))
-            result = QString::fromWCharArray(path);
+            result = convertCharArray(path);
         break;
 
     case PicturesLocation:
         if (SHGetSpecialFolderPath(0, path, CSIDL_MYPICTURES, FALSE))
-            result = QString::fromWCharArray(path);
+            result = convertCharArray(path);
         break;
 
     case CacheLocation:
@@ -156,6 +173,42 @@ QString QStandardPaths::storageLocation(StandardLocation type)
     return result;
 }
 
+QStringList QStandardPaths::standardLocations(StandardLocation type)
+{
+    QStringList dirs;
+
+    // type-specific handling goes here
+
+#ifndef Q_WS_WINCE
+    static GetSpecialFolderPath SHGetSpecialFolderPath = resolveGetSpecialFolderPath();
+    if (SHGetSpecialFolderPath) {
+        wchar_t path[MAX_PATH];
+        switch (type) {
+        case ConfigLocation: // same as DataLocation, on Windows
+        case DataLocation:
+        case GenericDataLocation:
+            if (SHGetSpecialFolderPath(0, path, CSIDL_COMMON_APPDATA, FALSE)) {
+                QString result = convertCharArray(path);
+                if (type != GenericDataLocation) {
+                    if (!QCoreApplication::organizationName().isEmpty())
+                        result += QLatin1Char('/') + QCoreApplication::organizationName();
+                    if (!QCoreApplication::applicationName().isEmpty())
+                        result += QLatin1Char('/') + QCoreApplication::applicationName();
+                }
+                dirs.append(result);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+#endif
+
+    const QString localDir = storageLocation(type);
+    dirs.prepend(localDir);
+    return dirs;
+}
+
 QString QStandardPaths::displayName(StandardLocation type)
 {
     Q_UNUSED(type);
index cbe2b60..fdb8b99 100644 (file)
@@ -15,6 +15,7 @@ SUBDIRS=\
     qprocessenvironment \
     qresourceengine \
     qsettings \
+    qstandardpaths \
     qtemporaryfile \
     qtextstream \
     qurl \