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