7#include <QtCore/qstringlist.h>
8#include <QtCore/qhash.h>
9#include <QtCore/qmap.h>
10#include <QtCore/qresource.h>
11#include <QtCore/qfileinfo.h>
12#include <QtCore/qiodevice.h>
13#include <QtCore/qdir.h>
14#include <QtCore/qdebug.h>
15#include <QtCore/qbuffer.h>
16#include <QtCore/qfilesystemwatcher.h>
26 Q_DECLARE_PUBLIC(QtResourceSet)
42 QtResourceModel *q_ptr =
nullptr;
43 Q_DECLARE_PUBLIC(QtResourceModel)
47 void activate(QtResourceSet *resourceSet,
const QStringList &newPaths,
int *errorCount =
nullptr, QString *errorMessages =
nullptr);
48 void removeOldPaths(QtResourceSet *resourceSet,
const QStringList &newPaths);
71 void registerResourceSet(QtResourceSet *resourceSet);
72 void unregisterResourceSet(QtResourceSet *resourceSet);
73 void setWatcherEnabled(
const QString &path,
bool enable);
74 void addWatcher(
const QString &path);
75 void removeWatcher(
const QString &path);
77 void slotFileChanged(
const QString &);
79 const QByteArray *createResource(
const QString &path, QStringList *contents,
int *errorCount, QIODevice &errorDevice)
const;
80 void deleteResource(
const QByteArray *data)
const;
86QtResourceSet::QtResourceSet() :
87 d_ptr(
new QtResourceSetPrivate)
92QtResourceSet::QtResourceSet(QtResourceModel *model) :
93 d_ptr(
new QtResourceSetPrivate(model))
98QtResourceSet::~QtResourceSet() =
default;
100QStringList QtResourceSet::activeResourceFilePaths()
const
102 QtResourceSet *that =
const_cast<QtResourceSet *>(
this);
103 return d_ptr->m_resourceModel->d_ptr->m_resourceSetToPaths.value(that);
106void QtResourceSet::activateResourceFilePaths(
const QStringList &paths,
int *errorCount, QString *errorMessages)
108 d_ptr->m_resourceModel->d_ptr->activate(
this, paths, errorCount, errorMessages);
111bool QtResourceSet::isModified(
const QString &path)
const
113 return d_ptr->m_resourceModel->isModified(path);
116void QtResourceSet::setModified(
const QString &path)
118 d_ptr->m_resourceModel->setModified(path);
122const QByteArray *
QtResourceModelPrivate::createResource(
const QString &path, QStringList *contents,
int *errorCount, QIODevice &errorDevice)
const
125 const QByteArray *rc =
nullptr;
132 library.setInputFiles(QStringList(path));
136 buffer.open(QIODevice::WriteOnly);
140 const ResourceDataFileMap resMap = library.resourceDataFileMap();
144 *errorCount = library.failedResources().size();
145 *contents = resMap.keys();
147 if (resMap.isEmpty())
151 rc =
new QByteArray(buffer.data());
155 qDebug() <<
"createResource" << path <<
"returns data=" << rc <<
" hasWarnings=" << *errorCount;
163 qDebug() <<
"deleteResource";
174 const QStringList toRegister = resourceSet->activeResourceFilePaths();
175 for (
const QString &path : toRegister) {
176 if (debugResourceModel)
177 qDebug() <<
"registerResourceSet " << path;
178 const auto itRcc = m_pathToData.constFind(path);
179 if (itRcc != m_pathToData.constEnd()) {
180 const QByteArray *data = itRcc.value();
182 if (!QResource::registerResource(
reinterpret_cast<
const uchar *>(data->constData()))) {
183 qWarning() <<
"** WARNING: Failed to register " << path <<
" (QResource failure).";
185 const QStringList contents = m_pathToContents.value(path);
186 for (
const QString &filePath : contents) {
187 if (!m_fileToQrc.contains(filePath))
188 m_fileToQrc.insert(filePath, path);
202 const QStringList toUnregister = resourceSet->activeResourceFilePaths();
203 for (
const QString &path : toUnregister) {
204 if (debugResourceModel)
205 qDebug() <<
"unregisterResourceSet " << path;
206 const auto itRcc = m_pathToData.constFind(path);
207 if (itRcc != m_pathToData.constEnd()) {
208 const QByteArray *data = itRcc.value();
210 if (!QResource::unregisterResource(
reinterpret_cast<
const uchar *>(itRcc.value()->constData())))
211 qWarning() <<
"** WARNING: Failed to unregister " << path <<
" (QResource failure).";
218void QtResourceModelPrivate::activate(QtResourceSet *resourceSet,
const QStringList &newPaths,
int *errorCountPtr, QString *errorMessages)
220 if (debugResourceModel)
221 qDebug() <<
"activate " << resourceSet;
225 errorMessages->clear();
228 errorStream.open(QIODevice::WriteOnly);
231 int generatedCount = 0;
232 bool newResourceSetChanged =
false;
234 if (resourceSet && resourceSet->activeResourceFilePaths() != newPaths && !m_newlyCreated.contains(resourceSet))
235 newResourceSetChanged =
true;
237 auto newPathToData = m_pathToData;
239 for (
const QString &path : newPaths) {
240 if (resourceSet && !m_pathToResourceSet[path].contains(resourceSet))
241 m_pathToResourceSet[path].append(resourceSet);
242 const auto itMod = m_pathToModified.find(path);
243 if (itMod == m_pathToModified.end() || itMod.value()) {
244 QStringList contents;
247 const QByteArray *data = createResource(path, &contents, &qrcErrorCount, errorStream);
249 newPathToData.insert(path, data);
254 m_pathToModified.insert(path,
false);
255 m_pathToContents.insert(path, contents);
256 newResourceSetChanged =
true;
257 const auto itReload = m_pathToResourceSet.find(path);
258 if (itReload != m_pathToResourceSet.end()) {
259 const auto resources = itReload.value();
260 for (QtResourceSet *res : resources) {
261 if (res != resourceSet) {
262 m_resourceSetToReload[res] =
true;
270 const auto oldData = m_pathToData.values();
271 const auto newData = newPathToData.values();
273 QList<
const QByteArray *> toDelete;
274 for (
const QByteArray *array : oldData) {
275 if (array && !newData.contains(array))
276 toDelete.append(array);
280 if (generatedCount) {
282 *errorCountPtr = errorCount;
284 const QString stderrOutput = QString::fromUtf8(errorStream.data());
285 if (debugResourceModel)
286 qDebug() <<
"Output: (" << errorCount <<
")\n" << stderrOutput;
288 *errorMessages = stderrOutput;
291 const auto itReload = m_resourceSetToReload.find(resourceSet);
292 if (itReload != m_resourceSetToReload.end()) {
293 if (itReload.value()) {
294 newResourceSetChanged =
true;
295 m_resourceSetToReload.insert(resourceSet,
false);
299 QStringList oldActivePaths;
300 if (m_currentResourceSet)
301 oldActivePaths = m_currentResourceSet->activeResourceFilePaths();
303 const bool needReregister = (oldActivePaths != newPaths) || newResourceSetChanged;
305 const auto itNew = m_newlyCreated.find(resourceSet);
306 if (itNew != m_newlyCreated.end()) {
307 m_newlyCreated.remove(resourceSet);
309 newResourceSetChanged =
true;
312 if (!newResourceSetChanged && !needReregister && (m_currentResourceSet == resourceSet)) {
313 for (
const QByteArray *data : std::as_const(toDelete))
314 deleteResource(data);
320 unregisterResourceSet(m_currentResourceSet);
322 for (
const QByteArray *data : std::as_const(toDelete))
323 deleteResource(data);
325 m_pathToData = newPathToData;
326 m_currentResourceSet = resourceSet;
329 removeOldPaths(resourceSet, newPaths);
332 registerResourceSet(m_currentResourceSet);
334 emit q_ptr->resourceSetActivated(m_currentResourceSet, newResourceSetChanged);
346 const QStringList oldPaths = m_resourceSetToPaths.value(resourceSet);
347 if (oldPaths != newPaths) {
349 for (
const QString &oldPath : oldPaths) {
350 if (!newPaths.contains(oldPath)) {
351 const auto itRemove = m_pathToResourceSet.find(oldPath);
352 if (itRemove != m_pathToResourceSet.end()) {
353 const int idx = itRemove.value().indexOf(resourceSet);
355 itRemove.value().removeAt(idx);
356 if (itRemove.value().isEmpty()) {
357 const auto it = m_pathToData.find(oldPath);
358 if (it != m_pathToData.end())
359 deleteResource(it.value());
360 m_pathToResourceSet.erase(itRemove);
361 m_pathToModified.remove(oldPath);
362 m_pathToContents.remove(oldPath);
363 m_pathToData.remove(oldPath);
364 removeWatcher(oldPath);
369 m_resourceSetToPaths[resourceSet] = newPaths;
376 m_fileWatcher->removePath(path);
382 m_fileWatcher->addPath(path);
387 const auto it = m_fileWatchedMap.constFind(path);
388 if (it != m_fileWatchedMap.constEnd() && !it.value())
391 m_fileWatchedMap.insert(path,
true);
394 setWatcherEnabled(path,
true);
399 if (!m_fileWatchedMap.contains(path))
402 m_fileWatchedMap.remove(path);
405 setWatcherEnabled(path,
false);
410 setWatcherEnabled(path,
false);
411 emit q_ptr->qrcFileModifiedExternally(path);
412 setWatcherEnabled(path,
true);
416QtResourceModel::QtResourceModel(QObject *parent) :
418 d_ptr(
new QtResourceModelPrivate)
422 d_ptr->m_fileWatcher =
new QFileSystemWatcher(
this);
423 connect(d_ptr->m_fileWatcher, &QFileSystemWatcher::fileChanged,
424 this, [
this](
const QString &fileName) { d_ptr->slotFileChanged(fileName); });
427QtResourceModel::~QtResourceModel()
430 const auto resourceList = resourceSets();
431 for (QtResourceSet *rs : resourceList)
432 removeResourceSet(rs);
436QStringList QtResourceModel::loadedQrcFiles()
const
438 return d_ptr->m_pathToModified.keys();
441bool QtResourceModel::isModified(
const QString &path)
const
443 return d_ptr->m_pathToModified.value(path,
true);
446void QtResourceModel::setModified(
const QString &path)
448 if (!d_ptr->m_pathToModified.contains(path))
451 d_ptr->m_pathToModified[path] =
true;
452 const auto it = d_ptr->m_pathToResourceSet.constFind(path);
453 if (it == d_ptr->m_pathToResourceSet.constEnd())
456 const auto resourceList = it.value();
457 for (QtResourceSet *rs : resourceList)
458 d_ptr->m_resourceSetToReload.insert(rs,
true);
461QList<QtResourceSet *> QtResourceModel::resourceSets()
const
463 return d_ptr->m_resourceSetToPaths.keys();
466QtResourceSet *QtResourceModel::currentResourceSet()
const
468 return d_ptr->m_currentResourceSet;
471void QtResourceModel::setCurrentResourceSet(QtResourceSet *resourceSet,
int *errorCount, QString *errorMessages)
473 d_ptr->activate(resourceSet, d_ptr->m_resourceSetToPaths.value(resourceSet), errorCount, errorMessages);
476QtResourceSet *QtResourceModel::addResourceSet(
const QStringList &paths)
478 QtResourceSet *newResource =
new QtResourceSet(
this);
479 d_ptr->m_resourceSetToPaths.insert(newResource, paths);
480 d_ptr->m_resourceSetToReload.insert(newResource,
false);
481 d_ptr->m_newlyCreated.insert(newResource,
true);
482 for (
const QString &path : paths)
483 d_ptr->m_pathToResourceSet[path].append(newResource);
488void QtResourceModel::removeResourceSet(QtResourceSet *resourceSet)
492 if (currentResourceSet() == resourceSet)
493 setCurrentResourceSet(
nullptr);
496 d_ptr->removeOldPaths(resourceSet, QStringList());
498 d_ptr->m_resourceSetToPaths.remove(resourceSet);
499 d_ptr->m_resourceSetToReload.remove(resourceSet);
500 d_ptr->m_newlyCreated.remove(resourceSet);
504void QtResourceModel::reload(
const QString &path,
int *errorCount, QString *errorMessages)
508 d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages);
511void QtResourceModel::reload(
int *errorCount, QString *errorMessages)
513 for (
auto it = d_ptr->m_pathToModified.begin(), end = d_ptr->m_pathToModified.end(); it != end; ++it)
517 for (
auto itReload = d_ptr->m_resourceSetToReload.begin(), end = d_ptr->m_resourceSetToReload.end(); itReload != end; ++itReload)
518 itReload.value() =
true;
520 d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages);
523QMap<QString, QString> QtResourceModel::contents()
const
525 return d_ptr->m_fileToQrc;
528QString QtResourceModel::qrcPath(
const QString &file)
const
530 return d_ptr->m_fileToQrc.value(file);
533void QtResourceModel::setWatcherEnabled(
bool enable)
535 if (d_ptr->m_fileWatcherEnabled == enable)
538 d_ptr->m_fileWatcherEnabled = enable;
540 if (!d_ptr->m_fileWatchedMap.isEmpty())
541 d_ptr->setWatcherEnabled(d_ptr->m_fileWatchedMap.firstKey(), d_ptr->m_fileWatcherEnabled);
544bool QtResourceModel::isWatcherEnabled()
const
546 return d_ptr->m_fileWatcherEnabled;
549void QtResourceModel::setWatcherEnabled(
const QString &path,
bool enable)
551 const auto it = d_ptr->m_fileWatchedMap.find(path);
552 if (it == d_ptr->m_fileWatchedMap.end())
555 if (it.value() == enable)
560 if (!d_ptr->m_fileWatcherEnabled)
563 d_ptr->setWatcherEnabled(it.key(), enable);
566bool QtResourceModel::isWatcherEnabled(
const QString &path)
568 return d_ptr->m_fileWatchedMap.value(path,
false);
573#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)
Combined button and popup list for selecting options.