mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4ce82a262 | ||
|
|
4242abeab1 | ||
|
|
7d8d4bb545 | ||
|
|
af789246f9 | ||
|
|
6b5ffc41f4 | ||
|
|
5a44a6d934 | ||
|
|
78f4343304 | ||
|
|
6eb5191653 | ||
|
|
1e8ada5348 | ||
|
|
f7bb36b1b1 | ||
|
|
c94858be4d | ||
|
|
07faf9c635 | ||
|
|
829ee4f9c2 |
+13
-8
@@ -35,8 +35,10 @@ DownloadList::DownloadList(OrganizerCore& core, QObject* parent)
|
||||
: QAbstractTableModel(parent), m_manager(*core.downloadManager()),
|
||||
m_settings(core.settings())
|
||||
{
|
||||
connect(&m_manager, SIGNAL(update(int)), this, SLOT(update(int)));
|
||||
connect(&m_manager, SIGNAL(aboutToUpdate()), this, SLOT(aboutToUpdate()));
|
||||
connect(&m_manager, &DownloadManager::aboutToResetModel, this,
|
||||
&DownloadList::onAboutToResetModel);
|
||||
connect(&m_manager, &DownloadManager::modelReset, this, &DownloadList::onModelReset);
|
||||
connect(&m_manager, &DownloadManager::rowChanged, this, &DownloadList::onRowChanged);
|
||||
}
|
||||
|
||||
int DownloadList::rowCount(const QModelIndex& parent) const
|
||||
@@ -239,16 +241,19 @@ QVariant DownloadList::data(const QModelIndex& index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void DownloadList::aboutToUpdate()
|
||||
void DownloadList::onAboutToResetModel()
|
||||
{
|
||||
emit beginResetModel();
|
||||
beginResetModel();
|
||||
}
|
||||
|
||||
void DownloadList::update(int row)
|
||||
void DownloadList::onModelReset()
|
||||
{
|
||||
if (row < 0)
|
||||
emit endResetModel();
|
||||
else if (row < this->rowCount())
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void DownloadList::onRowChanged(int row)
|
||||
{
|
||||
if (row < this->rowCount())
|
||||
emit dataChanged(
|
||||
this->index(row, 0, QModelIndex()),
|
||||
this->index(row, this->columnCount(QModelIndex()) - 1, QModelIndex()));
|
||||
|
||||
+11
-7
@@ -83,16 +83,20 @@ public:
|
||||
//
|
||||
bool lessThanPredicate(const QModelIndex& left, const QModelIndex& right);
|
||||
|
||||
public slots:
|
||||
private slots:
|
||||
|
||||
/**
|
||||
* @brief used to inform the model that data has changed
|
||||
*
|
||||
* @param row the row that changed. This can be negative to update the whole view
|
||||
**/
|
||||
void update(int row);
|
||||
* @brief full reset (row count changed). Drops selection and scroll state.
|
||||
*/
|
||||
void onAboutToResetModel();
|
||||
void onModelReset();
|
||||
|
||||
void aboutToUpdate();
|
||||
/**
|
||||
* @brief single-row data change (row count unchanged). Preserves view state.
|
||||
*
|
||||
* @param row the row that changed
|
||||
*/
|
||||
void onRowChanged(int row);
|
||||
|
||||
private:
|
||||
DownloadManager& m_manager;
|
||||
|
||||
+205
-239
File diff suppressed because it is too large
Load Diff
+112
-44
@@ -52,6 +52,58 @@ class NexusInterface;
|
||||
class PluginContainer;
|
||||
class OrganizerCore;
|
||||
|
||||
/**
|
||||
* @brief QFileSystemWatcher with a nestable RAII suspension scope.
|
||||
*
|
||||
* Forwards directoryChanged() only while no Guard is alive. Use a Guard to
|
||||
* bracket filesystem writes that would otherwise trigger a spurious refresh.
|
||||
*/
|
||||
class DirWatcherManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DirWatcherManager(QObject* parent = nullptr);
|
||||
|
||||
/// Set the directory being watched (replaces any previous path).
|
||||
void setPath(const QString& path);
|
||||
|
||||
/// True while one or more Guards are alive.
|
||||
bool isSuspended() const;
|
||||
|
||||
/**
|
||||
* @brief RAII suspension guard. Nests safely; the only way to suspend
|
||||
* forwarding.
|
||||
*/
|
||||
class [[nodiscard]] Guard
|
||||
{
|
||||
public:
|
||||
explicit Guard(DirWatcherManager& manager);
|
||||
~Guard();
|
||||
Guard(const Guard&) = delete;
|
||||
Guard& operator=(const Guard&) = delete;
|
||||
Guard(Guard&&) = delete;
|
||||
Guard& operator=(Guard&&) = delete;
|
||||
|
||||
private:
|
||||
DirWatcherManager& m_manager;
|
||||
};
|
||||
|
||||
/// Returns a suspension Guard bound to the caller's scope.
|
||||
[[nodiscard]] Guard scopedGuard();
|
||||
|
||||
signals:
|
||||
/// Emitted when the watched directory changes and no Guard is active.
|
||||
void directoryChanged();
|
||||
|
||||
private slots:
|
||||
void onDirectoryChanged(const QString&);
|
||||
|
||||
private:
|
||||
QFileSystemWatcher m_watcher;
|
||||
int m_suspendDepth = 0;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief manages downloading of files and provides progress information for gui
|
||||
*elements
|
||||
@@ -61,6 +113,25 @@ class DownloadManager : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief RAII full-reset guard. Use when the row count changes; drops
|
||||
* view selection/scroll state. Nests safely: inner guards coalesce into
|
||||
* the outermost scope so only one reset is emitted.
|
||||
*/
|
||||
class [[nodiscard]] ModelResetGuard
|
||||
{
|
||||
public:
|
||||
explicit ModelResetGuard(DownloadManager& manager);
|
||||
~ModelResetGuard();
|
||||
ModelResetGuard(const ModelResetGuard&) = delete;
|
||||
ModelResetGuard& operator=(const ModelResetGuard&) = delete;
|
||||
ModelResetGuard(ModelResetGuard&&) = delete;
|
||||
ModelResetGuard& operator=(ModelResetGuard&&) = delete;
|
||||
|
||||
private:
|
||||
DownloadManager& m_manager;
|
||||
};
|
||||
|
||||
enum DownloadState
|
||||
{
|
||||
STATE_STARTED = 0,
|
||||
@@ -191,19 +262,6 @@ public:
|
||||
**/
|
||||
void setOutputDirectory(const QString& outputDirectory, const bool refresh = true);
|
||||
|
||||
/**
|
||||
* @brief disables feedback from the downlods fileSystemWhatcher untill
|
||||
*disableDownloadsWatcherEnd() is called
|
||||
*
|
||||
**/
|
||||
static void startDisableDirWatcher();
|
||||
|
||||
/**
|
||||
* @brief re-enables feedback from the downlods fileSystemWhatcher after
|
||||
*disableDownloadsWatcherStart() was called
|
||||
**/
|
||||
static void endDisableDirWatcher();
|
||||
|
||||
/**
|
||||
* @return current download directory
|
||||
**/
|
||||
@@ -408,6 +466,12 @@ public:
|
||||
*/
|
||||
void queryDownloadListInfo();
|
||||
|
||||
/**
|
||||
* @return the directory watcher for the downloads folder; call
|
||||
* scopedGuard() on it to suspend across filesystem writes.
|
||||
*/
|
||||
DirWatcherManager& dirWatcher() { return m_DirWatcher; }
|
||||
|
||||
public: // IDownloadManager interface:
|
||||
int startDownloadURLs(const QStringList& urls);
|
||||
int startDownloadURLWithMeta(const QString& url, const QString& name,
|
||||
@@ -435,16 +499,38 @@ public: // IDownloadManager interface:
|
||||
|
||||
void pauseAll();
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
void aboutToUpdate();
|
||||
|
||||
/**
|
||||
* @brief signals that the specified download has changed
|
||||
* @brief notify the UI that a single row's data changed. Preserves view
|
||||
* state; prefer over ModelResetGuard when the row count is unchanged.
|
||||
*
|
||||
* @param row the row that changed. This corresponds to the download index
|
||||
**/
|
||||
void update(int row);
|
||||
*/
|
||||
void notifyRowChanged(int row);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
/**
|
||||
* @brief emitted before the download list model is about to be reset
|
||||
*
|
||||
* Emitted by ModelResetGuard on construction. Views should call
|
||||
* beginResetModel() in response.
|
||||
*/
|
||||
void aboutToResetModel();
|
||||
|
||||
/**
|
||||
* @brief emitted after the download list model has been reset
|
||||
*
|
||||
* Emitted by ModelResetGuard on destruction. Views should call
|
||||
* endResetModel() in response.
|
||||
*/
|
||||
void modelReset();
|
||||
|
||||
/**
|
||||
* @brief signals that the specified download row's data has changed
|
||||
*
|
||||
* @param row the row that changed. This corresponds to the download index
|
||||
*/
|
||||
void rowChanged(int row);
|
||||
|
||||
/**
|
||||
* @brief signals the ui that a message should be displayed
|
||||
@@ -541,7 +627,6 @@ private slots:
|
||||
void downloadFinished(int index = 0);
|
||||
void downloadError(QNetworkReply::NetworkError error);
|
||||
void metaDataChanged();
|
||||
void directoryChanged(const QString& dirctory);
|
||||
void checkDownloadTimeout();
|
||||
|
||||
private:
|
||||
@@ -581,10 +666,6 @@ private:
|
||||
|
||||
void removeFile(int index, bool deleteFile);
|
||||
|
||||
void refreshAlphabeticalTranslation();
|
||||
|
||||
bool ByName(int LHS, int RHS);
|
||||
|
||||
QString getFileNameFromNetworkReply(QNetworkReply* reply);
|
||||
|
||||
void setState(DownloadInfo* info, DownloadManager::DownloadState state);
|
||||
@@ -597,6 +678,8 @@ private:
|
||||
|
||||
void writeData(DownloadInfo* info);
|
||||
|
||||
QString getValidGameShortName(const QString& gameNexusName) const;
|
||||
|
||||
private:
|
||||
static const int AUTOMATIC_RETRIES = 3;
|
||||
|
||||
@@ -612,22 +695,17 @@ private:
|
||||
|
||||
QString m_OutputDirectory;
|
||||
std::set<int> m_RequestIDs;
|
||||
QVector<int> m_AlphabeticalTranslation;
|
||||
|
||||
QFileSystemWatcher m_DirWatcher;
|
||||
DirWatcherManager m_DirWatcher;
|
||||
|
||||
// nesting depth of active ModelResetGuard scopes; see its docs
|
||||
int m_modelResetDepth = 0;
|
||||
|
||||
SignalDownloadCallback m_DownloadComplete;
|
||||
SignalDownloadCallback m_DownloadPaused;
|
||||
SignalDownloadCallback m_DownloadFailed;
|
||||
SignalDownloadCallback m_DownloadRemoved;
|
||||
|
||||
// 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.
|
||||
static int m_DirWatcherDisabler;
|
||||
|
||||
std::map<QString, int> m_DownloadFails;
|
||||
|
||||
bool m_ShowHidden;
|
||||
@@ -637,14 +715,4 @@ private:
|
||||
QTimer m_TimeoutTimer;
|
||||
};
|
||||
|
||||
class ScopedDisableDirWatcher
|
||||
{
|
||||
public:
|
||||
ScopedDisableDirWatcher(DownloadManager* downloadManager);
|
||||
~ScopedDisableDirWatcher();
|
||||
|
||||
private:
|
||||
DownloadManager* m_downloadManager;
|
||||
};
|
||||
|
||||
#endif // DOWNLOADMANAGER_H
|
||||
|
||||
@@ -818,11 +818,13 @@ void ModListViewActions::removeMods(const QModelIndexList& indices) const
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
// use mod names instead of indexes because those become invalid during the
|
||||
// removal
|
||||
DownloadManager::startDisableDirWatcher();
|
||||
for (QString name : modNames) {
|
||||
m_core.modList()->removeRowForce(ModInfo::getIndex(name), QModelIndex());
|
||||
{
|
||||
DirWatcherManager::Guard dirWatcherGuard =
|
||||
m_core.downloadManager()->dirWatcher().scopedGuard();
|
||||
for (QString name : modNames) {
|
||||
m_core.modList()->removeRowForce(ModInfo::getIndex(name), QModelIndex());
|
||||
}
|
||||
}
|
||||
DownloadManager::endDisableDirWatcher();
|
||||
}
|
||||
} else if (!indices.isEmpty()) {
|
||||
m_core.modList()->removeRow(indices[0].data(ModList::IndexRole).toInt(),
|
||||
|
||||
@@ -866,7 +866,8 @@ OrganizerCore::doInstall(const QString& archivePath, GuessedValue<QString> modNa
|
||||
|
||||
ModInfo::Ptr OrganizerCore::installDownload(int index, int priority)
|
||||
{
|
||||
ScopedDisableDirWatcher scopedDirwatcher(&m_DownloadManager);
|
||||
DirWatcherManager::Guard dirWatcherGuard =
|
||||
m_DownloadManager.dirWatcher().scopedGuard();
|
||||
|
||||
try {
|
||||
QString fileName = m_DownloadManager.getFilePath(index);
|
||||
|
||||
Reference in New Issue
Block a user