comparison base/ResourceFinder.cpp @ 679:c8badbd4c005

* Introduce ResourceFinder
author Chris Cannam
date Wed, 04 May 2011 14:05:08 +0100
parents
children 27cdabba2d3e
comparison
equal deleted inserted replaced
678:948271d124ac 679:c8badbd4c005
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version. See the file
12 COPYING included with this distribution for more information.
13 */
14
15 /*
16 This is a modified version of a source file from the
17 Rosegarden MIDI and audio sequencer and notation editor.
18 This file copyright 2005-2011 Chris Cannam and the Rosegarden
19 development team.
20 */
21
22 #include "ResourceFinder.h"
23
24 #include <QDir>
25 #include <QFileInfo>
26 #include <QStringList>
27 #include <QProcess>
28 #include <QCoreApplication>
29
30 #include <cstdlib>
31 #include <iostream>
32
33 /**
34 Resource files may be found in three places:
35
36 * Bundled into the application as Qt4 resources. These may be
37 opened using Qt classes such as QFile, with "fake" file paths
38 starting with a colon. For example ":icons/fileopen.png".
39
40 * Installed with the package, or in the user's equivalent home
41 directory location. For example,
42
43 - on Linux, in /usr/share/<appname> or /usr/local/share/<appname>
44 - on Linux, in $HOME/.local/share/<appname>
45
46 - on OS/X, in /Library/Application Support/<appname>
47 - on OS/X, in $HOME/Library/Application Support/<appname>
48
49 - on Windows, in %ProgramFiles%/<company>/<appname>
50 - on Windows, in (where?)
51
52 These locations are searched in reverse order (user-installed
53 copies take priority over system-installed copies take priority
54 over bundled copies). Also, /usr/local takes priority over /usr.
55 */
56
57 QStringList
58 ResourceFinder::getSystemResourcePrefixList()
59 {
60 // returned in order of priority
61
62 QStringList list;
63
64 #ifdef Q_OS_WIN32
65 char *programFiles = getenv("ProgramFiles");
66 if (programFiles && programFiles[0]) {
67 list << QString("%1/%2/%3")
68 .arg(programFiles)
69 .arg(qApp->organizationName())
70 .arg(qApp->applicationName());
71 } else {
72 list << QString("C:/Program Files/%1/%2")
73 .arg(qApp->organizationName())
74 .arg(qApp->applicationName());
75 }
76 #else
77 #ifdef Q_OS_MAC
78 list << QString("/Library/Application Support/%1/%2")
79 .arg(qApp->organizationName())
80 .arg(qApp->applicationName());
81 #else
82 list << QString("/usr/local/share/%1")
83 .arg(qApp->applicationName());
84 list << QString("/usr/share/%1")
85 .arg(qApp->applicationName());
86 #endif
87 #endif
88
89 return list;
90 }
91
92 QString
93 ResourceFinder::getUserResourcePrefix()
94 {
95 char *home = getenv("HOME");
96 if (!home || !home[0]) return "";
97
98 #ifdef Q_OS_WIN32
99 return QString(); //!!!???
100 #else
101 #ifdef Q_OS_MAC
102 return QString("%1/Library/Application Support/%2/%3")
103 .arg(home)
104 .arg(qApp->organizationName())
105 .arg(qApp->applicationName());
106 #else
107 return QString("%1/.local/share/%2")
108 .arg(home)
109 .arg(qApp->applicationName());
110 #endif
111 #endif
112 }
113
114 QStringList
115 ResourceFinder::getResourcePrefixList()
116 {
117 // returned in order of priority
118
119 QStringList list;
120
121 QString user = getUserResourcePrefix();
122 if (user != "") list << user;
123
124 list << getSystemResourcePrefixList();
125
126 list << ":"; // bundled resource location
127
128 return list;
129 }
130
131 QString
132 ResourceFinder::getResourcePath(QString resourceCat, QString fileName)
133 {
134 // We don't simply call getResourceDir here, because that returns
135 // only the "installed file" location. We also want to search the
136 // bundled resources and user-saved files.
137
138 QStringList prefixes = getResourcePrefixList();
139
140 if (resourceCat != "") resourceCat = "/" + resourceCat;
141
142 for (QStringList::const_iterator i = prefixes.begin();
143 i != prefixes.end(); ++i) {
144
145 QString prefix = *i;
146
147 std::cerr << "ResourceFinder::getResourcePath: Looking up file \"" << fileName.toStdString() << "\" for category \"" << resourceCat.toStdString() << "\" in prefix \"" << prefix.toStdString() << "\"" << std::endl;
148
149 QString path =
150 QString("%1%2/%3").arg(prefix).arg(resourceCat).arg(fileName);
151 if (QFileInfo(path).exists() && QFileInfo(path).isReadable()) {
152 std::cerr << "Found it!" << std::endl;
153 return path;
154 }
155 }
156
157 return "";
158 }
159
160 QString
161 ResourceFinder::getResourceDir(QString resourceCat)
162 {
163 // Returns only the "installed file" location
164
165 QStringList prefixes = getSystemResourcePrefixList();
166
167 if (resourceCat != "") resourceCat = "/" + resourceCat;
168
169 for (QStringList::const_iterator i = prefixes.begin();
170 i != prefixes.end(); ++i) {
171
172 QString prefix = *i;
173 QString path = QString("%1%2").arg(prefix).arg(resourceCat);
174 if (QFileInfo(path).exists() &&
175 QFileInfo(path).isDir() &&
176 QFileInfo(path).isReadable()) {
177 return path;
178 }
179 }
180
181 return "";
182 }
183
184 QString
185 ResourceFinder::getResourceSavePath(QString resourceCat, QString fileName)
186 {
187 QString dir = getResourceSaveDir(resourceCat);
188 if (dir == "") return "";
189
190 return dir + "/" + fileName;
191 }
192
193 QString
194 ResourceFinder::getResourceSaveDir(QString resourceCat)
195 {
196 // Returns the "user" location
197
198 QString user = getUserResourcePrefix();
199 if (user == "") return "";
200
201 if (resourceCat != "") resourceCat = "/" + resourceCat;
202
203 QDir userDir(user);
204 if (!userDir.exists()) {
205 if (!userDir.mkpath(user)) {
206 std::cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << user.toStdString() << "\"" << std::endl;
207 return "";
208 }
209 }
210
211 if (resourceCat != "") {
212 QString save = QString("%1%2").arg(user).arg(resourceCat);
213 QDir saveDir(save);
214 if (!saveDir.exists()) {
215 if (!userDir.mkpath(save)) {
216 std::cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << save.toStdString() << "\"" << std::endl;
217 return "";
218 }
219 }
220 return save;
221 } else {
222 return user;
223 }
224 }
225
226 QStringList
227 ResourceFinder::getResourceFiles(QString resourceCat, QString fileExt)
228 {
229 QStringList results;
230 QStringList prefixes = getResourcePrefixList();
231
232 QStringList filters;
233 filters << QString("*.%1").arg(fileExt);
234
235 for (QStringList::const_iterator i = prefixes.begin();
236 i != prefixes.end(); ++i) {
237
238 QString prefix = *i;
239 QString path;
240
241 if (resourceCat != "") {
242 path = QString("%1/%2").arg(prefix).arg(resourceCat);
243 } else {
244 path = prefix;
245 }
246
247 QDir dir(path);
248 if (!dir.exists()) continue;
249
250 dir.setNameFilters(filters);
251 QStringList entries = dir.entryList
252 (QDir::Files | QDir::Readable, QDir::Name);
253
254 for (QStringList::const_iterator j = entries.begin();
255 j != entries.end(); ++j) {
256 results << QString("%1/%2").arg(path).arg(*j);
257 }
258 }
259
260 return results;
261 }
262
263 bool
264 ResourceFinder::unbundleResource(QString resourceCat, QString fileName)
265 {
266 QString path = getResourcePath(resourceCat, fileName);
267
268 if (!path.startsWith(':')) return true;
269
270 // This is the lowest-priority alternative path for this
271 // resource, so we know that there must be no installed copy.
272 // Install one to the user location.
273 std::cerr << "ResourceFinder::unbundleResource: File " << fileName.toStdString() << " is bundled, un-bundling it" << std::endl;
274 QString target = getResourceSavePath(resourceCat, fileName);
275 QFile file(path);
276 if (!file.copy(target)) {
277 std::cerr << "ResourceFinder::unbundleResource: ERROR: Failed to un-bundle resource file \"" << fileName.toStdString() << "\" to user location \"" << target.toStdString() << "\"" << std::endl;
278 return false;
279 }
280
281 // Now since the file is in the user's editable space, the user should get
282 // to edit it. The chords.xml file I unbundled came out 444 instead of 644
283 // which won't do. Rather than put the chmod code there, I decided to put
284 // it here, because I think it will always be appropriate to make unbundled
285 // files editable. That's rather the point in many cases, and for the rest,
286 // nobody will likely notice they could have edited their font files or what
287 // have you that were unbundled to improve performance. (Dissenting
288 // opinions welcome. We can always shuffle this somewhere else if
289 // necessary. There are many possibilities.)
290 QFile chmod(target);
291 chmod.setPermissions(QFile::ReadOwner |
292 QFile::ReadUser | /* for potential platform-independence */
293 QFile::ReadGroup |
294 QFile::ReadOther |
295 QFile::WriteOwner|
296 QFile::WriteUser); /* for potential platform-independence */
297
298 return true;
299 }
300