LeechCraft  0.6.70-15082-g543737046d
Modular cross-platform feature rich live environment.
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 
20 namespace 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 }
LC::Util::ResourceLoader::SetCacheParams
void SetCacheParams(int size, int timeout)
Sets the caching parameters of this loader.
Definition: resourceloader.cpp:120
LC::Util::ResourceLoader::List
QFileInfoList List(const QString &option, const QStringList &names={}, QDir::Filters filters=QDir::NoFilter) const
Lists the available files for the given option.
Definition: resourceloader.cpp:148
LC::Util::ResourceLoader::SetNameFilters
void SetNameFilters(const QStringList &)
Sets the name filters for the subelement model.
Definition: resourceloader.cpp:297
LC::Util::QIODevice_ptr
std::shared_ptr< QIODevice > QIODevice_ptr
Definition: resourceloader.h:27
LC::Util::ResourceLoader::GetIconPath
QString GetIconPath(const QString &basename) const
Calls GetPath() with standard variants for the icon extensions.
Definition: resourceloader.cpp:208
LC::Util
Definition: icoreproxy.h:33
LC::Util::ResourceLoader::watchedDirectoriesChanged
void watchedDirectoriesChanged()
LC::Util::ResourceLoader::LoadPixmap
QPixmap LoadPixmap(const QString &basename) const
Returns the pixmap for the given basename.
Definition: resourceloader.cpp:270
LC::Util::ResourceLoader::GetIconDevice
QIODevice_ptr GetIconDevice(const QString &basename, bool open=false) const
Returns the QIODevice for the corresponding icon.
Definition: resourceloader.cpp:265
LC::Util::ResourceLoader::AddLocalPrefix
void AddLocalPrefix(QString prefix=QString())
Registers a local search prefix.
Definition: resourceloader.cpp:56
LC::Util::ResourceLoader::SetAttrFilters
void SetAttrFilters(QDir::Filters)
Sets the attribute filters for the subelement model.
Definition: resourceloader.cpp:292
LC::Util::ResourceLoader::SetVerbose
void SetVerbose(bool verbose)
Control whether the ResourceLoader prints what it is doing to the logs.
Definition: resourceloader.cpp:302
LC::Util::ResourceLoader::GetPath
QString GetPath(const QStringList &pathVariants) const
Returns the first found path for the list of variants.
Definition: resourceloader.cpp:171
LC::Util::ResourceLoader::FlushCache
void FlushCache()
Forcefully flushes the cache.
Definition: resourceloader.cpp:142
LC::Util::ResourceLoader::GetSubElemModel
QAbstractItemModel * GetSubElemModel() const
Returns the subelement model with the contents of registered paths.
Definition: resourceloader.cpp:287
LC::Util::ResourceLoader::AddGlobalPrefix
void AddGlobalPrefix()
Registers global OS-dependent prefixes.
Definition: resourceloader.cpp:84
LC::Util::ResourceLoader::Load
QIODevice_ptr Load(const QStringList &pathVariants, bool open=false) const
Returns the QIODevice for the corresponding resource.
Definition: resourceloader.cpp:213
resourceloader.h
LC::Util::ResourceLoader::ResourceLoader
ResourceLoader(const QString &relPath, QObject *obj=nullptr)
Initializes the loader with the given path.
Definition: resourceloader.cpp:28