Mercurial > hg > svcore
diff base/ResourceFinder.cpp @ 679:c8badbd4c005
* Introduce ResourceFinder
author | Chris Cannam |
---|---|
date | Wed, 04 May 2011 14:05:08 +0100 |
parents | |
children | 27cdabba2d3e |
line wrap: on
line diff
--- /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 <QDir> +#include <QFileInfo> +#include <QStringList> +#include <QProcess> +#include <QCoreApplication> + +#include <cstdlib> +#include <iostream> + +/** + 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/<appname> or /usr/local/share/<appname> + - on Linux, in $HOME/.local/share/<appname> + + - on OS/X, in /Library/Application Support/<appname> + - on OS/X, in $HOME/Library/Application Support/<appname> + + - on Windows, in %ProgramFiles%/<company>/<appname> + - 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; +} +