# HG changeset patch # User Chris Cannam # Date 1304514308 -3600 # Node ID c8badbd4c00586a58e1ba940841133bd75d18de7 # Parent 948271d124ac1aca33e67c9cd032645eeb226969 * Introduce ResourceFinder diff -r 948271d124ac -r c8badbd4c005 base/ResourceFinder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/ResourceFinder.cpp Wed May 04 14:05:08 2011 +0100 @@ -0,0 +1,300 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +/* + This is a modified version of a source file from the + Rosegarden MIDI and audio sequencer and notation editor. + This file copyright 2005-2011 Chris Cannam and the Rosegarden + development team. +*/ + +#include "ResourceFinder.h" + +#include +#include +#include +#include +#include + +#include +#include + +/** + Resource files may be found in three places: + + * Bundled into the application as Qt4 resources. These may be + opened using Qt classes such as QFile, with "fake" file paths + starting with a colon. For example ":icons/fileopen.png". + + * Installed with the package, or in the user's equivalent home + directory location. For example, + + - on Linux, in /usr/share/ or /usr/local/share/ + - on Linux, in $HOME/.local/share/ + + - on OS/X, in /Library/Application Support/ + - on OS/X, in $HOME/Library/Application Support/ + + - on Windows, in %ProgramFiles%// + - on Windows, in (where?) + + These locations are searched in reverse order (user-installed + copies take priority over system-installed copies take priority + over bundled copies). Also, /usr/local takes priority over /usr. +*/ + +QStringList +ResourceFinder::getSystemResourcePrefixList() +{ + // returned in order of priority + + QStringList list; + +#ifdef Q_OS_WIN32 + char *programFiles = getenv("ProgramFiles"); + if (programFiles && programFiles[0]) { + list << QString("%1/%2/%3") + .arg(programFiles) + .arg(qApp->organizationName()) + .arg(qApp->applicationName()); + } else { + list << QString("C:/Program Files/%1/%2") + .arg(qApp->organizationName()) + .arg(qApp->applicationName()); + } +#else +#ifdef Q_OS_MAC + list << QString("/Library/Application Support/%1/%2") + .arg(qApp->organizationName()) + .arg(qApp->applicationName()); +#else + list << QString("/usr/local/share/%1") + .arg(qApp->applicationName()); + list << QString("/usr/share/%1") + .arg(qApp->applicationName()); +#endif +#endif + + return list; +} + +QString +ResourceFinder::getUserResourcePrefix() +{ + char *home = getenv("HOME"); + if (!home || !home[0]) return ""; + +#ifdef Q_OS_WIN32 + return QString(); //!!!??? +#else +#ifdef Q_OS_MAC + return QString("%1/Library/Application Support/%2/%3") + .arg(home) + .arg(qApp->organizationName()) + .arg(qApp->applicationName()); +#else + return QString("%1/.local/share/%2") + .arg(home) + .arg(qApp->applicationName()); +#endif +#endif +} + +QStringList +ResourceFinder::getResourcePrefixList() +{ + // returned in order of priority + + QStringList list; + + QString user = getUserResourcePrefix(); + if (user != "") list << user; + + list << getSystemResourcePrefixList(); + + list << ":"; // bundled resource location + + return list; +} + +QString +ResourceFinder::getResourcePath(QString resourceCat, QString fileName) +{ + // We don't simply call getResourceDir here, because that returns + // only the "installed file" location. We also want to search the + // bundled resources and user-saved files. + + QStringList prefixes = getResourcePrefixList(); + + if (resourceCat != "") resourceCat = "/" + resourceCat; + + for (QStringList::const_iterator i = prefixes.begin(); + i != prefixes.end(); ++i) { + + QString prefix = *i; + + std::cerr << "ResourceFinder::getResourcePath: Looking up file \"" << fileName.toStdString() << "\" for category \"" << resourceCat.toStdString() << "\" in prefix \"" << prefix.toStdString() << "\"" << std::endl; + + QString path = + QString("%1%2/%3").arg(prefix).arg(resourceCat).arg(fileName); + if (QFileInfo(path).exists() && QFileInfo(path).isReadable()) { + std::cerr << "Found it!" << std::endl; + return path; + } + } + + return ""; +} + +QString +ResourceFinder::getResourceDir(QString resourceCat) +{ + // Returns only the "installed file" location + + QStringList prefixes = getSystemResourcePrefixList(); + + if (resourceCat != "") resourceCat = "/" + resourceCat; + + for (QStringList::const_iterator i = prefixes.begin(); + i != prefixes.end(); ++i) { + + QString prefix = *i; + QString path = QString("%1%2").arg(prefix).arg(resourceCat); + if (QFileInfo(path).exists() && + QFileInfo(path).isDir() && + QFileInfo(path).isReadable()) { + return path; + } + } + + return ""; +} + +QString +ResourceFinder::getResourceSavePath(QString resourceCat, QString fileName) +{ + QString dir = getResourceSaveDir(resourceCat); + if (dir == "") return ""; + + return dir + "/" + fileName; +} + +QString +ResourceFinder::getResourceSaveDir(QString resourceCat) +{ + // Returns the "user" location + + QString user = getUserResourcePrefix(); + if (user == "") return ""; + + if (resourceCat != "") resourceCat = "/" + resourceCat; + + QDir userDir(user); + if (!userDir.exists()) { + if (!userDir.mkpath(user)) { + std::cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << user.toStdString() << "\"" << std::endl; + return ""; + } + } + + if (resourceCat != "") { + QString save = QString("%1%2").arg(user).arg(resourceCat); + QDir saveDir(save); + if (!saveDir.exists()) { + if (!userDir.mkpath(save)) { + std::cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << save.toStdString() << "\"" << std::endl; + return ""; + } + } + return save; + } else { + return user; + } +} + +QStringList +ResourceFinder::getResourceFiles(QString resourceCat, QString fileExt) +{ + QStringList results; + QStringList prefixes = getResourcePrefixList(); + + QStringList filters; + filters << QString("*.%1").arg(fileExt); + + for (QStringList::const_iterator i = prefixes.begin(); + i != prefixes.end(); ++i) { + + QString prefix = *i; + QString path; + + if (resourceCat != "") { + path = QString("%1/%2").arg(prefix).arg(resourceCat); + } else { + path = prefix; + } + + QDir dir(path); + if (!dir.exists()) continue; + + dir.setNameFilters(filters); + QStringList entries = dir.entryList + (QDir::Files | QDir::Readable, QDir::Name); + + for (QStringList::const_iterator j = entries.begin(); + j != entries.end(); ++j) { + results << QString("%1/%2").arg(path).arg(*j); + } + } + + return results; +} + +bool +ResourceFinder::unbundleResource(QString resourceCat, QString fileName) +{ + QString path = getResourcePath(resourceCat, fileName); + + if (!path.startsWith(':')) return true; + + // This is the lowest-priority alternative path for this + // resource, so we know that there must be no installed copy. + // Install one to the user location. + std::cerr << "ResourceFinder::unbundleResource: File " << fileName.toStdString() << " is bundled, un-bundling it" << std::endl; + QString target = getResourceSavePath(resourceCat, fileName); + QFile file(path); + if (!file.copy(target)) { + std::cerr << "ResourceFinder::unbundleResource: ERROR: Failed to un-bundle resource file \"" << fileName.toStdString() << "\" to user location \"" << target.toStdString() << "\"" << std::endl; + return false; + } + + // Now since the file is in the user's editable space, the user should get + // to edit it. The chords.xml file I unbundled came out 444 instead of 644 + // which won't do. Rather than put the chmod code there, I decided to put + // it here, because I think it will always be appropriate to make unbundled + // files editable. That's rather the point in many cases, and for the rest, + // nobody will likely notice they could have edited their font files or what + // have you that were unbundled to improve performance. (Dissenting + // opinions welcome. We can always shuffle this somewhere else if + // necessary. There are many possibilities.) + QFile chmod(target); + chmod.setPermissions(QFile::ReadOwner | + QFile::ReadUser | /* for potential platform-independence */ + QFile::ReadGroup | + QFile::ReadOther | + QFile::WriteOwner| + QFile::WriteUser); /* for potential platform-independence */ + + return true; +} + diff -r 948271d124ac -r c8badbd4c005 base/ResourceFinder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/ResourceFinder.h Wed May 04 14:05:08 2011 +0100 @@ -0,0 +1,117 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +/* + This is a modified version of a source file from the + Rosegarden MIDI and audio sequencer and notation editor. + This file copyright 2005-2011 Chris Cannam and the Rosegarden + development team. +*/ + +#ifndef _RESOURCE_FINDER_H_ +#define _RESOURCE_FINDER_H_ + +#include + +class ResourceFinder +{ +public: + ResourceFinder() { } + virtual ~ResourceFinder() { } + + /** + * Return the location (as a true file path, or a Qt4 ":"-prefixed + * resource path) of the file best matching the given resource + * filename in the given resource category. + * + * Category should be a relative directory path without leading or + * trailing slashes, for example "chords". The fileName is the + * remainder of the file name without any path content, for + * example "user_chords.xml". + * + * Returns an empty string if no matching resource is found. + * + * Use this when you know that a particular resource is required + * and just need to locate it. + */ + QString getResourcePath(QString resourceCat, QString fileName); + + /** + * Return a list of full file paths for files with the given file + * extension, found in the given resource category. + * + * Category should be a relative directory path without leading or + * trailing slashes, for example "chords". File extension should + * be the extension without the dot, for example "xml". Returned + * list may mix true file paths in both installed and user + * locations with Qt4 ":"-prefixed resource paths. + * + * Use this when you need to enumerate the options available for + * use directly in the program (rather than e.g. offering the user + * a file-open dialog). + */ + QStringList getResourceFiles(QString resourceCat, QString fileExt); + + /** + * Return the true file path for installed resource files in the + * given resource category. Category should be a relative + * directory path without leading or trailing slashes, for example + * "chords". Note that resources may also exist in the Qt4 + * resource bundle; this method only returns the external + * (installed) resource location. Use getResourceFiles instead to + * return an authoritative list of available resources of a given + * type. + * + * Use this when you need a file path, e.g. for use in a file + * finder dialog. + */ + QString getResourceDir(QString resourceCat); + + /** + * Return the true file path for the location in which the named + * resource file in the given resource category should be saved. + */ + QString getResourceSavePath(QString resourceCat, QString fileName); + + /** + * Return the true file path for the location in which resource + * files in the given resource category should be saved. + */ + QString getResourceSaveDir(QString resourceCat); + + /** + * If the named resource file in the given resource category is + * available only as a bundled resource, copy it out into the user + * location returned by getResourceSavePath so that it can be read + * by non-Qt code. Any subsequent call to getResourcePath for + * this resource should return a true file path (if the resource + * exists) in either user or system location, or an empty string + * (if the resource does not exist), but never a ":"-prefixed + * resource path. This function does not overwrite any existing + * unbundled copy of the resource. + * + * Return false if a system error occurs during unbundling + * (e.g. disk full). + */ + bool unbundleResource(QString resourceCat, QString fileName); + +protected: + QString getUserResourcePrefix(); + QStringList getSystemResourcePrefixList(); + QStringList getResourcePrefixList(); +}; + +#endif + + diff -r 948271d124ac -r c8badbd4c005 svcore.pro --- a/svcore.pro Thu Apr 14 15:20:27 2011 +0100 +++ b/svcore.pro Wed May 04 14:05:08 2011 +0100 @@ -43,6 +43,7 @@ base/RecentFiles.h \ base/Resampler.h \ base/ResizeableBitset.h \ + base/ResourceFinder.h \ base/RingBuffer.h \ base/Scavenger.h \ base/Selection.h \ @@ -75,6 +76,7 @@ base/RealTime.cpp \ base/RecentFiles.cpp \ base/Resampler.cpp \ + base/ResourceFinder.cpp \ base/Selection.cpp \ base/Serialiser.cpp \ base/StorageAdviser.cpp \ diff -r 948271d124ac -r c8badbd4c005 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Thu Apr 14 15:20:27 2011 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Wed May 04 14:05:08 2011 +0100 @@ -519,7 +519,7 @@ } error = fftModels[ch]->getError(); if (error != "") { - std::cerr << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << std::endl; + std::cerr << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error.toStdString() << std::endl; m_abandoned = true; m_message = error; }