From 1c6543454ef4d3b84e2ce1f6928b6c99005eee29 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Fri, 11 May 2018 20:16:48 +0200 Subject: Initial work on avoiding the spamming of the Downlods tab refresh caused by the fileSystemWatcher picking up changes made by MO itself and triggering multiple refreshes --- src/downloadmanager.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++++-- src/downloadmanager.h | 16 ++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 8aba42aa..ea8253be 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -180,7 +180,7 @@ QString DownloadManager::DownloadInfo::currentURL() DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) - : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false), + : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_DirWatcherDisabler(0), m_ShowHidden(false), m_DateExpression("/Date\\((\\d+)\\)/") { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); @@ -209,8 +209,10 @@ bool DownloadManager::downloadsInProgress() return false; } + void DownloadManager::pauseAll() { + // first loop: pause all downloads for (int i = 0; i < m_ActiveDownloads.count(); ++i) { if (m_ActiveDownloads[i]->m_State < STATE_READY) { @@ -238,6 +240,7 @@ void DownloadManager::pauseAll() ::Sleep(100); } } + } @@ -276,9 +279,30 @@ void DownloadManager::setPluginContainer(PluginContainer *pluginContainer) m_NexusInterface->setPluginContainer(pluginContainer); } + +void DownloadManager::startDisableDirWatcher() +{ + m_DirWatcherDisabler++; +} + + +void DownloadManager::endDisableDirWatcher() +{ + QCoreApplication::processEvents(); + if (m_DirWatcherDisabler > 0) + m_DirWatcherDisabler--; + else + m_DirWatcherDisabler = 0; +} + + + void DownloadManager::refreshList() { try { + //avoid triggering other refreshes + startDisableDirWatcher(); + int downloadsBefore = m_ActiveDownloads.size(); // remove finished downloads @@ -339,6 +363,10 @@ void DownloadManager::refreshList() qDebug("downloads after refresh: %d", m_ActiveDownloads.size()); } emit update(-1); + + //let watcher trigger refreshes again + endDisableDirWatcher(); + } catch (const std::bad_alloc&) { reportError(tr("Memory allocation error (in refreshing directory).")); } @@ -388,7 +416,10 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, baseName = dispoName; } } + + startDisableDirWatcher(); newDownload->setName(getDownloadFileName(baseName), false); + endDisableDirWatcher(); startDownload(reply, newDownload, false); // emit update(-1); @@ -458,7 +489,9 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl else setState(newDownload, STATE_CANCELING); } else { + startDisableDirWatcher(); newDownload->setName(getDownloadFileName(newDownload->m_FileName, true), true); + endDisableDirWatcher(); if (newDownload->m_State == STATE_PAUSED) resumeDownload(indexByName(newDownload->m_FileName)); else @@ -530,6 +563,9 @@ void DownloadManager::addNXMDownload(const QString &url) void DownloadManager::removeFile(int index, bool deleteFile) { + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + if (index >= m_ActiveDownloads.size()) { throw MyException(tr("remove: invalid download index %1").arg(index)); } @@ -540,6 +576,7 @@ void DownloadManager::removeFile(int index, bool deleteFile) (download->m_State == STATE_DOWNLOADING)) { // shouldn't have been possible qCritical("tried to remove active download"); + endDisableDirWatcher(); return; } @@ -550,6 +587,7 @@ void DownloadManager::removeFile(int index, bool deleteFile) if (deleteFile) { if (!shellDelete(QStringList(filePath), true)) { reportError(tr("failed to delete %1").arg(filePath)); + endDisableDirWatcher(); return; } @@ -561,6 +599,8 @@ void DownloadManager::removeFile(int index, bool deleteFile) QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); metaSettings.setValue("removed", true); } + + endDisableDirWatcher(); } class LessThanWrapper @@ -606,14 +646,22 @@ void DownloadManager::restoreDownload(int index) QString filePath = m_OutputDirectory + "/" + download->m_FileName; + //avoid dirWatcher triggering refreshes + startDisableDirWatcher(); + QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); metaSettings.setValue("removed", false); + + endDisableDirWatcher(); } void DownloadManager::removeDownload(int index, bool deleteFile) { try { + //avoid dirWatcher triggering refreshes + startDisableDirWatcher(); + emit aboutToUpdate(); if (index < 0) { @@ -634,6 +682,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) } else { if (index >= m_ActiveDownloads.size()) { reportError(tr("remove: invalid download index %1").arg(index)); + endDisableDirWatcher(); return; } @@ -641,6 +690,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) delete m_ActiveDownloads.at(index); m_ActiveDownloads.erase(m_ActiveDownloads.begin() + index); } + endDisableDirWatcher(); emit update(-1); } catch (const std::exception &e) { qCritical("failed to remove download: %s", e.what()); @@ -970,11 +1020,16 @@ void DownloadManager::markInstalled(int index) throw MyException(tr("mark installed: invalid download index %1").arg(index)); } + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + DownloadInfo *info = m_ActiveDownloads.at(index); QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("installed", true); metaFile.setValue("uninstalled", false); + endDisableDirWatcher(); + setState(m_ActiveDownloads.at(index), STATE_INSTALLED); } @@ -986,10 +1041,15 @@ void DownloadManager::markInstalled(QString fileName) } else { DownloadInfo *info = getDownloadInfo(fileName); if (info != nullptr) { + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("installed", true); metaFile.setValue("uninstalled", false); delete info; + + endDisableDirWatcher(); } } } @@ -1005,10 +1065,15 @@ void DownloadManager::markUninstalled(int index) throw MyException(tr("mark uninstalled: invalid download index %1").arg(index)); } + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + DownloadInfo *info = m_ActiveDownloads.at(index); QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("uninstalled", true); + endDisableDirWatcher(); + setState(m_ActiveDownloads.at(index), STATE_UNINSTALLED); } @@ -1022,9 +1087,15 @@ void DownloadManager::markUninstalled(QString fileName) QString filePath = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + fileName; DownloadInfo *info = getDownloadInfo(filePath); if (info != nullptr) { + + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("uninstalled", true); delete info; + + endDisableDirWatcher(); } } } @@ -1183,6 +1254,9 @@ void DownloadManager::downloadReadyRead() void DownloadManager::createMetaFile(DownloadInfo *info) { + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + QSettings metaFile(QString("%1.meta").arg(info->m_Output.fileName()), QSettings::IniFormat); metaFile.setValue("gameName", info->m_FileInfo->gameName); metaFile.setValue("modID", info->m_FileInfo->modID); @@ -1204,6 +1278,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info) (info->m_State == DownloadManager::STATE_ERROR)); metaFile.setValue("removed", info->m_Hidden); + endDisableDirWatcher(); // slightly hackish... for (int i = 0; i < m_ActiveDownloads.size(); ++i) { if (m_ActiveDownloads[i] == info) { @@ -1572,11 +1647,14 @@ void DownloadManager::downloadFinished(int index) QString newName = getFileNameFromNetworkReply(reply); QString oldName = QFileInfo(info->m_Output).fileName(); + + startDisableDirWatcher(); if (!newName.isEmpty() && (oldName.isEmpty())) { info->setName(getDownloadFileName(newName), true); } else { info->setName(m_OutputDirectory + "/" + info->m_FileName, true); // don't rename but remove the ".unfinished" extension } + endDisableDirWatcher(); if (!isNexus) { setState(info, STATE_READY); @@ -1616,7 +1694,9 @@ void DownloadManager::metaDataChanged() if (info != nullptr) { QString newName = getFileNameFromNetworkReply(info->m_Reply); if (!newName.isEmpty() && (info->m_FileName.isEmpty())) { + startDisableDirWatcher(); info->setName(getDownloadFileName(newName), true); + endDisableDirWatcher(); refreshAlphabeticalTranslation(); if (!info->m_Output.isOpen() && !info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) { reportError(tr("failed to re-open %1").arg(info->m_FileName)); @@ -1630,7 +1710,8 @@ void DownloadManager::metaDataChanged() void DownloadManager::directoryChanged(const QString&) { - refreshList(); + if(m_DirWatcherDisabler==0) + refreshList(); } void DownloadManager::managedGameChanged(MOBase::IPluginGame const *managedGame) diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 17ae8a35..74627be5 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -144,6 +144,17 @@ public: **/ void setOutputDirectory(const QString &outputDirectory); + /** + * @brief disables feedback from the downlods fileSystemWhatcher untill disableDownloadsWatcherEnd() is called + * + **/ + void startDisableDirWatcher(); + + /** + * @brief re-enables feedback from the downlods fileSystemWhatcher after disableDownloadsWatcherStart() was called + **/ + void endDisableDirWatcher(); + /** * @return current download directory **/ @@ -519,6 +530,11 @@ private: QFileSystemWatcher m_DirWatcher; + //The dirWatcher is actually triggering off normal Mo operations such as deleting downloads or editing .meta files + //so it needs to be disabled during operations that are known to cause the creation or deletion of files in the Downloads folder. + //Notably using QSettings to edit a file creates a temporarily .lock file that causes the Watcher to trigger multiple listRefreshes freezing the ui. + int m_DirWatcherDisabler; + std::map m_DownloadFails; bool m_ShowHidden; -- cgit v1.3.1