8#include <QtCore/qstringlist.h>
9#include <QtCore/qhash.h>
10#include <QtCore/qmap.h>
11#include <QtCore/qresource.h>
12#include <QtCore/qfileinfo.h>
13#include <QtCore/qiodevice.h>
14#include <QtCore/qdir.h>
15#include <QtCore/qdebug.h>
16#include <QtCore/qbuffer.h>
17#include <QtCore/qfilesystemwatcher.h>
27 Q_DECLARE_PUBLIC(QtResourceSet)
43 QtResourceModel *q_ptr =
nullptr;
44 Q_DECLARE_PUBLIC(QtResourceModel)
48 void activate(QtResourceSet *resourceSet,
const QStringList &newPaths,
int *errorCount =
nullptr, QString *errorMessages =
nullptr);
49 void removeOldPaths(QtResourceSet *resourceSet,
const QStringList &newPaths);
72 void registerResourceSet(QtResourceSet *resourceSet);
73 void unregisterResourceSet(QtResourceSet *resourceSet);
74 void setWatcherEnabled(
const QString &path,
bool enable);
75 void addWatcher(
const QString &path);
76 void removeWatcher(
const QString &path);
78 void slotFileChanged(
const QString &);
80 const QByteArray *createResource(
const QString &path, QStringList *contents,
int *errorCount, QIODevice &errorDevice)
const;
81 void deleteResource(
const QByteArray *data)
const;
87QtResourceSet::QtResourceSet() :
88 d_ptr(
new QtResourceSetPrivate)
93QtResourceSet::QtResourceSet(QtResourceModel *model) :
94 d_ptr(
new QtResourceSetPrivate(model))
99QtResourceSet::~QtResourceSet() =
default;
101QStringList QtResourceSet::activeResourceFilePaths()
const
103 QtResourceSet *that =
const_cast<QtResourceSet *>(
this);
104 return d_ptr->m_resourceModel->d_ptr->m_resourceSetToPaths.value(that);
107void QtResourceSet::activateResourceFilePaths(
const QStringList &paths,
int *errorCount, QString *errorMessages)
109 d_ptr->m_resourceModel->d_ptr->activate(
this, paths, errorCount, errorMessages);
112bool QtResourceSet::isModified(
const QString &path)
const
114 return d_ptr->m_resourceModel->isModified(path);
117void QtResourceSet::setModified(
const QString &path)
119 d_ptr->m_resourceModel->setModified(path);
123const QByteArray *
QtResourceModelPrivate::createResource(
const QString &path, QStringList *contents,
int *errorCount, QIODevice &errorDevice)
const
126 const QByteArray *rc =
nullptr;
133 library.setInputFiles(QStringList(path));
137 buffer.open(QIODevice::WriteOnly);
141 const ResourceDataFileMap resMap = library.resourceDataFileMap();
145 *errorCount = library.failedResources().size();
146 *contents = resMap.keys();
148 if (resMap.isEmpty())
152 rc =
new QByteArray(buffer.data());
156 qDebug() <<
"createResource" << path <<
"returns data=" << rc <<
" hasWarnings=" << *errorCount;
164 qDebug() <<
"deleteResource";
175 const QStringList toRegister = resourceSet->activeResourceFilePaths();
176 for (
const QString &path : toRegister) {
177 if (debugResourceModel)
178 qDebug() <<
"registerResourceSet " << path;
179 const auto itRcc = m_pathToData.constFind(path);
180 if (itRcc != m_pathToData.constEnd()) {
181 const QByteArray *data = itRcc.value();
183 if (!QResource::registerResource(
reinterpret_cast<
const uchar *>(data->constData()))) {
184 qWarning() <<
"** WARNING: Failed to register " << path <<
" (QResource failure).";
186 const QStringList contents = m_pathToContents.value(path);
187 for (
const QString &filePath : contents) {
188 if (!m_fileToQrc.contains(filePath))
189 m_fileToQrc.insert(filePath, path);
203 const QStringList toUnregister = resourceSet->activeResourceFilePaths();
204 for (
const QString &path : toUnregister) {
205 if (debugResourceModel)
206 qDebug() <<
"unregisterResourceSet " << path;
207 const auto itRcc = m_pathToData.constFind(path);
208 if (itRcc != m_pathToData.constEnd()) {
209 const QByteArray *data = itRcc.value();
211 if (!QResource::unregisterResource(
reinterpret_cast<
const uchar *>(itRcc.value()->constData())))
212 qWarning() <<
"** WARNING: Failed to unregister " << path <<
" (QResource failure).";
219void QtResourceModelPrivate::activate(QtResourceSet *resourceSet,
const QStringList &newPaths,
int *errorCountPtr, QString *errorMessages)
221 if (debugResourceModel)
222 qDebug() <<
"activate " << resourceSet;
226 errorMessages->clear();
229 errorStream.open(QIODevice::WriteOnly);
232 int generatedCount = 0;
233 bool newResourceSetChanged =
false;
235 if (resourceSet && resourceSet->activeResourceFilePaths() != newPaths && !m_newlyCreated.contains(resourceSet))
236 newResourceSetChanged =
true;
238 auto newPathToData = m_pathToData;
240 for (
const QString &path : newPaths) {
241 if (resourceSet && !m_pathToResourceSet[path].contains(resourceSet))
242 m_pathToResourceSet[path].append(resourceSet);
243 const auto itMod = m_pathToModified.find(path);
244 if (itMod == m_pathToModified.end() || itMod.value()) {
245 QStringList contents;
248 const QByteArray *data = createResource(path, &contents, &qrcErrorCount, errorStream);
250 newPathToData.insert(path, data);
255 m_pathToModified.insert(path,
false);
256 m_pathToContents.insert(path, contents);
257 newResourceSetChanged =
true;
258 const auto itReload = m_pathToResourceSet.find(path);
259 if (itReload != m_pathToResourceSet.end()) {
260 const auto resources = itReload.value();
261 for (QtResourceSet *res : resources) {
262 if (res != resourceSet) {
263 m_resourceSetToReload[res] =
true;
271 const auto oldData = m_pathToData.values();
272 const auto newData = newPathToData.values();
274 QList<
const QByteArray *> toDelete;
275 for (
const QByteArray *array : oldData) {
276 if (array && !newData.contains(array))
277 toDelete.append(array);
281 if (generatedCount) {
283 *errorCountPtr = errorCount;
285 const QString stderrOutput = QString::fromUtf8(errorStream.data());
286 if (debugResourceModel)
287 qDebug() <<
"Output: (" << errorCount <<
")\n" << stderrOutput;
289 *errorMessages = stderrOutput;
292 const auto itReload = m_resourceSetToReload.find(resourceSet);
293 if (itReload != m_resourceSetToReload.end()) {
294 if (itReload.value()) {
295 newResourceSetChanged =
true;
296 m_resourceSetToReload.insert(resourceSet,
false);
300 QStringList oldActivePaths;
301 if (m_currentResourceSet)
302 oldActivePaths = m_currentResourceSet->activeResourceFilePaths();
304 const bool needReregister = (oldActivePaths != newPaths) || newResourceSetChanged;
306 const auto itNew = m_newlyCreated.find(resourceSet);
307 if (itNew != m_newlyCreated.end()) {
308 m_newlyCreated.remove(resourceSet);
310 newResourceSetChanged =
true;
313 if (!newResourceSetChanged && !needReregister && (m_currentResourceSet == resourceSet)) {
314 for (
const QByteArray *data : std::as_const(toDelete))
315 deleteResource(data);
321 unregisterResourceSet(m_currentResourceSet);
323 for (
const QByteArray *data : std::as_const(toDelete))
324 deleteResource(data);
326 m_pathToData = newPathToData;
327 m_currentResourceSet = resourceSet;
330 removeOldPaths(resourceSet, newPaths);
333 registerResourceSet(m_currentResourceSet);
335 emit q_ptr->resourceSetActivated(m_currentResourceSet, newResourceSetChanged);
347 const QStringList oldPaths = m_resourceSetToPaths.value(resourceSet);
348 if (oldPaths != newPaths) {
350 for (
const QString &oldPath : oldPaths) {
351 if (!newPaths.contains(oldPath)) {
352 const auto itRemove = m_pathToResourceSet.find(oldPath);
353 if (itRemove != m_pathToResourceSet.end()) {
354 const int idx = itRemove.value().indexOf(resourceSet);
356 itRemove.value().removeAt(idx);
357 if (itRemove.value().isEmpty()) {
358 const auto it = m_pathToData.find(oldPath);
359 if (it != m_pathToData.end())
360 deleteResource(it.value());
361 m_pathToResourceSet.erase(itRemove);
362 m_pathToModified.remove(oldPath);
363 m_pathToContents.remove(oldPath);
364 m_pathToData.remove(oldPath);
365 removeWatcher(oldPath);
370 m_resourceSetToPaths[resourceSet] = newPaths;
377 m_fileWatcher->removePath(path);
383 m_fileWatcher->addPath(path);
388 const auto it = m_fileWatchedMap.constFind(path);
389 if (it != m_fileWatchedMap.constEnd() && !it.value())
392 m_fileWatchedMap.insert(path,
true);
395 setWatcherEnabled(path,
true);
400 if (!m_fileWatchedMap.contains(path))
403 m_fileWatchedMap.remove(path);
406 setWatcherEnabled(path,
false);
411 setWatcherEnabled(path,
false);
412 emit q_ptr->qrcFileModifiedExternally(path);
413 setWatcherEnabled(path,
true);
417QtResourceModel::QtResourceModel(QObject *parent) :
419 d_ptr(
new QtResourceModelPrivate)
423 d_ptr->m_fileWatcher =
new QFileSystemWatcher(
this);
424 connect(d_ptr->m_fileWatcher, &QFileSystemWatcher::fileChanged,
425 this, [
this](
const QString &fileName) { d_ptr->slotFileChanged(fileName); });
428QtResourceModel::~QtResourceModel()
431 const auto resourceList = resourceSets();
432 for (QtResourceSet *rs : resourceList)
433 removeResourceSet(rs);
437QStringList QtResourceModel::loadedQrcFiles()
const
439 return d_ptr->m_pathToModified.keys();
442bool QtResourceModel::isModified(
const QString &path)
const
444 return d_ptr->m_pathToModified.value(path,
true);
447void QtResourceModel::setModified(
const QString &path)
449 if (!d_ptr->m_pathToModified.contains(path))
452 d_ptr->m_pathToModified[path] =
true;
453 const auto it = d_ptr->m_pathToResourceSet.constFind(path);
454 if (it == d_ptr->m_pathToResourceSet.constEnd())
457 const auto resourceList = it.value();
458 for (QtResourceSet *rs : resourceList)
459 d_ptr->m_resourceSetToReload.insert(rs,
true);
462QList<QtResourceSet *> QtResourceModel::resourceSets()
const
464 return d_ptr->m_resourceSetToPaths.keys();
467QtResourceSet *QtResourceModel::currentResourceSet()
const
469 return d_ptr->m_currentResourceSet;
472void QtResourceModel::setCurrentResourceSet(QtResourceSet *resourceSet,
int *errorCount, QString *errorMessages)
474 d_ptr->activate(resourceSet, d_ptr->m_resourceSetToPaths.value(resourceSet), errorCount, errorMessages);
477QtResourceSet *QtResourceModel::addResourceSet(
const QStringList &paths)
479 QtResourceSet *newResource =
new QtResourceSet(
this);
480 d_ptr->m_resourceSetToPaths.insert(newResource, paths);
481 d_ptr->m_resourceSetToReload.insert(newResource,
false);
482 d_ptr->m_newlyCreated.insert(newResource,
true);
483 for (
const QString &path : paths)
484 d_ptr->m_pathToResourceSet[path].append(newResource);
489void QtResourceModel::removeResourceSet(QtResourceSet *resourceSet)
493 if (currentResourceSet() == resourceSet)
494 setCurrentResourceSet(
nullptr);
497 d_ptr->removeOldPaths(resourceSet, QStringList());
499 d_ptr->m_resourceSetToPaths.remove(resourceSet);
500 d_ptr->m_resourceSetToReload.remove(resourceSet);
501 d_ptr->m_newlyCreated.remove(resourceSet);
505void QtResourceModel::reload(
const QString &path,
int *errorCount, QString *errorMessages)
509 d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages);
512void QtResourceModel::reload(
int *errorCount, QString *errorMessages)
514 for (
auto it = d_ptr->m_pathToModified.begin(), end = d_ptr->m_pathToModified.end(); it != end; ++it)
518 for (
auto itReload = d_ptr->m_resourceSetToReload.begin(), end = d_ptr->m_resourceSetToReload.end(); itReload != end; ++itReload)
519 itReload.value() =
true;
521 d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages);
524QMap<QString, QString> QtResourceModel::contents()
const
526 return d_ptr->m_fileToQrc;
529QString QtResourceModel::qrcPath(
const QString &file)
const
531 return d_ptr->m_fileToQrc.value(file);
534void QtResourceModel::setWatcherEnabled(
bool enable)
536 if (d_ptr->m_fileWatcherEnabled == enable)
539 d_ptr->m_fileWatcherEnabled = enable;
541 if (!d_ptr->m_fileWatchedMap.isEmpty())
542 d_ptr->setWatcherEnabled(d_ptr->m_fileWatchedMap.firstKey(), d_ptr->m_fileWatcherEnabled);
545bool QtResourceModel::isWatcherEnabled()
const
547 return d_ptr->m_fileWatcherEnabled;
550void QtResourceModel::setWatcherEnabled(
const QString &path,
bool enable)
552 const auto it = d_ptr->m_fileWatchedMap.find(path);
553 if (it == d_ptr->m_fileWatchedMap.end())
556 if (it.value() == enable)
561 if (!d_ptr->m_fileWatcherEnabled)
564 d_ptr->setWatcherEnabled(it.key(), enable);
567bool QtResourceModel::isWatcherEnabled(
const QString &path)
569 return d_ptr->m_fileWatchedMap.value(path,
false);
574#include "moc_qtresourcemodel_p.cpp"
QHash< QtResourceSet *, bool > m_resourceSetToReload
QMap< QString, QString > m_fileToQrc
QtResourceSet * m_currentResourceSet
QHash< QtResourceSet *, QStringList > m_resourceSetToPaths
void activate(QtResourceSet *resourceSet, const QStringList &newPaths, int *errorCount=nullptr, QString *errorMessages=nullptr)
QMap< QString, QStringList > m_pathToContents
QMap< QString, const QByteArray * > m_pathToData
QMap< QString, QList< QtResourceSet * > > m_pathToResourceSet
void removeOldPaths(QtResourceSet *resourceSet, const QStringList &newPaths)
bool m_fileWatcherEnabled
QMap< QString, bool > m_fileWatchedMap
QFileSystemWatcher * m_fileWatcher
QHash< QtResourceSet *, bool > m_newlyCreated
QMap< QString, bool > m_pathToModified
QtResourceModel * m_resourceModel
bool readFiles(bool listMode, QIODevice &errorDevice)
bool output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice)
QHash< QString, QString > ResourceDataFileMap
Combined button and popup list for selecting options.