Mercurial > hg > svcore
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 |