summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLostDragonist <lost.dragonist@gmail.com>2019-01-22 14:59:07 -0600
committerLostDragonist <lost.dragonist@gmail.com>2019-01-22 14:59:07 -0600
commit7fd81029ce2f67aa7a6555858dfed7d6ac58c4e3 (patch)
tree7cfca3567dbd02aee370ffa68e1cff28f4b37b1c
parenta40613f885c18a8a9f090cd88c595bcb960d97c9 (diff)
Reduce file I/O operations when enabling/disabling multiple mods
-rw-r--r--src/mainwindow.cpp6
-rw-r--r--src/mainwindow.h1
-rw-r--r--src/modlist.cpp36
-rw-r--r--src/modlist.h13
-rw-r--r--src/modlistsortproxy.cpp8
-rw-r--r--src/organizercore.cpp190
-rw-r--r--src/organizercore.h3
-rw-r--r--src/profile.cpp31
-rw-r--r--src/profile.h17
9 files changed, 229 insertions, 76 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index df78df82..a9dd3e51 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -2509,6 +2509,12 @@ void MainWindow::modlistChanged(const QModelIndex&, int)
updateModCount();
}
+void MainWindow::modlistChanged(const QModelIndexList&, int)
+{
+ m_OrganizerCore.currentProfile()->writeModlist();
+ updateModCount();
+}
+
void MainWindow::modlistSelectionChanged(const QModelIndex &current, const QModelIndex&)
{
if (current.isValid()) {
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 23c38f19..e0311046 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -549,6 +549,7 @@ private slots:
void updateStyle(const QString &style);
void modlistChanged(const QModelIndex &index, int role);
+ void modlistChanged(const QModelIndexList &indicies, int role);
void fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName);
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 58fe95d5..cc15a111 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -527,7 +527,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
m_Profile->setModEnabled(modID, enabled);
m_Modified = true;
m_LastCheck.restart();
- emit modlist_changed(index, role);
+ emit modlistChanged(index, role);
}
result = true;
emit dataChanged(index, index);
@@ -562,7 +562,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
int newID = value.toInt(&ok);
if (ok) {
info->setNexusID(newID);
- emit modlist_changed(index, role);
+ emit modlistChanged(index, role);
result = true;
} else {
result = false;
@@ -1283,12 +1283,24 @@ bool ModList::toggleSelection(QAbstractItemView *itemView)
QItemSelectionModel *selectionModel = itemView->selectionModel();
+ QList<unsigned int> modsToEnable;
+ QList<unsigned int> modsToDisable;
+ QModelIndexList dirtyMods;
for (QModelIndex idx : selectionModel->selectedRows()) {
int modId = idx.data(Qt::UserRole + 1).toInt();
- m_Profile->setModEnabled(modId, !m_Profile->modEnabled(modId));
- emit modlist_changed(idx, 0);
+ if (m_Profile->modEnabled(modId)) {
+ modsToDisable.append(modId);
+ dirtyMods.append(idx);
+ } else {
+ modsToEnable.append(modId);
+ dirtyMods.append(idx);
+ }
}
+ m_Profile->setModsEnabled(modsToEnable, modsToDisable);
+
+ emit modlistChanged(dirtyMods, 0);
+
m_Modified = true;
m_LastCheck.restart();
@@ -1331,13 +1343,12 @@ bool ModList::eventFilter(QObject *obj, QEvent *event)
void ModList::enableSelected(const QItemSelectionModel *selectionModel)
{
if (selectionModel->hasSelection()) {
- bool dirty = false;
+ QList<unsigned int> modsToEnable;
for (auto row : selectionModel->selectedRows(COL_PRIORITY)) {
int modID = m_Profile->modIndexByPriority(row.data().toInt());
- if (!m_Profile->modEnabled(modID)) {
- m_Profile->setModEnabled(modID, true);
- }
+ modsToEnable.append(modID);
}
+ m_Profile->setModsEnabled(modsToEnable, QList<unsigned int>());
}
}
@@ -1345,14 +1356,11 @@ void ModList::enableSelected(const QItemSelectionModel *selectionModel)
void ModList::disableSelected(const QItemSelectionModel *selectionModel)
{
if (selectionModel->hasSelection()) {
- bool dirty = false;
+ QList<unsigned int> modsToDisable;
for (auto row : selectionModel->selectedRows(COL_PRIORITY)) {
int modID = m_Profile->modIndexByPriority(row.data().toInt());
- if (m_Profile->modEnabled(modID)) {
- m_Profile->setModEnabled(modID, false);
- }
+ modsToDisable.append(modID);
}
-
-
+ m_Profile->setModsEnabled(QList<unsigned int>(), modsToDisable);
}
}
diff --git a/src/modlist.h b/src/modlist.h
index 42269386..443d583b 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -233,9 +233,18 @@ signals:
* @param role role of the field that changed
* @note this signal must only be emitted if the row really did change.
* Slots handling this signal therefore do not have to verify that a change has happened
- * @note this signal is currently only used in tutorials
**/
- void modlist_changed(const QModelIndex &index, int role);
+ void modlistChanged(const QModelIndex &index, int role);
+
+ /**
+ * @brief emitted whenever multiple row sin the list has changed
+ *
+ * @param indicies the list of indicies of the changed field
+ * @param role role of the field that changed
+ * @note this signal must only be emitted if the row really did change.
+ * Slots handling this signal therefore do not have to verify that a change has happened
+ **/
+ void modlistChanged(const QModelIndexList &indicies, int role);
/**
* @brief emitted to have all selected mods deleted
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index 464f9104..5afcc26a 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -86,10 +86,12 @@ void ModListSortProxy::enableAllVisible()
{
if (m_Profile == nullptr) return;
+ QList<unsigned int> modsToEnable;
for (int i = 0; i < this->rowCount(); ++i) {
int modID = mapToSource(index(i, 0)).data(Qt::UserRole + 1).toInt();
- m_Profile->setModEnabled(modID, true);
+ modsToEnable.append(modID);
}
+ m_Profile->setModsEnabled(modsToEnable, QList<unsigned int>());
invalidate();
}
@@ -97,10 +99,12 @@ void ModListSortProxy::disableAllVisible()
{
if (m_Profile == nullptr) return;
+ QList<unsigned int> modsToDisable;
for (int i = 0; i < this->rowCount(); ++i) {
int modID = mapToSource(index(i, 0)).data(Qt::UserRole + 1).toInt();
- m_Profile->setModEnabled(modID, false);
+ modsToDisable.append(modID);
}
+ m_Profile->setModsEnabled(QList<unsigned int>(), modsToDisable);
invalidate();
}
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 90e702ef..90e5a423 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -542,8 +542,10 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface,
m_UserInterface = userInterface;
if (widget != nullptr) {
- connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget,
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), widget,
SLOT(modlistChanged(QModelIndex, int)));
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), widget,
+ SLOT(modlistChanged(QModelIndexList, int)));
connect(&m_ModList, SIGNAL(showMessage(QString)), widget,
SLOT(showMessage(QString)));
connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), widget,
@@ -821,8 +823,8 @@ void OrganizerCore::setCurrentProfile(const QString &profileName)
m_CurrentProfile->deactivateInvalidation();
}
- connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this,
- SLOT(modStatusChanged(uint)));
+ connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint)));
+ connect(m_CurrentProfile, SIGNAL(modStatusChanged(QList<uint>)), this, SLOT(modStatusChanged(QList<uint>)));
refreshDirectoryStructure();
}
@@ -1798,60 +1800,70 @@ void OrganizerCore::refreshLists()
void OrganizerCore::updateModActiveState(int index, bool active)
{
- ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
- QDir dir(modInfo->absolutePath());
- for (const QString &esm :
- dir.entryList(QStringList() << "*.esm", QDir::Files)) {
- const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esm));
- if (file.get() == nullptr) {
- qWarning("failed to activate %s", qUtf8Printable(esm));
- continue;
- }
+ QList<unsigned int> modsToUpdate;
+ modsToUpdate.append(index);
+ updateModsActiveState(modsToUpdate, active);
+}
- if (active != m_PluginList.isEnabled(esm)
- && file->getAlternatives().empty()) {
- m_PluginList.blockSignals(true);
- m_PluginList.enableESP(esm, active);
- m_PluginList.blockSignals(false);
- }
- }
- int enabled = 0;
- for (const QString &esl :
- dir.entryList(QStringList() << "*.esl", QDir::Files)) {
- const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esl));
- if (file.get() == nullptr) {
- qWarning("failed to activate %s", qUtf8Printable(esl));
- continue;
- }
+void OrganizerCore::updateModsActiveState(const QList<unsigned int> &modIndices, bool active)
+{
+ int enabled = 0;
+ for (auto index : modIndices) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
+ QDir dir(modInfo->absolutePath());
+ for (const QString &esm :
+ dir.entryList(QStringList() << "*.esm", QDir::Files)) {
+ const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esm));
+ if (file.get() == nullptr) {
+ qWarning("failed to activate %s", qUtf8Printable(esm));
+ continue;
+ }
- if (active != m_PluginList.isEnabled(esl)
+ if (active != m_PluginList.isEnabled(esm)
&& file->getAlternatives().empty()) {
- m_PluginList.blockSignals(true);
- m_PluginList.enableESP(esl, active);
- m_PluginList.blockSignals(false);
- ++enabled;
+ m_PluginList.blockSignals(true);
+ m_PluginList.enableESP(esm, active);
+ m_PluginList.blockSignals(false);
+ }
}
- }
- QStringList esps = dir.entryList(QStringList() << "*.esp", QDir::Files);
- for (const QString &esp : esps) {
- const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esp));
- if (file.get() == nullptr) {
- qWarning("failed to activate %s", qUtf8Printable(esp));
- continue;
+
+ for (const QString &esl :
+ dir.entryList(QStringList() << "*.esl", QDir::Files)) {
+ const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esl));
+ if (file.get() == nullptr) {
+ qWarning("failed to activate %s", qUtf8Printable(esl));
+ continue;
+ }
+
+ if (active != m_PluginList.isEnabled(esl)
+ && file->getAlternatives().empty()) {
+ m_PluginList.blockSignals(true);
+ m_PluginList.enableESP(esl, active);
+ m_PluginList.blockSignals(false);
+ ++enabled;
+ }
}
+ QStringList esps = dir.entryList(QStringList() << "*.esp", QDir::Files);
+ for (const QString &esp : esps) {
+ const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esp));
+ if (file.get() == nullptr) {
+ qWarning("failed to activate %s", qUtf8Printable(esp));
+ continue;
+ }
- if (active != m_PluginList.isEnabled(esp)
+ if (active != m_PluginList.isEnabled(esp)
&& file->getAlternatives().empty()) {
- m_PluginList.blockSignals(true);
- m_PluginList.enableESP(esp, active);
- m_PluginList.blockSignals(false);
- ++enabled;
+ m_PluginList.blockSignals(true);
+ m_PluginList.enableESP(esp, active);
+ m_PluginList.blockSignals(false);
+ ++enabled;
+ }
}
}
if (active && (enabled > 1)) {
MessageDialog::showMessage(
- tr("Multiple esps/esls activated, please check that they don't conflict."),
- qApp->activeWindow());
+ tr("Multiple esps/esls activated, please check that they don't conflict."),
+ qApp->activeWindow());
}
m_PluginList.refreshLoadOrder();
// immediately save affected lists
@@ -1861,18 +1873,29 @@ void OrganizerCore::updateModActiveState(int index, bool active)
void OrganizerCore::updateModInDirectoryStructure(unsigned int index,
ModInfo::Ptr modInfo)
{
- // add files of the bsa to the directory structure
- m_DirectoryRefresher.addModFilesToStructure(
- m_DirectoryStructure, modInfo->name(),
- m_CurrentProfile->getModPriority(index), modInfo->absolutePath(),
- modInfo->stealFiles());
+ QMap<unsigned int, ModInfo::Ptr> allModInfo;
+ allModInfo[index] = modInfo;
+ updateModsInDirectoryStructure(allModInfo);
+}
+
+void OrganizerCore::updateModsInDirectoryStructure(QMap<unsigned int, ModInfo::Ptr> modInfo)
+{
+ for (auto idx : modInfo.keys()) {
+ // add files of the bsa to the directory structure
+ m_DirectoryRefresher.addModFilesToStructure(
+ m_DirectoryStructure, modInfo[idx]->name(),
+ m_CurrentProfile->getModPriority(idx), modInfo[idx]->absolutePath(),
+ modInfo[idx]->stealFiles());
+ }
DirectoryRefresher::cleanStructure(m_DirectoryStructure);
// need to refresh plugin list now so we can activate esps
refreshESPList(true);
// activate all esps of the specified mod so the bsas get activated along with
// it
m_PluginList.blockSignals(true);
- updateModActiveState(index, true);
+ for (auto idx : modInfo.keys()) {
+ updateModActiveState(idx, true);
+ }
m_PluginList.blockSignals(false);
// now we need to refresh the bsa list and save it so there is no confusion
// about what archives are avaiable and active
@@ -1883,14 +1906,16 @@ void OrganizerCore::updateModInDirectoryStructure(unsigned int index,
std::vector<QString> archives = enabledArchives();
m_DirectoryRefresher.setMods(
- m_CurrentProfile->getActiveMods(),
- std::set<QString>(archives.begin(), archives.end()));
+ m_CurrentProfile->getActiveMods(),
+ std::set<QString>(archives.begin(), archives.end()));
// finally also add files from bsas to the directory structure
- m_DirectoryRefresher.addModBSAToStructure(
- m_DirectoryStructure, modInfo->name(),
- m_CurrentProfile->getModPriority(index), modInfo->absolutePath(),
- modInfo->archives());
+ for (auto idx : modInfo.keys()) {
+ m_DirectoryRefresher.addModBSAToStructure(
+ m_DirectoryStructure, modInfo[idx]->name(),
+ m_CurrentProfile->getModPriority(idx), modInfo[idx]->absolutePath(),
+ modInfo[idx]->archives());
+ }
}
void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply)
@@ -2065,6 +2090,55 @@ void OrganizerCore::modStatusChanged(unsigned int index)
}
}
+void OrganizerCore::modStatusChanged(QList<unsigned int> index) {
+ try {
+ QMap<unsigned int, ModInfo::Ptr> modsToEnable;
+ QMap<unsigned int, ModInfo::Ptr> modsToDisable;
+ for (auto idx : index) {
+ if (m_CurrentProfile->modEnabled(idx)) {
+ modsToEnable[idx] = ModInfo::getByIndex(idx);
+ } else {
+ modsToDisable[idx] = ModInfo::getByIndex(idx);
+ }
+ }
+ if (!modsToEnable.isEmpty()) {
+ updateModsInDirectoryStructure(modsToEnable);
+ for (auto modInfo : modsToEnable.values()) {
+ modInfo->clearCaches();
+ }
+ }
+ if (!modsToDisable.isEmpty()) {
+ updateModsActiveState(modsToDisable.keys(), false);
+ for (auto idx : modsToDisable.keys()) {
+ if (m_DirectoryStructure->originExists(ToWString(modsToDisable[idx]->name()))) {
+ FilesOrigin &origin
+ = m_DirectoryStructure->getOriginByName(ToWString(modsToDisable[idx]->name()));
+ origin.enable(false);
+ }
+ }
+ if (m_UserInterface != nullptr) {
+ m_UserInterface->archivesWriter().write();
+ }
+ }
+
+ for (unsigned int i = 0; i < m_CurrentProfile->numMods(); ++i) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
+ int priority = m_CurrentProfile->getModPriority(i);
+ if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) {
+ // priorities in the directory structure are one higher because data is
+ // 0
+ m_DirectoryStructure->getOriginByName(ToWString(modInfo->name()))
+ .setPriority(priority + 1);
+ }
+ }
+ m_DirectoryStructure->getFileRegister()->sortOrigins();
+
+ refreshLists();
+ } catch (const std::exception &e) {
+ reportError(tr("failed to update mod list: %1").arg(e.what()));
+ }
+}
+
void OrganizerCore::loginSuccessful(bool necessary)
{
if (necessary) {
diff --git a/src/organizercore.h b/src/organizercore.h
index 61020acd..7a62d2c8 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -136,6 +136,7 @@ public:
void refreshDirectoryStructure();
void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo);
+ void updateModsInDirectoryStructure(QMap<unsigned int, ModInfo::Ptr> modInfos);
void doAfterLogin(const std::function<void()> &function) { m_PostLoginTasks.append(function); }
@@ -238,6 +239,7 @@ public slots:
void installDownload(int downloadIndex);
void modStatusChanged(unsigned int index);
+ void modStatusChanged(QList<unsigned int> index);
void requestDownload(const QUrl &url, QNetworkReply *reply);
void downloadRequestedNXM(const QString &url);
@@ -266,6 +268,7 @@ private:
bool queryLogin(QString &username, QString &password);
void updateModActiveState(int index, bool active);
+ void updateModsActiveState(const QList<unsigned int> &modIndices, bool active);
bool testForSteam();
diff --git a/src/profile.cpp b/src/profile.cpp
index afe6fdc7..629e043f 100644
--- a/src/profile.cpp
+++ b/src/profile.cpp
@@ -568,6 +568,37 @@ void Profile::setModEnabled(unsigned int index, bool enabled)
}
}
+void Profile::setModsEnabled(const QList<unsigned int> &modsToEnable, const QList<unsigned int> &modsToDisable)
+{
+ QList<unsigned int> dirtyMods;
+ for (auto idx : modsToEnable) {
+ if (idx >= m_ModStatus.size()) {
+ qCritical() << tr("invalid index %1").arg(idx);
+ continue;
+ }
+ if (!m_ModStatus[idx].m_Enabled) {
+ m_ModStatus[idx].m_Enabled = true;
+ dirtyMods.append(idx);
+ }
+ }
+ for (auto idx : modsToDisable) {
+ if (idx >= m_ModStatus.size()) {
+ qCritical() << tr("invalid index %1").arg(idx);
+ continue;
+ }
+ if (ModInfo::getByIndex(idx)->alwaysEnabled()) {
+ continue;
+ }
+ if (m_ModStatus[idx].m_Enabled) {
+ m_ModStatus[idx].m_Enabled = false;
+ dirtyMods.append(idx);
+ }
+ }
+ if (!dirtyMods.isEmpty()) {
+ emit modStatusChanged(dirtyMods);
+ }
+}
+
bool Profile::modEnabled(unsigned int index) const
{
if (index >= m_ModStatus.size()) {
diff --git a/src/profile.h b/src/profile.h
index dea933ad..a7ba7e91 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -274,6 +274,16 @@ public:
void setModEnabled(unsigned int index, bool enabled);
/**
+ * @brief enable or disable multiple mods at once
+ * This is an abbreviated process and should be immediately followed by a full refresh
+ * to maintain data consistency.
+ *
+ * @param modsToEnable list of mod indicies to enable
+ * @param modsToDisable list of mod indicies to disable
+ **/
+ void setModsEnabled(const QList<unsigned int> &modsToEnable, const QList<unsigned int> &modsToDisable);
+
+ /**
* change the priority of a mod. Of course this also changes the priority of other mods.
* The priority of the mods in the range ]old, new priority] are shifted so that no gaps
* are possible.
@@ -335,6 +345,13 @@ signals:
**/
void modStatusChanged(unsigned int index);
+ /**
+ * @brief emitted whenever the status (enabled/disabled) of multiple mods change
+ *
+ * @param index list of indices of the mods that changed
+ **/
+ void modStatusChanged(QList<unsigned int> index);
+
public slots:
// should only be called by DelayedFileWriter, use writeModlist() and writeModlistNow() instead