1 /*
2 * Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
3 * Copyright (c) 2015 Friedrich W. H. Kossebau <kossebau@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "KoResourcePaths.h"
21
22 #include <QGlobalStatic>
23 #include <QStringList>
24 #include <QHash>
25 #include <QStandardPaths>
26 #include <QDir>
27 #include <QFileInfo>
28 #include <QDebug>
29 #include <QSet>
30
31
32 #ifdef Q_OS_WIN
33 static const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
34 #else
35 static const Qt::CaseSensitivity cs = Qt::CaseSensitive;
36 #endif
37
38 class KoResourcePathsImpl
39 {
40 public:
mapTypeToQStandardPaths(const QString & type)41 static QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type)
42 {
43 return
44 type == QLatin1String("data") ? QStandardPaths::GenericDataLocation :
45 type == QLatin1String("config") ? QStandardPaths::GenericConfigLocation :
46 type == QLatin1String("cache") ? QStandardPaths::CacheLocation :
47 type == QLatin1String("tmp") ? QStandardPaths::TempLocation :
48 type == QLatin1String("appdata") ? QStandardPaths::DataLocation :
49 type == QLatin1String("locale") ? QStandardPaths::GenericDataLocation :
50 /* default */ QStandardPaths::GenericDataLocation;
51 }
52
53 KoResourcePathsImpl() = default;
54 ~KoResourcePathsImpl() = default;
55
56 void addResourceTypeInternal(const QString &type, const QString &basetype,
57 const QString &relativeName, bool priority);
58
59 void addResourceDirInternal(const QString &type, const QString &absdir, bool priority);
60
61 QString findResourceInternal(const QString &type, const QString &fileName);
62
63 QStringList findDirsInternal(const QString &type, const QString &relDir);
64
65 QStringList findAllResourcesInternal(const QString &type,
66 const QString &filter = QString(),
67 KoResourcePaths::SearchOptions options = KoResourcePaths::NoSearchOptions) const;
68
69 QStringList resourceDirsInternal(const QString &type);
70
71 QString saveLocationInternal(const QString &type, const QString &suffix = QString(), bool create = true);
72
73 QString locateLocalInternal(const QString &type, const QString &filename, bool createDir = false);
74
75 private:
76 QHash<QString, QStringList> m_absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global
77 QHash<QString, QStringList> m_relatives; // Same with relative paths
78 };
79
addResourceTypeInternal(const QString & type,const QString & basetype,const QString & relativename,bool priority)80 void KoResourcePathsImpl::addResourceTypeInternal(const QString &type, const QString &basetype,
81 const QString &relativename,
82 bool priority)
83 {
84 if (relativename.isEmpty()) return;
85
86 QString copy = relativename;
87
88 Q_ASSERT(basetype == "data");
89
90 if (!copy.endsWith(QLatin1Char('/'))) {
91 copy += QLatin1Char('/');
92 }
93
94 QStringList &rels = m_relatives[type]; // find or insert
95
96 if (!rels.contains(copy, cs)) {
97 if (priority) {
98 rels.prepend(copy);
99 } else {
100 rels.append(copy);
101 }
102 }
103
104 //qDebug() << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << m_relatives[type];
105 }
106
addResourceDirInternal(const QString & type,const QString & absdir,bool priority)107 void KoResourcePathsImpl::addResourceDirInternal(const QString &type, const QString &absdir, bool priority)
108 {
109 if (absdir.isEmpty() || type.isEmpty()) return;
110
111 // find or insert entry in the map
112 QString copy = absdir;
113 if (!copy.endsWith(QLatin1Char('/'))) {
114 copy += QLatin1Char('/');
115 }
116
117 QStringList &paths = m_absolutes[type];
118 if (!paths.contains(copy, cs)) {
119 if (priority) {
120 paths.prepend(copy);
121 } else {
122 paths.append(copy);
123 }
124 }
125
126 //qDebug() << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << m_absolutes[type];
127 }
128
findResourceInternal(const QString & type,const QString & fileName)129 QString KoResourcePathsImpl::findResourceInternal(const QString &type, const QString &fileName)
130 {
131 const QStandardPaths::StandardLocation location = mapTypeToQStandardPaths(type);
132 QString resource = QStandardPaths::locate(location, fileName, QStandardPaths::LocateFile);
133 if (resource.isEmpty()) {
134 foreach(const QString &relative, m_relatives.value(type)) {
135 resource = QStandardPaths::locate(location, relative + fileName, QStandardPaths::LocateFile);
136 if (!resource.isEmpty()) {
137 break;
138 }
139 }
140 }
141 if (resource.isEmpty()) {
142 foreach(const QString &absolute, m_absolutes.value(type)) {
143 const QString filePath = absolute + fileName;
144 if (QFileInfo::exists(filePath)) {
145 resource = filePath;
146 break;
147 }
148 }
149 }
150 //Q_ASSERT(!resource.isEmpty());
151 //qDebug() << "findResource: type" << type << "filename" << fileName << "resource" << resource;
152 return resource;
153 }
154
findDirsInternal(const QString & type,const QString & relDir)155 QStringList KoResourcePathsImpl::findDirsInternal(const QString &type, const QString &relDir)
156 {
157 const QStandardPaths::StandardLocation location = mapTypeToQStandardPaths(type);
158
159 QStringList dirs = QStandardPaths::locateAll(location, relDir, QStandardPaths::LocateDirectory);
160
161 foreach(const QString &relative, m_relatives.value(type)) {
162 dirs << QStandardPaths::locateAll(location, relative + relDir, QStandardPaths::LocateDirectory);
163 }
164
165 foreach(const QString &absolute, m_absolutes.value(type)) {
166 const QString dirPath = absolute + relDir;
167 if (QDir(dirPath).exists()) {
168 dirs << dirPath;
169 }
170 }
171
172 //Q_ASSERT(!dirs.isEmpty());
173 //qDebug() << "findDirs: type" << type << "relDir" << relDir<< "resource" << dirs;
174 return dirs;
175 }
176
177
filesInDir(const QString & startdir,const QString & filter,bool noduplicates,bool recursive)178 QStringList filesInDir(const QString &startdir, const QString & filter, bool noduplicates, bool recursive)
179 {
180 //qDebug() << "filesInDir: startdir" << startdir << "filter" << filter << "noduplicates" << noduplicates << "recursive" << recursive;
181 QStringList result;
182
183 // First the entries in this path
184 QStringList nameFilters;
185 nameFilters << filter;
186 const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name);
187 //qDebug() << "\tFound:" << fileNames.size() << ":" << fileNames;
188 Q_FOREACH (const QString &fileName, fileNames) {
189 QString file = startdir + '/' + fileName;
190 result << file;
191 }
192
193 // And then everything underneath, if recursive is specified
194 if (recursive) {
195 const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
196 Q_FOREACH (const QString &subdir, entries) {
197 //qDebug() << "\tGoing to look in subdir" << subdir << "of" << startdir;
198 result << filesInDir(startdir + '/' + subdir, filter, noduplicates, recursive);
199 }
200 }
201 return result;
202 }
203
findAllResourcesInternal(const QString & type,const QString & _filter,KoResourcePaths::SearchOptions options) const204 QStringList KoResourcePathsImpl::findAllResourcesInternal(const QString &type,
205 const QString &_filter,
206 KoResourcePaths::SearchOptions options) const
207 {
208 //qDebug() << "=====================================================";
209
210 bool noDuplicates = options & KoResourcePaths::NoDuplicates;
211 bool recursive = options & KoResourcePaths::Recursive;
212
213 //qDebug() << "findAllResources: type" << type << "filter" << _filter << "no dups" << noDuplicates << "recursive" << recursive;
214
215 const QStandardPaths::StandardLocation location = mapTypeToQStandardPaths(type);
216
217 const QStringList relatives = m_relatives.value(type);
218 QString filter = _filter;
219 QString prefix;
220
221 // In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistgered resource types
222 if (filter.indexOf('*') > 0) {
223 prefix = filter.split('*').first();
224 filter = '*' + filter.split('*')[1];
225 //qDebug() << "Split up alias" << relatives << "filter" << filter;
226 }
227
228 QStringList resources;
229 if (relatives.isEmpty()) {
230 resources << QStandardPaths::locateAll(location, prefix + filter, QStandardPaths::LocateFile);
231 }
232
233 ////qDebug() << "\tresources from qstandardpaths:" << resources.size();
234
235
236 foreach(const QString &relative, relatives) {
237 //qDebug() << "\t\relative:" << relative;
238 const QStringList dirs = QStandardPaths::locateAll(location, relative + prefix, QStandardPaths::LocateDirectory);
239 QSet<QString> s = QSet<QString>::fromList(dirs);
240
241 //qDebug() << "\t\tdirs:" << dirs;
242 Q_FOREACH (const QString &dir, s) {
243 resources << filesInDir(dir, filter, noDuplicates, recursive);
244 }
245 }
246
247 foreach(const QString &absolute, m_absolutes.value(type)) {
248 const QString dir = absolute + prefix;
249 if (QDir(dir).exists()) {
250 resources << filesInDir(dir, filter, noDuplicates, recursive);
251 }
252 }
253
254 if (noDuplicates) {
255 QSet<QString> s = QSet<QString>::fromList(resources);
256 resources = s.toList();
257 }
258
259 //qDebug() << "\tresources also from aliases:" << resources.size();
260 //qDebug() << "=====================================================";
261
262 //Q_ASSERT(!resources.isEmpty());
263
264 return resources;
265 }
266
resourceDirsInternal(const QString & type)267 QStringList KoResourcePathsImpl::resourceDirsInternal(const QString &type)
268 {
269 //return KGlobal::dirs()->resourceDirs(type.toLatin1());
270 QStringList resourceDirs;
271
272 const QStandardPaths::StandardLocation location = mapTypeToQStandardPaths(type);
273 foreach(const QString &relative, m_relatives.value(type)) {
274 resourceDirs << QStandardPaths::locateAll(location, relative, QStandardPaths::LocateDirectory);
275 }
276 foreach(const QString &absolute, m_absolutes.value(type)) {
277 if (QDir(absolute).exists()) {
278 resourceDirs << absolute;
279 }
280 }
281 //qDebug() << "resourceDirs: type" << type << resourceDirs;
282
283 return resourceDirs;
284 }
285
saveLocationInternal(const QString & type,const QString & suffix,bool create)286 QString KoResourcePathsImpl::saveLocationInternal(const QString &type, const QString &suffix, bool create)
287 {
288 QString path = QStandardPaths::writableLocation(mapTypeToQStandardPaths(type)) + '/' + suffix;
289 QDir d(path);
290 if (!d.exists() && create) {
291 d.mkpath(path);
292 }
293 //qDebug() << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path;
294
295 return path;
296 }
297
locateLocalInternal(const QString & type,const QString & filename,bool createDir)298 QString KoResourcePathsImpl::locateLocalInternal(const QString &type, const QString &filename, bool createDir)
299 {
300 QString path = saveLocationInternal(type, "", createDir);
301 //qDebug() << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path;
302 return path + '/' + filename;
303 }
304
305 Q_GLOBAL_STATIC(KoResourcePathsImpl, s_instance);
306
307
addResourceType(const char * type,const char * basetype,const QString & relativeName,bool priority)308 void KoResourcePaths::addResourceType(const char *type, const char *basetype,
309 const QString &relativeName, bool priority)
310 {
311 s_instance->addResourceTypeInternal(QString::fromLatin1(type), QString::fromLatin1(basetype), relativeName, priority);
312 }
313
addResourceDir(const char * type,const QString & dir,bool priority)314 void KoResourcePaths::addResourceDir(const char *type, const QString &dir, bool priority)
315 {
316 s_instance->addResourceDirInternal(QString::fromLatin1(type), dir, priority);
317 }
318
findResource(const char * type,const QString & fileName)319 QString KoResourcePaths::findResource(const char *type, const QString &fileName)
320 {
321 return s_instance->findResourceInternal(QString::fromLatin1(type), fileName);
322 }
323
findDirs(const char * type,const QString & reldir)324 QStringList KoResourcePaths::findDirs(const char *type, const QString &reldir)
325 {
326 return s_instance->findDirsInternal(QString::fromLatin1(type), reldir);
327 }
328
findAllResources(const char * type,const QString & filter,SearchOptions options)329 QStringList KoResourcePaths::findAllResources(const char *type,
330 const QString &filter,
331 SearchOptions options)
332 {
333 return s_instance->findAllResourcesInternal(QString::fromLatin1(type), filter, options);
334 }
335
resourceDirs(const char * type)336 QStringList KoResourcePaths::resourceDirs(const char *type)
337 {
338 return s_instance->resourceDirsInternal(QString::fromLatin1(type));
339 }
340
saveLocation(const char * type,const QString & suffix,bool create)341 QString KoResourcePaths::saveLocation(const char *type, const QString &suffix, bool create)
342 {
343 return s_instance->saveLocationInternal(QString::fromLatin1(type), suffix, create);
344 }
345
locate(const char * type,const QString & filename)346 QString KoResourcePaths::locate(const char *type, const QString &filename)
347 {
348 return s_instance->findResourceInternal(QString::fromLatin1(type), filename);
349 }
350
locateLocal(const char * type,const QString & filename,bool createDir)351 QString KoResourcePaths::locateLocal(const char *type, const QString &filename, bool createDir)
352 {
353 return s_instance->locateLocalInternal(QString::fromLatin1(type), filename, createDir);
354 }
355