LeechCraft 0.6.70-17335-ge406ffdcaf
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
resourceloader.cpp
Go to the documentation of this file.
1/**********************************************************************
2 * LeechCraft - modular cross-platform feature rich internet client.
3 * Copyright (C) 2006-2014 Georg Rudoy
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7 **********************************************************************/
8
9#include "resourceloader.h"
10#include <QApplication>
11#include <QFile>
12#include <QDir>
13#include <QStandardItemModel>
14#include <QSortFilterProxyModel>
15#include <QFileSystemWatcher>
16#include <QTimer>
17#include <QtDebug>
18#include <QBuffer>
19
20namespace LC::Util
21{
22 ResourceLoader::ResourceLoader (const QString& relPath, QObject* parent)
23 : QObject (parent)
24 , RelativePath_ (relPath)
25 , SubElemModel_ (new QStandardItemModel (this))
26 , SortModel_ (new QSortFilterProxyModel (this))
27 , Watcher_ (new QFileSystemWatcher (this))
28 , CacheFlushTimer_ (new QTimer (this))
29 {
30 if (RelativePath_.startsWith ('/'))
31 RelativePath_ = RelativePath_.mid (1);
32 if (!RelativePath_.endsWith ('/'))
33 RelativePath_.append ('/');
34
35 SortModel_->setDynamicSortFilter (true);
36 SortModel_->setSourceModel (SubElemModel_);
37 SortModel_->sort (0);
38
39 connect (Watcher_,
40 &QFileSystemWatcher::directoryChanged,
41 this,
42 &ResourceLoader::HandleDirectoryChanged);
43
44 connect (CacheFlushTimer_,
45 &QTimer::timeout,
46 this,
48 }
49
50 void ResourceLoader::AddLocalPrefix (QString prefix)
51 {
52 if (!prefix.isEmpty () &&
53 !prefix.endsWith ('/'))
54 prefix.append ('/');
55 QString result = QDir::homePath () + "/.leechcraft/data/" + prefix;
56 LocalPrefixesChain_ << result;
57
58 QDir testDir = QDir::home ();
59 if (!testDir.exists (".leechcraft/data/" + prefix + RelativePath_))
60 {
61 qDebug () << Q_FUNC_INFO
62 << ".leechcraft/data/" + prefix + RelativePath_
63 << "doesn't exist, trying to create it...";
64
65 if (!testDir.mkpath (".leechcraft/data/" + prefix + RelativePath_))
66 {
67 qWarning () << Q_FUNC_INFO
68 << "failed to create"
69 << ".leechcraft/data/" + prefix + RelativePath_;
70 }
71 }
72
73 ScanPath (result + RelativePath_);
74
75 Watcher_->addPath (result + RelativePath_);
76 }
77
79 {
80#if defined (Q_OS_MAC) && !defined (USE_UNIX_LAYOUT)
81 const QStringList prefixes { QApplication::applicationDirPath () + "/../Resources/share/" };
82#elif defined (Q_OS_WIN32)
83 const QStringList prefixes { QApplication::applicationDirPath () + "/share/" };
84#elif defined (INSTALL_PREFIX)
85 const QStringList prefixes { INSTALL_PREFIX "/share/leechcraft/" };
86#else
87 const QStringList prefixes
88 {
89 "/usr/local/share/leechcraft/",
90 "/usr/share/leechcraft/"
91 };
92#endif
93 bool hasBeenAdded = false;
94 for (const auto& prefix : prefixes)
95 {
96 GlobalPrefixesChain_ << prefix;
97 ScanPath (prefix + RelativePath_);
98
99 if (QFile::exists (prefix + RelativePath_))
100 {
101 Watcher_->addPath (prefix + RelativePath_);
102 hasBeenAdded = true;
103 }
104 }
105
106 if (!hasBeenAdded)
107 qWarning () << Q_FUNC_INFO
108 << "no prefixes have been added:"
109 << prefixes
110 << "; rel path:"
111 << RelativePath_;
112 }
113
114 void ResourceLoader::SetCacheParams (int size, int timeout)
115 {
116 if (qApp->property ("no-resource-caching").toBool ())
117 return;
118
119 if (size <= 0)
120 {
121 CacheFlushTimer_->stop ();
122
123 CachePathContents_.setMaxCost (0);
124 CachePixmaps_.setMaxCost (0);
125 }
126 else
127 {
128 if (timeout > 0)
129 CacheFlushTimer_->start (timeout);
130
131 CachePathContents_.setMaxCost (size * 1024);
132 CachePixmaps_.setMaxCost (size * 1024);
133 }
134 }
135
137 {
138 CachePathContents_.clear ();
139 CachePixmaps_.clear ();
140 }
141
142 QFileInfoList ResourceLoader::List (const QString& option,
143 const QStringList& nameFilters, QDir::Filters filters) const
144 {
145 QSet<QString> alreadyListed;
146 QFileInfoList result;
147 for (const auto& prefix : LocalPrefixesChain_ + GlobalPrefixesChain_)
148 {
149 const QDir dir { prefix + RelativePath_ + option };
150 const auto& list = dir.entryInfoList (nameFilters, filters);
151 for (const auto& info : list)
152 {
153 const auto& fname = info.fileName ();
154 if (alreadyListed.contains (fname))
155 continue;
156
157 alreadyListed << fname;
158 result << info;
159 }
160 }
161
162 return result;
163 }
164
165 QString ResourceLoader::GetPath (const QStringList& pathVariants) const
166 {
167 for (const auto& prefix : LocalPrefixesChain_ + GlobalPrefixesChain_)
168 for (const auto& path : pathVariants)
169 {
170 if (Verbose_)
171 qDebug () << Q_FUNC_INFO
172 << "trying"
173 << prefix + RelativePath_ + path;
174 const QString& can = QFileInfo (prefix + RelativePath_ + path).absoluteFilePath ();
175 if (Verbose_)
176 qDebug () << Q_FUNC_INFO
177 << "absolute file path"
178 << can
179 << "; file exists?"
180 << QFile::exists (can);
181 if (QFile::exists (can))
182 return can;
183 }
184
185 return QString ();
186 }
187
188 namespace
189 {
190 QStringList IconizeBasename (const QString& basename)
191 {
192 return
193 {
194 basename + ".svg",
195 basename + ".png",
196 basename + ".jpg",
197 basename + ".gif"
198 };
199 }
200 }
201
202 QString ResourceLoader::GetIconPath (const QString& basename) const
203 {
204 return GetPath (IconizeBasename (basename));
205 }
206
207 QIODevice_ptr ResourceLoader::Load (const QStringList& pathVariants, bool open) const
208 {
209 const auto& path = GetPath (pathVariants);
210 if (path.isNull ())
211 return {};
212
213 if (CachePathContents_.contains (path))
214 {
215 if (Verbose_)
216 qDebug () << Q_FUNC_INFO
217 << "found"
218 << path
219 << "in cache";
220
221 auto result = std::make_shared<QBuffer> ();
222 result->setData (*CachePathContents_ [path]);
223 if (open)
224 result->open (QIODevice::ReadOnly);
225 return result;
226 }
227
228 auto result = std::make_shared<QFile> (path);
229
230 if (!result->isSequential () &&
231 result->size () < CachePathContents_.maxCost () / 2)
232 {
233 if (result->open (QIODevice::ReadOnly))
234 {
235 const auto& data = result->readAll ();
236 CachePathContents_.insert (path, new QByteArray { data }, data.size ());
237 if (!open)
238 result->close ();
239 else
240 result->seek (0);
241 }
242 }
243
244 if (open && !result->isOpen ())
245 if (!result->open (QIODevice::ReadOnly))
246 qWarning () << Q_FUNC_INFO
247 << "unable to open file"
248 << path
249 << result->errorString ();
250
251 return result;
252 }
253
254 QIODevice_ptr ResourceLoader::Load (const QString& pathVariant, bool open) const
255 {
256 return Load (QStringList { pathVariant }, open);
257 }
258
259 QIODevice_ptr ResourceLoader::GetIconDevice (const QString& basename, bool open) const
260 {
261 return Load (IconizeBasename (basename), open);
262 }
263
264 QPixmap ResourceLoader::LoadPixmap (const QString& basename) const
265 {
266 if (CachePixmaps_.contains (basename))
267 return *CachePixmaps_ [basename];
268
269 auto dev = GetIconDevice (basename, true);
270 if (!dev)
271 return QPixmap ();
272
273 const auto& data = dev->readAll ();
274
275 QPixmap result;
276 result.loadFromData (data);
277 CachePixmaps_.insert (basename, new QPixmap (result), data.size ());
278 return result;
279 }
280
281 QAbstractItemModel* ResourceLoader::GetSubElemModel () const
282 {
283 return SortModel_;
284 }
285
286 void ResourceLoader::SetAttrFilters (QDir::Filters filters)
287 {
288 AttrFilters_ = filters;
289 }
290
291 void ResourceLoader::SetNameFilters (const QStringList& filters)
292 {
293 NameFilters_ = filters;
294 }
295
296 void ResourceLoader::SetVerbose (bool verbose)
297 {
298 Verbose_ = verbose;
299 }
300
301 void ResourceLoader::ScanPath (const QString& path)
302 {
303 for (const auto& entry : QDir (path).entryList (NameFilters_, AttrFilters_))
304 {
305 Entry2Paths_ [entry] << path;
306 if (!SubElemModel_->findItems (entry).isEmpty ())
307 continue;
308
309 SubElemModel_->appendRow (new QStandardItem (entry));
310 }
311 }
312
313 void ResourceLoader::HandleDirectoryChanged (const QString& path)
314 {
316
317 for (auto& paths : Entry2Paths_)
318 paths.removeAll (path);
319
320 QFileInfo fi (path);
321 if (fi.exists () &&
322 fi.isDir () &&
323 fi.isReadable ())
324 ScanPath (path);
325
326 for (auto i = Entry2Paths_.begin (); i != Entry2Paths_.end ();)
327 if (i->isEmpty ())
328 {
329 for (auto item : SubElemModel_->findItems (i.key ()))
330 SubElemModel_->removeRow (item->row ());
331 i = Entry2Paths_.erase (i);
332 }
333 else
334 ++i;
335 }
336}
void SetCacheParams(int size, int timeout)
Sets the caching parameters of this loader.
QAbstractItemModel * GetSubElemModel() const
Returns the subelement model with the contents of registered paths.
void FlushCache()
Forcefully flushes the cache.
QIODevice_ptr Load(const QStringList &pathVariants, bool open=false) const
Returns the QIODevice for the corresponding resource.
void SetAttrFilters(QDir::Filters)
Sets the attribute filters for the subelement model.
QString GetPath(const QStringList &pathVariants) const
Returns the first found path for the list of variants.
void AddGlobalPrefix()
Registers global OS-dependent prefixes.
ResourceLoader(const QString &relPath, QObject *obj=nullptr)
Initializes the loader with the given path.
QFileInfoList List(const QString &option, const QStringList &names={}, QDir::Filters filters=QDir::NoFilter) const
Lists the available files for the given option.
QString GetIconPath(const QString &basename) const
Calls GetPath() with standard variants for the icon extensions.
QPixmap LoadPixmap(const QString &basename) const
Returns the pixmap for the given basename.
QIODevice_ptr GetIconDevice(const QString &basename, bool open=false) const
Returns the QIODevice for the corresponding icon.
void SetNameFilters(const QStringList &)
Sets the name filters for the subelement model.
void SetVerbose(bool verbose)
Control whether the ResourceLoader prints what it is doing to the logs.
void AddLocalPrefix(QString prefix=QString())
Registers a local search prefix.
std::shared_ptr< QIODevice > QIODevice_ptr