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