summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/datatab.cpp24
-rw-r--r--src/datatab.h10
-rw-r--r--src/directoryrefresher.cpp2
-rw-r--r--src/downloadlist.cpp2
-rw-r--r--src/mainwindow.cpp18
-rw-r--r--src/mainwindow.h7
-rw-r--r--src/modinfo.h16
-rw-r--r--src/modinfobackup.h2
-rw-r--r--src/modinfodialogconflicts.cpp3
-rw-r--r--src/modinfoforeign.h1
-rw-r--r--src/modinfooverwrite.h1
-rw-r--r--src/modinforegular.h6
-rw-r--r--src/modlist.cpp110
-rw-r--r--src/modlist.h9
-rw-r--r--src/modlistbypriorityproxy.cpp2
-rw-r--r--src/modlistdropinfo.cpp4
-rw-r--r--src/modlistdropinfo.h4
-rw-r--r--src/modlistsortproxy.cpp7
-rw-r--r--src/modlistview.cpp146
-rw-r--r--src/modlistview.h113
-rw-r--r--src/modlistviewactions.cpp14
-rw-r--r--src/organizercore.cpp47
-rw-r--r--src/organizercore.h1
-rw-r--r--src/profile.cpp258
-rw-r--r--src/profile.h69
-rw-r--r--src/settingsdialogplugins.cpp30
-rw-r--r--src/settingsdialogplugins.h8
-rw-r--r--src/shared/filesorigin.cpp2
-rw-r--r--src/shared/filesorigin.h4
-rw-r--r--src/shared/originconnection.cpp14
-rw-r--r--src/shared/originconnection.h1
31 files changed, 463 insertions, 472 deletions
diff --git a/src/datatab.cpp b/src/datatab.cpp
index 21c205e5..165ee26a 100644
--- a/src/datatab.cpp
+++ b/src/datatab.cpp
@@ -20,9 +20,9 @@ DataTab::DataTab(
QWidget* parent, Ui::MainWindow* mwui) :
m_core(core), m_pluginContainer(pc), m_parent(parent),
ui{
- mwui->dataTabRefresh, mwui->dataTree,
+ mwui->tabWidget, mwui->dataTab, mwui->dataTabRefresh, mwui->dataTree,
mwui->dataTabShowOnlyConflicts, mwui->dataTabShowFromArchives},
- m_firstActivation(true)
+ m_needUpdate(true)
{
m_filetree.reset(new FileTree(core, m_pluginContainer, ui.tree));
m_filter.setUseSourceSort(true);
@@ -85,12 +85,16 @@ void DataTab::restoreState(const Settings& s)
void DataTab::activated()
{
- if (m_firstActivation) {
- m_firstActivation = false;
+ if (m_needUpdate) {
updateTree();
}
}
+bool DataTab::isActive() const
+{
+ return ui.tabs->currentWidget() == ui.tab;
+}
+
void DataTab::onRefresh()
{
if (QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) {
@@ -103,6 +107,16 @@ void DataTab::onRefresh()
void DataTab::updateTree()
{
+ if (isActive()) {
+ doUpdateTree();
+ }
+ else {
+ m_needUpdate = true;
+ }
+}
+
+void DataTab::doUpdateTree()
+{
m_filetree->model()->setEnabled(true);
m_filetree->refresh();
@@ -113,6 +127,8 @@ void DataTab::updateTree()
m->invalidate();
}
}
+
+ m_needUpdate = true;
}
void DataTab::ensureFullyLoaded()
diff --git a/src/datatab.h b/src/datatab.h
index 32d788f6..ad172ab0 100644
--- a/src/datatab.h
+++ b/src/datatab.h
@@ -29,6 +29,10 @@ public:
void restoreState(const Settings& s);
void activated();
+ // if the data tab is currently visible, trigger an update of the
+ // tree, otherwise mark the tree has modified and will refresh when
+ // the tab is activated
+ //
void updateTree();
signals:
@@ -39,6 +43,8 @@ signals:
private:
struct DataTabUi
{
+ QTabWidget* tabs;
+ QWidget* tab;
QPushButton* refresh;
QTreeView* tree;
QCheckBox* conflicts;
@@ -52,7 +58,7 @@ private:
std::unique_ptr<FileTree> m_filetree;
std::vector<QTreeWidgetItem*> m_removeLater;
MOBase::FilterWidget m_filter;
- bool m_firstActivation;
+ bool m_needUpdate;
void onRefresh();
void onItemExpanded(QTreeWidgetItem* item);
@@ -60,6 +66,8 @@ private:
void onArchives();
void updateOptions();
void ensureFullyLoaded();
+ bool isActive() const;
+ void doUpdateTree();
};
#endif // MODORGANIZER_DATATAB_INCLUDED
diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp
index 26e43d53..063cd328 100644
--- a/src/directoryrefresher.cpp
+++ b/src/directoryrefresher.cpp
@@ -415,7 +415,7 @@ void DirectoryRefresher::addMultipleModsFilesToStructure(
for (std::size_t i=0; i<entries.size(); ++i) {
const auto& e = entries[i];
- const int prio = static_cast<int>(i + 1);
+ const int prio = e.priority + 1;
if constexpr (DirectoryStats::EnableInstrumentation) {
stats[i].mod = entries[i].modName.toStdString();
diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp
index 93f8538c..9f29f475 100644
--- a/src/downloadlist.cpp
+++ b/src/downloadlist.cpp
@@ -99,7 +99,7 @@ Qt::ItemFlags DownloadList::flags(const QModelIndex& idx) const
QMimeData* DownloadList::mimeData(const QModelIndexList& indexes) const
{
QMimeData* result = QAbstractItemModel::mimeData(indexes);
- result->setData("text/plain", ModListDropInfo::DOWNLOAD_TEXT);
+ result->setData("text/plain", ModListDropInfo::DownloadText);
return result;
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index a25406dd..4053a030 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -313,6 +313,15 @@ MainWindow::MainWindow(Settings &settings
setupModList();
ui->espList->setup(m_OrganizerCore, this, ui);
+ connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [this]() {
+ onDirectoryStructureChanged();
+ });
+ connect(m_OrganizerCore.modList(), &ModList::modStatesChanged, [this]() {
+ onDirectoryStructureChanged();
+ });
+ connect(m_OrganizerCore.modList(), &QAbstractItemModel::rowsRemoved, [this]() {
+ onDirectoryStructureChanged();
+ });
ui->bsaList->setLocalMoveOnly(true);
ui->bsaList->setHeaderHidden(true);
@@ -385,7 +394,7 @@ MainWindow::MainWindow(Settings &settings
connect(&m_PluginContainer, SIGNAL(diagnosisUpdate()), this, SLOT(scheduleCheckForProblems()));
- connect(m_OrganizerCore.directoryRefresher(), SIGNAL(refreshed()), this, SLOT(directory_refreshed()));
+ connect(m_OrganizerCore.directoryRefresher(), &DirectoryRefresher::refreshed, [this] { onDirectoryStructureChanged(); });
connect(
m_OrganizerCore.directoryRefresher(),
&DirectoryRefresher::progress,
@@ -2191,15 +2200,12 @@ void MainWindow::refresherProgress(const DirectoryRefreshProgress* p)
}
}
-void MainWindow::directory_refreshed()
+void MainWindow::onDirectoryStructureChanged()
{
// some problem-reports may rely on the virtual directory tree so they need to be updated
// now
scheduleCheckForProblems();
-
- if (ui->tabWidget->currentWidget() == ui->dataTab) {
- m_DataTab->updateTree();
- }
+ m_DataTab->updateTree();
}
void MainWindow::modInstalled(const QString &modName)
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 1f521f38..f9cf541c 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -142,8 +142,6 @@ public:
public slots:
void refresherProgress(const DirectoryRefreshProgress* p);
- void directory_refreshed();
-
signals:
/**
@@ -169,6 +167,11 @@ private slots:
private:
+ // update data tab and schedule a problem check after a directory
+ // structure update
+ //
+ void onDirectoryStructureChanged();
+
void cleanup();
void setupToolbar();
diff --git a/src/modinfo.h b/src/modinfo.h
index c9b74a66..f93296d2 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -602,17 +602,15 @@ public: // Methods after this do not come from IModInterface:
*/
virtual void ignoreUpdate(bool ignore) = 0;
- /**
- * @return the fixed priority of mods of this type or INT_MIN if the priority of mods
- * needs to be user-modifiable. Can be < 0 to force a priority below user-modifable mods
- * or INT_MAX to force priority above all user-modifiables.
- */
- virtual int getFixedPriority() const = 0;
+ // check if the priority of this mod is not user-modifiable (i.e.
+ // computed by MO2 automatically)
+ //
+ bool hasAutomaticPriority() const { return isBackup() || isOverwrite(); }
- /**
- * @return true if the mod is always enabled.
- */
+ // check if this mod should always be enabled or disabled
+ //
virtual bool alwaysEnabled() const { return false; }
+ virtual bool alwaysDisabled() const { return false; }
/**
* @return true if the mod can be updated.
diff --git a/src/modinfobackup.h b/src/modinfobackup.h
index f25ee9cf..853a1211 100644
--- a/src/modinfobackup.h
+++ b/src/modinfobackup.h
@@ -19,8 +19,8 @@ public:
virtual void setGameName(const QString& gameName) override {}
virtual void setNexusID(int) override {}
virtual void endorse(bool) override {}
- virtual int getFixedPriority() const override { return -1; }
virtual void ignoreUpdate(bool) override {}
+ virtual bool alwaysDisabled() const override { return true; }
virtual bool canBeUpdated() const override { return false; }
virtual QDateTime getExpires() const override { return QDateTime(); }
virtual bool canBeEnabled() const override { return false; }
diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp
index 1bfb3218..bb3346f7 100644
--- a/src/modinfodialogconflicts.cpp
+++ b/src/modinfodialogconflicts.cpp
@@ -992,9 +992,6 @@ std::optional<ConflictItem> AdvancedConflictsTab::createItem(
if (!alternatives.empty()) {
const bool showAllAlts = ui->conflictsAdvancedShowAll->isChecked();
- int beforePrio = 0;
- int afterPrio = std::numeric_limits<int>::max();
-
if (currOrigin->getID() == fileOrigin) {
// current origin is the active winner, all alternatives go in 'before'
diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h
index 31fd13aa..3acebeaf 100644
--- a/src/modinfoforeign.h
+++ b/src/modinfoforeign.h
@@ -63,7 +63,6 @@ public:
virtual QDateTime getNexusLastModified() const override { return QDateTime(); }
virtual void setNexusLastModified(QDateTime) override {}
virtual QString getNexusDescription() const override { return QString(); }
- virtual int getFixedPriority() const override { return std::numeric_limits<int>::min(); }
virtual QStringList archives(bool = false) override { return m_Archives; }
virtual QStringList stealFiles() const override { return m_Archives + QStringList(m_ReferenceFile); }
virtual bool alwaysEnabled() const override { return true; }
diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h
index 065a3ba2..997e24ea 100644
--- a/src/modinfooverwrite.h
+++ b/src/modinfooverwrite.h
@@ -48,7 +48,6 @@ public:
virtual QString installationFile() const override { return ""; }
virtual bool converted() const override { return false; }
virtual bool validated() const override { return false; }
- virtual int getFixedPriority() const override { return std::numeric_limits<int>::max(); }
virtual QString gameName() const override { return ""; }
virtual int nexusId() const override { return -1; }
virtual bool isOverwrite() const override { return true; }
diff --git a/src/modinforegular.h b/src/modinforegular.h
index 08660993..24f5cf9c 100644
--- a/src/modinforegular.h
+++ b/src/modinforegular.h
@@ -251,12 +251,6 @@ public:
int nexusId() const override { return m_NexusID; }
/**
- * @return the fixed priority of mods of this type or INT_MIN if the priority of mods
- * needs to be user-modifiable
- */
- virtual int getFixedPriority() const override { return std::numeric_limits<int>::min(); }
-
- /**
* @return true if the mod can be updated
*/
virtual bool canBeUpdated() const override;
diff --git a/src/modlist.cpp b/src/modlist.cpp
index ffa841ed..de24fbbd 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -128,32 +128,6 @@ QString ModList::makeInternalName(ModInfo::Ptr info, QString name) const
return name;
}
-QVariant ModList::getOverwriteData(int column, int role) const
-{
- switch (role) {
- case Qt::DisplayRole: {
- if (column == 0) {
- return "Overwrite";
- } else {
- return QVariant();
- }
- } break;
- case Qt::CheckStateRole: {
- if (column == 0) {
- return Qt::Checked;
- } else {
- return QVariant();
- }
- } break;
- case Qt::TextAlignmentRole: return QVariant(Qt::AlignCenter | Qt::AlignVCenter);
- case GroupingRole: return -1;
- case Qt::ForegroundRole: return QBrush(Qt::red);
- case Qt::ToolTipRole: return tr("This entry contains files that have been created inside the virtual data tree (i.e. by the construction kit)");
- default: return QVariant();
- }
-}
-
-
QString ModList::getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const
{
switch (flag) {
@@ -223,8 +197,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
return version;
}
else if (column == COL_PRIORITY) {
- int priority = modInfo->getFixedPriority();
- if (priority != INT_MIN) {
+ if (modInfo->hasAutomaticPriority()) {
return QVariant(); // hide priority for mods where it's fixed
}
else {
@@ -336,15 +309,6 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
return QVariant();
}
}
- else if (column == COL_PRIORITY) {
- int priority = modInfo->getFixedPriority();
- if (priority != INT_MIN) {
- return priority;
- }
- else {
- return m_Profile->getModPriority(modIndex);
- }
- }
else {
return modInfo->nexusId();
}
@@ -365,13 +329,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
return modInfo->gameName();
}
else if (role == PriorityRole) {
- int priority = modInfo->getFixedPriority();
- if (priority != std::numeric_limits<int>::min()) {
- return priority;
- }
- else {
- return m_Profile->getModPriority(modIndex);
- }
+ return m_Profile->getModPriority(modIndex);
}
else if (role == Qt::FontRole) {
QFont result;
@@ -605,9 +563,6 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
case COL_PRIORITY: {
bool ok = false;
int newPriority = value.toInt(&ok);
- if (ok && newPriority < 0) {
- newPriority = 0;
- }
if (ok) {
changeModPriority(modID, newPriority);
result = true;
@@ -687,7 +642,7 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const
}
if (modelIndex.isValid()) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(modelIndex.row());
- if (modInfo->getFixedPriority() == INT_MIN) {
+ if (!modInfo->hasAutomaticPriority()) {
result |= Qt::ItemIsDragEnabled;
result |= Qt::ItemIsUserCheckable;
if ((modelIndex.column() == COL_PRIORITY) ||
@@ -722,7 +677,7 @@ QStringList ModList::mimeTypes() const
QMimeData *ModList::mimeData(const QModelIndexList &indexes) const
{
QMimeData *result = QAbstractItemModel::mimeData(indexes);
- result->setData("text/plain", ModListDropInfo::MOD_TEXT);
+ result->setData("text/plain", ModListDropInfo::ModText);
return result;
}
@@ -739,12 +694,12 @@ void ModList::changeModPriority(std::vector<int> sourceIndices, int newPriority)
});
// move mods that are decreasing in priority
- for (std::vector<int>::const_iterator iter = sourceIndices.begin();
- iter != sourceIndices.end(); ++iter) {
- int oldPriority = m_Profile->getModPriority(*iter);
+ for (const auto& index : sourceIndices) {
+ int oldPriority = m_Profile->getModPriority(index);
if (oldPriority > newPriority) {
- m_Profile->setModPriority(*iter, newPriority);
- m_ModMoved(ModInfo::getByIndex(*iter)->name(), oldPriority, newPriority);
+ if (m_Profile->setModPriority(index, newPriority)) {
+ m_ModMoved(ModInfo::getByIndex(index)->name(), oldPriority, newPriority);
+ }
}
}
@@ -756,9 +711,8 @@ void ModList::changeModPriority(std::vector<int> sourceIndices, int newPriority)
// if at least one mod is increasing in priority, the target index is
// that of the row BELOW the dropped location, otherwise it's the one above
- for (std::vector<int>::const_iterator iter = sourceIndices.begin();
- iter != sourceIndices.end(); ++iter) {
- int oldPriority = m_Profile->getModPriority(*iter);
+ for (const auto& index : sourceIndices) {
+ int oldPriority = m_Profile->getModPriority(index);
if (oldPriority < newPriority) {
--newPriority;
break;
@@ -766,12 +720,12 @@ void ModList::changeModPriority(std::vector<int> sourceIndices, int newPriority)
}
// move mods that are increasing in priority
- for (std::vector<int>::const_iterator iter = sourceIndices.begin();
- iter != sourceIndices.end(); ++iter) {
- int oldPriority = m_Profile->getModPriority(*iter);
+ for (const auto& index : sourceIndices) {
+ int oldPriority = m_Profile->getModPriority(index);
if (oldPriority < newPriority) {
- m_Profile->setModPriority(*iter, newPriority);
- m_ModMoved(ModInfo::getByIndex(*iter)->name(), oldPriority, newPriority);
+ if (m_Profile->setModPriority(index, newPriority)) {
+ m_ModMoved(ModInfo::getByIndex(index)->name(), oldPriority, newPriority);
+ }
}
}
@@ -901,11 +855,8 @@ QStringList ModList::allModsByProfilePriority(MOBase::IProfile* profile) const
m_Organizer->currentProfile() : static_cast<Profile*>(profile);
QStringList res;
- for (int i = mo2Profile->getPriorityMinimum();
- i < mo2Profile->getPriorityMinimum() + (int)mo2Profile->numRegularMods();
- ++i) {
- int modIndex = mo2Profile->modIndexByPriority(i);
- auto modInfo = ModInfo::getByIndex(modIndex);
+ for (auto& [priority, index] : mo2Profile->getAllIndexesByPriority()) {
+ auto modInfo = ModInfo::getByIndex(index);
if (!modInfo->isBackup() && !modInfo->isOverwrite()) {
res.push_back(modInfo->internalName());
}
@@ -1008,16 +959,13 @@ int ModList::priority(const QString &name) const
bool ModList::setPriority(const QString &name, int newPriority)
{
- if ((newPriority < 0) || (newPriority >= static_cast<int>(m_Profile->numRegularMods()))) {
- return false;
- }
-
- unsigned int modIndex = ModInfo::getIndex(name);
- if (modIndex == UINT_MAX) {
+ unsigned int index = ModInfo::getIndex(name);
+ if (index == UINT_MAX) {
return false;
} else {
- m_Profile->setModPriority(modIndex, newPriority);
- notifyChange(modIndex);
+ if (m_Profile->setModPriority(index, newPriority)) {
+ notifyChange(index);
+ }
return true;
}
}
@@ -1074,15 +1022,12 @@ int ModList::dropPriority(int row, const QModelIndex& parent) const
int newPriority = 0;
{
- if ((row < 0) || (row > static_cast<int>(m_Profile->numRegularMods()))) {
- newPriority = m_Profile->numRegularMods() + 1;
+ if (row < 0 || row >= rowCount()) {
+ newPriority = Profile::MaximumPriority;
}
else {
newPriority = m_Profile->getModPriority(row);
}
- if (newPriority == -1) {
- newPriority = m_Profile->numRegularMods() + 1;
- }
}
return newPriority;
@@ -1408,8 +1353,7 @@ void ModList::shiftModsPriority(const QModelIndexList& indices, int offset)
std::vector<int> notify;
for (auto index : allIndex) {
int newPriority = m_Profile->getModPriority(index) + offset;
- if ((newPriority >= 0) && (newPriority < static_cast<int>(m_Profile->numRegularMods()))) {
- m_Profile->setModPriority(index, newPriority);
+ if (m_Profile->setModPriority(index, newPriority)) {
notify.push_back(index);
}
}
@@ -1486,4 +1430,6 @@ void ModList::setActive(const QModelIndexList& indices, bool active)
else {
m_Profile->setModsEnabled({}, mods);
}
+
+ emit modStatesChanged(indices);
}
diff --git a/src/modlist.h b/src/modlist.h
index 5c7f28ee..e201de27 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -341,19 +341,12 @@ private:
QString getDisplayName(ModInfo::Ptr info) const;
QString makeInternalName(ModInfo::Ptr info, QString name) const;
- QVariant getOverwriteData(int column, int role) const;
-
QString getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const;
QString getConflictFlagText(ModInfo::EConflictFlag flag, ModInfo::Ptr modInfo) const;
QString getColumnToolTip(int column) const;
- ModList::EColumn getEnabledColumn(int index) const;
-
- QVariant categoryData(int categoryID, int column, int role) const;
- QVariant modData(int modID, int modelColumn, int role) const;
-
bool renameMod(int index, const QString &newName);
MOBase::IModList::ModStates state(unsigned int modIndex) const;
@@ -366,8 +359,6 @@ private:
//
int dropPriority(int row, const QModelIndex& parent) const;
-private slots:
-
private:
struct TModInfo {
diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp
index 7dae7a54..24e58ed0 100644
--- a/src/modlistbypriorityproxy.cpp
+++ b/src/modlistbypriorityproxy.cpp
@@ -234,7 +234,7 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi
bool hasSeparator = false;
unsigned int firstRowIndex = -1;
- int firstRowPriority = INT_MAX;
+ int firstRowPriority = Profile::MaximumPriority;
for (auto sourceRow : dropInfo.rows()) {
hasSeparator = hasSeparator || ModInfo::getByIndex(sourceRow)->isSeparator();
if (m_sortOrder == Qt::AscendingOrder && m_profile->getModPriority(sourceRow) < firstRowPriority
diff --git a/src/modlistdropinfo.cpp b/src/modlistdropinfo.cpp
index 62a103a4..a247194f 100644
--- a/src/modlistdropinfo.cpp
+++ b/src/modlistdropinfo.cpp
@@ -48,8 +48,8 @@ ModListDropInfo::ModListDropInfo(const QMimeData* mimeData, OrganizerCore& core)
}
}
- if (mimeData->text() != ModListDropInfo::MOD_TEXT) {
- if (mimeData->text() == ModListDropInfo::DOWNLOAD_TEXT && m_rows.size() == 1) {
+ if (mimeData->text() != ModListDropInfo::ModText) {
+ if (mimeData->text() == ModListDropInfo::DownloadText && m_rows.size() == 1) {
m_download = m_rows[0];
}
m_rows = {};
diff --git a/src/modlistdropinfo.h b/src/modlistdropinfo.h
index 14a21691..5d2af92f 100644
--- a/src/modlistdropinfo.h
+++ b/src/modlistdropinfo.h
@@ -18,8 +18,8 @@ public:
// text value for the mime-data for the various possible
// origin (not for external drops)
//
- static constexpr const char* MOD_TEXT = "mod";
- static constexpr const char* DOWNLOAD_TEXT = "download";
+ static constexpr const char* ModText = "mod";
+ static constexpr const char* DownloadText = "download";
public:
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index 18e91e6e..fc7f3270 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -215,7 +215,12 @@ bool ModListSortProxy::lessThan(const QModelIndex &left,
}
} break;
case ModList::COL_PRIORITY: {
- // nop, already compared by priority
+ if (leftMod->isBackup() != rightMod->isBackup()) {
+ lt = leftMod->isBackup();
+ }
+ else if (leftMod->isOverwrite() != rightMod->isOverwrite()) {
+ lt = rightMod->isOverwrite();
+ }
} break;
default: {
log::warn("Sorting is not defined for column {}", left.column());
diff --git a/src/modlistview.cpp b/src/modlistview.cpp
index 9597b48e..d7f90c9f 100644
--- a/src/modlistview.cpp
+++ b/src/modlistview.cpp
@@ -434,42 +434,6 @@ void ModListView::onModPrioritiesChanged(const QModelIndexList& indices)
}
}
- for (unsigned int i = 0; i < m_core->currentProfile()->numMods(); ++i) {
- int priority = m_core->currentProfile()->getModPriority(i);
- if (m_core->currentProfile()->modEnabled(i)) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
- // priorities in the directory structure are one higher because data is 0
- m_core->directoryStructure()->getOriginByName(MOBase::ToWString(modInfo->internalName())).setPriority(priority + 1);
- }
- }
- m_core->refreshBSAList();
- m_core->currentProfile()->writeModlist();
- m_core->directoryStructure()->getFileRegister()->sortOrigins();
-
- for (auto& idx : indices) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt());
- // clear caches on all mods conflicting with the moved mod
- for (int i : modInfo->getModOverwrite()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModOverwritten()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveOverwrite()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveOverwritten()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveLooseOverwrite()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveLooseOverwritten()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- // update conflict check on the moved mod
- modInfo->doConflictCheck();
- }
setOverwriteMarkers(selectionModel()->selectedRows());
}
@@ -507,67 +471,52 @@ void ModListView::onModFilterActive(bool filterActive)
}
}
-void ModListView::updateModCount()
+ModListView::ModCounters ModListView::counters() const
{
- int activeCount = 0;
- int visActiveCount = 0;
- int backupCount = 0;
- int visBackupCount = 0;
- int foreignCount = 0;
- int visForeignCount = 0;
- int separatorCount = 0;
- int visSeparatorCount = 0;
- int regularCount = 0;
- int visRegularCount = 0;
-
- QStringList allMods = m_core->modList()->allMods();
+ ModCounters c;
auto hasFlag = [](std::vector<ModInfo::EFlag> flags, ModInfo::EFlag filter) {
return std::find(flags.begin(), flags.end(), filter) != flags.end();
};
- bool isEnabled;
- bool isVisible;
- for (QString mod : allMods) {
- int modIndex = ModInfo::getIndex(mod);
- ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
- std::vector<ModInfo::EFlag> modFlags = modInfo->getFlags();
- isEnabled = m_core->currentProfile()->modEnabled(modIndex);
- isVisible = m_sortProxy->filterMatchesMod(modInfo, isEnabled);
- for (auto flag : modFlags) {
- switch (flag) {
- case ModInfo::FLAG_BACKUP: backupCount++;
- if (isVisible)
- visBackupCount++;
- break;
- case ModInfo::FLAG_FOREIGN: foreignCount++;
- if (isVisible)
- visForeignCount++;
- break;
- case ModInfo::FLAG_SEPARATOR: separatorCount++;
- if (isVisible)
- visSeparatorCount++;
- break;
- }
- }
+ for (unsigned int index = 0; index < ModInfo::getNumMods(); ++index) {
+ auto info = ModInfo::getByIndex(index);
+ const auto flags = info->getFlags();
+
+ const bool enabled = m_core->currentProfile()->modEnabled(index);
+ const bool visible = m_sortProxy->filterMatchesMod(info, enabled);
- if (!hasFlag(modFlags, ModInfo::FLAG_BACKUP) &&
- !hasFlag(modFlags, ModInfo::FLAG_FOREIGN) &&
- !hasFlag(modFlags, ModInfo::FLAG_SEPARATOR) &&
- !hasFlag(modFlags, ModInfo::FLAG_OVERWRITE)) {
- if (isEnabled) {
- activeCount++;
- if (isVisible)
- visActiveCount++;
+ if (info->isBackup()) {
+ c.backup++;
+ if (visible) c.visible.backup++;
+ }
+ else if (info->isForeign()) {
+ c.foreign++;
+ if (visible) c.visible.foreign++;
+ }
+ else if (info->isSeparator()) {
+ c.separator++;
+ if (visible) c.visible.separator++;
+ }
+ else if (!info->isOverwrite()) {
+ c.regular++;
+ if (visible) c.visible.regular++;
+ if (enabled) {
+ c.active++;
+ if (visible) c.visible.active++;
}
- if (isVisible)
- visRegularCount++;
- regularCount++;
}
}
- ui.counter->display(visActiveCount);
+ return c;
+}
+
+void ModListView::updateModCount()
+{
+ const auto c = counters();
+
+ ui.counter->display(c.visible.active);
ui.counter->setToolTip(tr("<table cellspacing=\"5\">"
"<tr><th>Type</th><th>All</th><th>Visible</th>"
"<tr><td>Enabled mods:&emsp;</td><td align=right>%1 / %2</td><td align=right>%3 / %4</td></tr>"
@@ -575,16 +524,16 @@ void ModListView::updateModCount()
"<tr><td>Mod backups:&emsp;</td><td align=right>%7</td><td align=right>%8</td></tr>"
"<tr><td>Separators:&emsp;</td><td align=right>%9</td><td align=right>%10</td></tr>"
"</table>")
- .arg(activeCount)
- .arg(regularCount)
- .arg(visActiveCount)
- .arg(visRegularCount)
- .arg(foreignCount)
- .arg(visForeignCount)
- .arg(backupCount)
- .arg(visBackupCount)
- .arg(separatorCount)
- .arg(visSeparatorCount)
+ .arg(c.active)
+ .arg(c.regular)
+ .arg(c.visible.active)
+ .arg(c.visible.regular)
+ .arg(c.foreign)
+ .arg(c.visible.foreign)
+ .arg(c.backup)
+ .arg(c.visible.backup)
+ .arg(c.separator)
+ .arg(c.visible.separator)
);
}
@@ -647,16 +596,15 @@ void ModListView::onExternalFolderDropped(const QUrl& url, int priority)
bool ModListView::moveSelection(int key)
{
- auto [cindex, sourceRows] = selected();
+ auto rows = selectionModel()->selectedRows();
+ const QPersistentModelIndex current(key == Qt::Key_Up ? rows.first() : rows.last());
int offset = key == Qt::Key_Up ? -1 : 1;
if (m_sortProxy->sortOrder() == Qt::DescendingOrder) {
offset = -offset;
}
- m_core->modList()->shiftModsPriority(sourceRows, offset);
-
- auto current = indexModelToView(key == Qt::Key_Up ? sourceRows.first() : sourceRows.last());
+ m_core->modList()->shiftModsPriority(indexViewToModel(rows), offset);
selectionModel()->setCurrentIndex(current, QItemSelectionModel::NoUpdate);
scrollTo(current);
diff --git a/src/modlistview.h b/src/modlistview.h
index adffd737..408bc900 100644
--- a/src/modlistview.h
+++ b/src/modlistview.h
@@ -131,9 +131,6 @@ public slots:
protected:
- friend class ModListContextMenu;
- friend class ModListViewActions;
-
// map from/to the view indexes to the model
//
QModelIndex indexModelToView(const QModelIndex& index) const;
@@ -183,14 +180,74 @@ protected slots:
void commitData(QWidget* editor) override;
-private:
+private: // friend classes
friend class ModConflictIconDelegate;
- friend class ModFlagIconDelegate;
friend class ModContentIconDelegate;
+ friend class ModFlagIconDelegate;
+ friend class ModListContextMenu;
friend class ModListStyledItemDelegate;
+ friend class ModListViewActions;
friend class ModListViewMarkingScrollBar;
+private: // private structures
+
+ struct ModListViewUi {
+ // the group by combo box
+ QComboBox* groupBy;
+
+ // the mod counter
+ QLCDNumber* counter;
+
+ // filters related
+ QLineEdit* filter;
+ QLabel* currentCategory;
+ QPushButton* clearFilters;
+ QComboBox* filterSeparators;
+
+ // the plugin list (for highligths)
+ PluginListView* pluginList;
+ };
+
+ struct MarkerInfos {
+ // conflicts
+ std::set<unsigned int> overwrite;
+ std::set<unsigned int> overwritten;
+ std::set<unsigned int> archiveOverwrite;
+ std::set<unsigned int> archiveOverwritten;
+ std::set<unsigned int> archiveLooseOverwrite;
+ std::set<unsigned int> archiveLooseOverwritten;
+
+ // selected plugins
+ std::set<unsigned int> highlight;
+ };
+
+ struct ModCounters {
+ int active = 0;
+ int backup = 0;
+ int foreign = 0;
+ int separator = 0;
+ int regular = 0;
+
+ struct {
+ int active = 0;
+ int backup = 0;
+ int foreign = 0;
+ int separator = 0;
+ int regular = 0;
+ } visible;
+ };
+
+ // index in the groupby combo
+ //
+ enum GroupBy {
+ NONE = 0,
+ CATEGORY = 1,
+ NEXUS_ID = 2
+ };
+
+private: // private functions
+
void onModPrioritiesChanged(const QModelIndexList& indices);
void onModInstalled(const QString& modName);
void onModFilterActive(bool filterActive);
@@ -229,6 +286,10 @@ private:
QList<QString> contentsIcons(const QModelIndex& index, bool* forceCompact = nullptr) const;
QString contentsTooltip(const QModelIndex& index) const;
+ // compute the counters for mods according to the current filter
+ //
+ ModCounters counters() const;
+
// get/set the selected items on the view, this method return/take indices
// from the mod list model, not the view, so it's safe to restore
//
@@ -244,33 +305,7 @@ private:
//
void updateGroupByProxy();
- // index in the groupby combo
- //
- enum GroupBy {
- NONE = 0,
- CATEGORY = 1,
- NEXUS_ID = 2
- };
-
-private:
-
- struct ModListViewUi
- {
- // the group by combo box
- QComboBox* groupBy;
-
- // the mod counter
- QLCDNumber* counter;
-
- // filters related
- QLineEdit* filter;
- QLabel* currentCategory;
- QPushButton* clearFilters;
- QComboBox* filterSeparators;
-
- // the plugin list (for highligths)
- PluginListView* pluginList;
- };
+public: // member variables
OrganizerCore* m_core;
std::unique_ptr<FilterList> m_filters;
@@ -291,19 +326,7 @@ private:
// losing them on model reset
std::map<QAbstractItemModel*, std::set<QString>> m_collapsed;
- struct MarkerInfos {
- // conflicts
- std::set<unsigned int> overwrite;
- std::set<unsigned int> overwritten;
- std::set<unsigned int> archiveOverwrite;
- std::set<unsigned int> archiveOverwritten;
- std::set<unsigned int> archiveLooseOverwrite;
- std::set<unsigned int> archiveLooseOverwritten;
-
- // selected plugins
- std::set<unsigned int> highlight;
- } m_markers;
-
+ MarkerInfos m_markers;
ViewMarkingScrollBar* m_scrollbar;
bool m_inDragMoveEvent = false;
diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp
index e7e74b16..9d4be554 100644
--- a/src/modlistviewactions.cpp
+++ b/src/modlistviewactions.cpp
@@ -207,10 +207,16 @@ void ModListViewActions::createSeparator(const QModelIndex& index) const
void ModListViewActions::setAllMatchingModsEnabled(bool enabled) const
{
+ // number of mods to enable / disable
+ const auto counters = m_view->counters();
+ const auto count = enabled ?
+ counters.visible.regular - counters.visible.active : counters.visible.active;
+
+ // retrieve visible mods from the model view
const auto allIndex = m_view->indexViewToModel(flatIndex(m_view->model()));
const QString message = enabled ?
tr("Really enable %1 mod(s)?") : tr("Really disable %1 mod(s)?");
- if (QMessageBox::question(m_parent, tr("Confirm"), message.arg(allIndex.size()),
+ if (QMessageBox::question(m_parent, tr("Confirm"), message.arg(count),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
m_core.modList()->setActive(allIndex, enabled);
}
@@ -539,12 +545,12 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in
void ModListViewActions::sendModsToTop(const QModelIndexList& indexes) const
{
- m_core.modList()->changeModsPriority(indexes, 0);
+ m_core.modList()->changeModsPriority(indexes, Profile::MinimumPriority);
}
void ModListViewActions::sendModsToBottom(const QModelIndexList& indexes) const
{
- m_core.modList()->changeModsPriority(indexes, std::numeric_limits<int>::max());
+ m_core.modList()->changeModsPriority(indexes, Profile::MaximumPriority);
}
void ModListViewActions::sendModsToPriority(const QModelIndexList& indexes) const
@@ -580,7 +586,7 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& indexes) con
if (!result.isEmpty()) {
result += "_separator";
- int newPriority = std::numeric_limits<int>::max();
+ int newPriority = Profile::MaximumPriority;
bool foundSection = false;
for (auto mod : m_core.modList()->allModsByProfilePriority()) {
unsigned int modIndex = ModInfo::getIndex(mod);
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index d6c22d3c..df4cfe5e 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -119,6 +119,7 @@ OrganizerCore::OrganizerCore(Settings &settings)
connect(&m_ModList, SIGNAL(removeOrigin(QString)), this,
SLOT(removeOrigin(QString)));
connect(&m_ModList, &ModList::modStatesChanged, [=] { currentProfile()->writeModlist(); });
+ connect(&m_ModList, &ModList::modPrioritiesChanged, [this](auto&& indexes) { modPrioritiesChanged(indexes); });
connect(NexusInterface::instance().getAccessManager(),
SIGNAL(validateSuccessful(bool)), this, SLOT(loginSuccessful(bool)));
@@ -581,8 +582,8 @@ void OrganizerCore::setCurrentProfile(const QString &profileName)
m_Settings.game().setSelectedProfileName(m_CurrentProfile->name());
- connect(m_CurrentProfile.get(), SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint)));
- connect(m_CurrentProfile.get(), SIGNAL(modStatusChanged(QList<uint>)), this, SLOT(modStatusChanged(QList<uint>)));
+ connect(m_CurrentProfile.get(), qOverload<uint>(&Profile::modStatusChanged), [this](auto&& index) { modStatusChanged(index); });
+ connect(m_CurrentProfile.get(), qOverload<QList<uint>>(&Profile::modStatusChanged), [this](auto&& indexes) { modStatusChanged(indexes); });
refreshDirectoryStructure();
m_CurrentProfile->debugDump();
@@ -1565,6 +1566,46 @@ void OrganizerCore::profileRefresh()
refresh();
}
+void OrganizerCore::modPrioritiesChanged(const QModelIndexList& indices)
+{
+ for (unsigned int i = 0; i < currentProfile()->numMods(); ++i) {
+ int priority = currentProfile()->getModPriority(i);
+ if (currentProfile()->modEnabled(i)) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
+ // priorities in the directory structure are one higher because data is 0
+ directoryStructure()->getOriginByName(MOBase::ToWString(modInfo->internalName())).setPriority(priority + 1);
+ }
+ }
+ refreshBSAList();
+ currentProfile()->writeModlist();
+ directoryStructure()->getFileRegister()->sortOrigins();
+
+ for (auto& idx : indices) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt());
+ // clear caches on all mods conflicting with the moved mod
+ for (int i : modInfo->getModOverwrite()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModOverwritten()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModArchiveOverwrite()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModArchiveOverwritten()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModArchiveLooseOverwrite()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ for (int i : modInfo->getModArchiveLooseOverwritten()) {
+ ModInfo::getByIndex(i)->clearCaches();
+ }
+ // update conflict check on the moved mod
+ modInfo->doConflictCheck();
+ }
+}
+
void OrganizerCore::modStatusChanged(unsigned int index)
{
try {
@@ -1943,7 +1984,7 @@ std::vector<Mapping> OrganizerCore::fileMapping(const QString &profileName,
bool overwriteActive = false;
- for (auto mod : profile.getActiveMods()) {
+ for (const auto& mod : profile.getActiveMods()) {
if (std::get<0>(mod).compare("overwrite", Qt::CaseInsensitive) == 0) {
continue;
}
diff --git a/src/organizercore.h b/src/organizercore.h
index 53686c8a..c00d0d42 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -362,6 +362,7 @@ public slots:
ModInfo::Ptr installArchive(const QString& archivePath, int priority = -1, bool reinstallation = false,
ModInfo::Ptr currentMod = nullptr, const QString& modName = QString());
+ void modPrioritiesChanged(QModelIndexList const& indexes);
void modStatusChanged(unsigned int index);
void modStatusChanged(QList<unsigned int> index);
void requestDownload(const QUrl &url, QNetworkReply *reply);
diff --git a/src/profile.cpp b/src/profile.cpp
index bb3a11e2..2546661b 100644
--- a/src/profile.cpp
+++ b/src/profile.cpp
@@ -230,23 +230,20 @@ void Profile::doWriteModlist()
return;
}
- for (std::map<int, unsigned int>::const_reverse_iterator iter = m_ModIndexByPriority.crbegin(); iter != m_ModIndexByPriority.crend(); iter++ ) {
+ for (auto iter = m_ModIndexByPriority.crbegin(); iter != m_ModIndexByPriority.crend(); iter++) {
// the priority order was inverted on load so it has to be inverted again
- unsigned int index = iter->second;
- if (index != UINT_MAX) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
- std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
- if ((modInfo->getFixedPriority() == INT_MIN)) {
- if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) {
- file->write("*");
- } else if (m_ModStatus[index].m_Enabled) {
- file->write("+");
- } else {
- file->write("-");
- }
- file->write(modInfo->name().toUtf8());
- file->write("\r\n");
+ const auto index = iter->second;
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
+ if (!modInfo->hasAutomaticPriority()) {
+ if (modInfo->isForeign()) {
+ file->write("*");
+ } else if (m_ModStatus[index].m_Enabled) {
+ file->write("+");
+ } else {
+ file->write("-");
}
+ file->write(modInfo->name().toUtf8());
+ file->write("\r\n");
}
}
@@ -270,10 +267,9 @@ void Profile::createTweakedIniFile()
return;
}
- for (int i = getPriorityMinimum(); i < getPriorityMinimum() + (int)numRegularMods(); ++i) {
- unsigned int idx = modIndexByPriority(i);
- if (m_ModStatus[idx].m_Enabled) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(idx);
+ for (const auto& [priority, index] : m_ModIndexByPriority) {
+ if (m_ModStatus[index].m_Enabled) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
mergeTweaks(modInfo, tweakedIni);
}
}
@@ -368,6 +364,38 @@ void Profile::renameModInList(QFile &modList, const QString &oldName, const QStr
void Profile::refreshModStatus()
{
+ // this function refreshes mod status (enabled/disabled) and priority
+ // using the profile mod list file and the mods in the mods folder using
+ // the following steps
+ //
+ // 1) the mod list file is read and mods status/priority are updated by
+ // considering the content of the file (for status) and the order (for
+ // priority), missing or invalid mods are discarded (with a warning)
+ // 2) the priority are reversed to match the plugin list (highest wins)
+ // since the mod list is written in reverse order
+ // 3) at the same time, new mods (not in the mod list file) are added
+ // - foreign mods are given low priority (below 0)
+ // - regular mods are given high priority (above mods from the mod list)
+ // 4) the priority are shifted to ensure that the minimum priority is 0
+ // 5) the priority of backups are computed such that the first backup is
+ // above all regular mods
+ //
+ // in the context of the profile, "regular mods" means a mod whose priority
+ // can be set by the user (i.e. not a backup or overwrite)
+ //
+ // this method ensures that the mods priority is as follow
+ //
+ // 0 mod1
+ // 1 mod2
+ // ...
+ // K-1 modK (K = m_NumRegularMods)
+ // K backup1
+ // K+1 backup2
+ // ...
+ // N-2 backupX
+ // N-1 overwrite (N = number of mods)
+ //
+
writeModlistNow(true); // if there are pending changes write them first
QFile file(getModlistFileName());
@@ -387,6 +415,8 @@ void Profile::refreshModStatus()
int index = 0;
while (!file.atEnd()) {
QByteArray line = file.readLine().trimmed();
+
+ // find the mod name and the enabled status
bool enabled = true;
QString modName;
if (line.length() == 0) {
@@ -398,89 +428,106 @@ void Profile::refreshModStatus()
} else if (line.at(0) == '-') {
enabled = false;
modName = QString::fromUtf8(line.mid(1).trimmed().constData());
- } else if ((line.at(0) == '+')
- || (line.at(0) == '*')) {
+ } else if (line.at(0) == '+' || line.at(0) == '*') {
modName = QString::fromUtf8(line.mid(1).trimmed().constData());
} else {
modName = QString::fromUtf8(line.trimmed().constData());
}
- if (modName.size() > 0) {
- QString lookupName = modName;
- if (modName.compare("overwrite", Qt::CaseInsensitive) == 0) {
- warnAboutOverwrite = true;
- }
- if (namesRead.find(lookupName) != namesRead.end()) {
- continue;
- } else {
- namesRead.insert(lookupName);
- }
- unsigned int modIndex = ModInfo::getIndex(lookupName);
- if (modIndex != UINT_MAX) {
- ModInfo::Ptr info = ModInfo::getByIndex(modIndex);
- if ((modIndex < m_ModStatus.size())
- && (info->getFixedPriority() == INT_MIN)) {
- m_ModStatus[modIndex].m_Enabled = enabled;
- if (m_ModStatus[modIndex].m_Priority == -1) {
- if (static_cast<size_t>(index) >= m_ModStatus.size()) {
- throw MyException(tr("invalid mod index: %1").arg(index));
- }
- m_ModStatus[modIndex].m_Priority = index++;
- }
- } else {
- log::warn(
- "no mod state for \"{}\" (profile \"{}\")",
- modName, m_Directory.path());
- // need to rewrite the modlist to fix this
- modStatusModified = true;
+
+ if (modName.isEmpty()) {
+ continue;
+ }
+
+ if (modName.compare("overwrite", Qt::CaseInsensitive) == 0) {
+ warnAboutOverwrite = true;
+ }
+
+ // check if the name was already read
+ if (namesRead.find(modName) != namesRead.end()) {
+ continue;
+ }
+ namesRead.insert(modName);
+
+ unsigned int modIndex = ModInfo::getIndex(modName);
+ if (modIndex == UINT_MAX) {
+ log::debug(
+ "mod not found: \"{}\" (profile \"{}\")",
+ modName, m_Directory.path());
+ // need to rewrite the modlist to fix this
+ modStatusModified = true;
+ continue;
+ }
+
+ // find the mod and check that this is a regular mod (and not a backup)
+ ModInfo::Ptr info = ModInfo::getByIndex(modIndex);
+ if (modIndex < m_ModStatus.size() && !info->hasAutomaticPriority()) {
+ m_ModStatus[modIndex].m_Enabled = enabled;
+ if (m_ModStatus[modIndex].m_Priority == -1) {
+ if (static_cast<size_t>(index) >= m_ModStatus.size()) {
+ throw Exception(tr("invalid mod index: %1").arg(index));
}
- } else {
- log::debug(
- "mod not found: \"{}\" (profile \"{}\")",
- modName, m_Directory.path());
- // need to rewrite the modlist to fix this
- modStatusModified = true;
+ m_ModStatus[modIndex].m_Priority = index++;
}
+ } else {
+ log::warn(
+ "no mod state for \"{}\" (profile \"{}\")",
+ modName, m_Directory.path());
+ // need to rewrite the modlist to fix this
+ modStatusModified = true;
}
- }
- int numKnownMods = index;
+ } // while (!file.atEnd())
+
+ file.close();
+ const int numKnownMods = index;
int topInsert = 0;
- // invert priority order to match that of the pluginlist. Also
- // give priorities to mods not referenced in the profile
+ // invert priority order to match that of the pluginlist, also
+ // give priorities to mods not referenced in the profile and
+ // count the number of regular mods
+ m_NumRegularMods = 0;
for (size_t i = 0; i < m_ModStatus.size(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(static_cast<int>(i));
if (modInfo->alwaysEnabled()) {
m_ModStatus[i].m_Enabled = true;
}
- if (modInfo->getFixedPriority() == INT_MAX) {
+ if (modInfo->isOverwrite()) {
+ m_ModStatus[i].m_Priority = m_ModStatus.size() - 1;
continue;
}
if (m_ModStatus[i].m_Priority != -1) {
m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1;
+ ++m_NumRegularMods;
} else {
if (static_cast<size_t>(index) >= m_ModStatus.size()) {
- throw MyException(tr("invalid mod index: %1").arg(index));
+ throw Exception(tr("invalid mod index: %1").arg(index));
}
- if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) {
+
+ // skip backups on purpose to avoid inserting backups in-between
+ // regular mods
+ if (modInfo->isForeign()) {
m_ModStatus[i].m_Priority = --topInsert;
- } else {
+ ++m_NumRegularMods;
+ } else if (!modInfo->isBackup()) {
m_ModStatus[i].m_Priority = index++;
+ ++m_NumRegularMods;
}
+
// also, mark the mod-list as changed
modStatusModified = true;
}
}
- // to support insertion of new mods at the top we may now have mods with negative priority. shift them all up
- // to align priority with 0
+
+ // to support insertion of new mods at the top we may now have mods with negative priority,
+ // so shift them all up to align priority with 0
if (topInsert < 0) {
int offset = topInsert * -1;
for (size_t i = 0; i < m_ModStatus.size(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(static_cast<unsigned int>(i));
- if (modInfo->getFixedPriority() == INT_MAX) {
+ if (modInfo->hasAutomaticPriority()) {
continue;
}
@@ -488,7 +535,15 @@ void Profile::refreshModStatus()
}
}
- file.close();
+ // set the backups priority
+ int backupPriority = m_NumRegularMods;
+ for (size_t i = 0; i < m_ModStatus.size(); ++i) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(static_cast<unsigned int>(i));
+ if (modInfo->isBackup()) {
+ m_ModStatus[i].m_Priority = backupPriority++;
+ }
+ }
+
updateIndices();
// User has a mod named some variation of "overwrite". Tell them about it.
@@ -519,17 +574,10 @@ void Profile::dumpModStatus() const
void Profile::updateIndices()
{
- m_NumRegularMods = 0;
m_ModIndexByPriority.clear();
for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
int priority = m_ModStatus[i].m_Priority;
- if (priority == INT_MIN) {
- // don't assign this to mapping at all, it's probably the overwrite mod
- continue;
- } else {
- ++m_NumRegularMods;
- m_ModIndexByPriority[priority] = i;
- }
+ m_ModIndexByPriority[priority] = i;
}
}
@@ -537,30 +585,16 @@ void Profile::updateIndices()
std::vector<std::tuple<QString, QString, int> > Profile::getActiveMods()
{
std::vector<std::tuple<QString, QString, int> > result;
- for (std::map<int, unsigned int>::const_iterator iter = m_ModIndexByPriority.begin(); iter != m_ModIndexByPriority.end(); iter++ ) {
- if ((iter->second != UINT_MAX) && m_ModStatus[iter->second].m_Enabled) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second);
- if (modInfo->hasFlag(ModInfo::FLAG_OVERWRITE))
- result.push_back(std::make_tuple(modInfo->internalName(), modInfo->absolutePath(), INT_MAX));
- else
- result.push_back(std::make_tuple(modInfo->internalName(), modInfo->absolutePath(), m_ModStatus[iter->second].m_Priority));
+ for (const auto& [priority, index] : m_ModIndexByPriority) {
+ if (m_ModStatus[index].m_Enabled) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
+ result.push_back(std::make_tuple(modInfo->internalName(), modInfo->absolutePath(), m_ModStatus[index].m_Priority));
}
}
return result;
}
-
-unsigned int Profile::modIndexByPriority(int priority) const
-{
- try {
- return m_ModIndexByPriority.at(priority);
- } catch (std::out_of_range) {
- throw MyException(tr("invalid priority %1").arg(priority));
- }
-}
-
-
void Profile::setModEnabled(unsigned int index, bool enabled)
{
if (index >= m_ModStatus.size()) {
@@ -568,11 +602,15 @@ void Profile::setModEnabled(unsigned int index, bool enabled)
}
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
+
// we could quit in the following case, this shouldn't be a change anyway,
// but at least this allows the situation to be fixed in case of an error
if (modInfo->alwaysEnabled()) {
enabled = true;
}
+ if (modInfo->alwaysDisabled()) {
+ enabled = false;
+ }
if (enabled != m_ModStatus[index].m_Enabled) {
m_ModStatus[index].m_Enabled = enabled;
@@ -580,7 +618,7 @@ void Profile::setModEnabled(unsigned int index, bool enabled)
}
}
-void Profile::setModsEnabled(const QList<unsigned int> &modsToEnable, const QList<unsigned int> &modsToDisable)
+void Profile::setModsEnabled(const QList<unsigned int>& modsToEnable, const QList<unsigned int>& modsToDisable)
{
QList<unsigned int> dirtyMods;
for (auto idx : modsToEnable) {
@@ -588,6 +626,9 @@ void Profile::setModsEnabled(const QList<unsigned int> &modsToEnable, const QLis
log::error("invalid mod index: {}", idx);
continue;
}
+ if (ModInfo::getByIndex(idx)->alwaysDisabled()) {
+ continue;
+ }
if (!m_ModStatus[idx].m_Enabled) {
m_ModStatus[idx].m_Enabled = true;
dirtyMods.append(idx);
@@ -631,30 +672,24 @@ int Profile::getModPriority(unsigned int index) const
}
-void Profile::setModPriority(unsigned int index, int &newPriority)
+bool Profile::setModPriority(unsigned int index, int &newPriority)
{
- if (m_ModStatus.at(index).m_Overwrite) {
- // can't change priority of the overwrite
- return;
+ if (ModInfo::getByIndex(index)->hasAutomaticPriority()) {
+ // can't change priority of overwrite/backups
+ return false;
}
+ newPriority = std::clamp(newPriority, 0, static_cast<int>(m_NumRegularMods) - 1);
+
int oldPriority = m_ModStatus.at(index).m_Priority;
int lastPriority = INT_MIN;
if (newPriority == oldPriority) {
// nothing to do
- return;
+ return false;
}
- // we need to put the mod before backups
- auto it = std::find_if(m_ModIndexByPriority.begin(), m_ModIndexByPriority.end(), [](auto&& p) {
- return ModInfo::getByIndex(p.second)->isBackup();
- });
- if (it != m_ModIndexByPriority.end() && it->first <= newPriority) {
- newPriority = it->first - 1;
- }
-
- for (auto& [priority, index] : m_ModIndexByPriority) {
+ for (const auto& [priority, index] : m_ModIndexByPriority) {
if (newPriority < oldPriority && priority >= newPriority && priority < oldPriority) {
m_ModStatus.at(index).m_Priority += 1;
}
@@ -669,6 +704,8 @@ void Profile::setModPriority(unsigned int index, int &newPriority)
updateIndices();
m_ModListWriter.write();
+
+ return true;
}
Profile *Profile::createPtrFrom(const QString &name, const Profile &reference, MOBase::IPluginGame const *gamePlugin)
@@ -1039,11 +1076,6 @@ void Profile::storeSettingsByArray(const QString &prefix, const QList<QVariantMa
m_Settings->endArray();
}
-int Profile::getPriorityMinimum() const
-{
- return m_ModIndexByPriority.begin()->first;
-}
-
bool Profile::forcedLibrariesEnabled(const QString &executable) const
{
return setting("forced_libraries", executable + "/enabled", false).toBool();
diff --git a/src/profile.h b/src/profile.h
index 04452ff6..672a2c13 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -52,7 +52,14 @@ class Profile : public QObject, public MOBase::IProfile
public:
- typedef boost::shared_ptr<Profile> Ptr;
+ using Ptr = boost::shared_ptr<Profile>;
+
+public:
+
+ // the minimum and maximum priority achievable by mods
+ //
+ static constexpr int MinimumPriority = 0;
+ static constexpr int MaximumPriority = std::numeric_limits<int>::max();
public:
@@ -243,7 +250,7 @@ public:
*
* @return list of active mods sorted by priority (ascending). "first" is the mod name, "second" is its path
**/
- std::vector<std::tuple<QString, QString, int> > getActiveMods();
+ std::vector<std::tuple<QString, QString, int>> getActiveMods();
/**
* @brief retrieve a mod of the indexes ordered by priority
@@ -262,20 +269,6 @@ public:
size_t numMods() const { return m_ModStatus.size(); }
/**
- * @return the number of mods that can be enabled and where the priority can be modified
- */
- unsigned int numRegularMods() const { return m_NumRegularMods; }
-
- /**
- * @brief retrieve the mod index based on the priority
- *
- * @param priority priority to look up
- * @return the index of the mod
- * @throw std::exception an exception is thrown if there is no mod with the specified priority
- **/
- unsigned int modIndexByPriority(int priority) const;
-
- /**
* @brief enable or disable a mod
*
* @param index index of the mod to enable/disable
@@ -293,17 +286,18 @@ public:
**/
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.
- *
- * @param index index of the mod to change
- * @param newPriority the new priority value
- *
- * @todo what happens if the new priority is outside the range?
- **/
- void setModPriority(unsigned int index, int &newPriority);
+ // set the priority of a mod, and the priority of other mods in the range
+ // [old priority, new priority] such that no gaps are possible
+ //
+ // the priority is clamped in the range of valid priority (>= 0, and lower than
+ // the number of "regular" mods), you should use MinimumPriority or MaximumPriority
+ // to send a mod to the "top" or "bottom" of the priority list
+ //
+ // the function returns true if the priority was changed, or false if the mod
+ // was already at the given priority (or if the priority of the mod cannot be
+ // set)
+ //
+ bool setModPriority(unsigned int index, int& newPriority);
/**
* @brief determine if a mod is enabled
@@ -338,8 +332,6 @@ public:
QList<QVariantMap> settingsByArray(const QString &prefix) const;
void storeSettingsByArray(const QString &prefix, const QList<QVariantMap> &values);
- int getPriorityMinimum() const;
-
bool forcedLibrariesEnabled(const QString &executable) const;
void setForcedLibrariesEnabled(const QString &executable, bool enabled);
QList<MOBase::ExecutableForcedLoadSetting> determineForcedLibraries(const QString &executable) const;
@@ -348,6 +340,9 @@ public:
void debugDump() const;
+
+ Profile& operator=(const Profile& reference) = delete;
+
signals:
/**
@@ -364,7 +359,7 @@ signals:
**/
void modStatusChanged(QList<unsigned int> index);
-public slots:
+protected slots:
// should only be called by DelayedFileWriter, use writeModlist() and writeModlistNow() instead
void doWriteModlist();
@@ -374,17 +369,13 @@ private:
class ModStatus {
friend class Profile;
public:
- ModStatus() : m_Overwrite(false), m_Enabled(false), m_Priority(-1) {}
+ ModStatus() : m_Enabled(false), m_Priority(-1) {}
private:
- bool m_Overwrite;
bool m_Enabled;
int m_Priority;
};
private:
- Profile& operator=(const Profile &reference); // not implemented
-
- void initTimer();
void updateIndices();
@@ -394,7 +385,6 @@ private:
void mergeTweak(const QString &tweakName, const QString &tweakedIni) const;
void mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const;
void touchFile(QString fileName);
- void finishChangeStatus() const;
static void renameModInList(QFile &modList, const QString &oldName, const QString &newName);
@@ -406,11 +396,14 @@ private:
const MOBase::IPluginGame *m_GamePlugin;
- mutable QByteArray m_LastModlistHash;
std::vector<ModStatus> m_ModStatus;
std::map<int, unsigned int> m_ModIndexByPriority;
- unsigned int m_NumRegularMods;
+ // "regular" here means mods with modifiable priority (i.e. not backups
+ // or overwrite)
+ std::size_t m_NumRegularMods;
+
+ mutable QByteArray m_LastModlistHash;
MOBase::DelayedFileWriter m_ModListWriter;
};
diff --git a/src/settingsdialogplugins.cpp b/src/settingsdialogplugins.cpp
index 16f6fb0a..2f7f3985 100644
--- a/src/settingsdialogplugins.cpp
+++ b/src/settingsdialogplugins.cpp
@@ -40,18 +40,18 @@ PluginsSettingsTab::PluginsSettingsTab(Settings& s, PluginContainer* pluginConta
QTreeWidgetItem* listItem = new QTreeWidgetItem(
topItems.at(m_pluginContainer->topImplementedInterface(plugin)));
listItem->setData(0, Qt::DisplayRole, plugin->localizedName());
- listItem->setData(0, ROLE_PLUGIN, QVariant::fromValue((void*)plugin));
- listItem->setData(0, ROLE_SETTINGS, settings().plugins().settings(plugin->name()));
- listItem->setData(0, ROLE_DESCRIPTIONS, settings().plugins().descriptions(plugin->name()));
+ listItem->setData(0, PluginRole, QVariant::fromValue((void*)plugin));
+ listItem->setData(0, SettingsRole, settings().plugins().settings(plugin->name()));
+ listItem->setData(0, DescriptionsRole, settings().plugins().descriptions(plugin->name()));
// Handle child item:
auto children = m_pluginContainer->requirements(plugin).children();
for (auto* child : children) {
QTreeWidgetItem* childItem = new QTreeWidgetItem(listItem);
childItem->setData(0, Qt::DisplayRole, child->localizedName());
- childItem->setData(0, ROLE_PLUGIN, QVariant::fromValue((void*)child));
- childItem->setData(0, ROLE_SETTINGS, settings().plugins().settings(child->name()));
- childItem->setData(0, ROLE_DESCRIPTIONS, settings().plugins().descriptions(child->name()));
+ childItem->setData(0, PluginRole, QVariant::fromValue((void*)child));
+ childItem->setData(0, SettingsRole, settings().plugins().settings(child->name()));
+ childItem->setData(0, DescriptionsRole, settings().plugins().descriptions(child->name()));
handledNames.insert(child->name());
}
@@ -174,7 +174,7 @@ void PluginsSettingsTab::filterPluginList()
IPlugin* PluginsSettingsTab::plugin(QTreeWidgetItem* pluginItem) const
{
- return static_cast<IPlugin*>(qvariant_cast<void*>(pluginItem->data(0, ROLE_PLUGIN)));
+ return static_cast<IPlugin*>(qvariant_cast<void*>(pluginItem->data(0, PluginRole)));
}
void PluginsSettingsTab::update()
@@ -185,7 +185,7 @@ void PluginsSettingsTab::update()
for (int j = 0; j < topLevelItem->childCount(); ++j) {
auto* item = topLevelItem->child(j);
settings().plugins().setSettings(
- plugin(item)->name(), item->data(0, ROLE_SETTINGS).toMap());
+ plugin(item)->name(), item->data(0, SettingsRole).toMap());
}
}
@@ -209,7 +209,7 @@ void PluginsSettingsTab::on_checkboxEnabled_clicked(bool checked)
{
// Retrieve the plugin:
auto *item = ui->pluginsList->currentItem();
- if (!item || !item->data(0, ROLE_PLUGIN).isValid()) {
+ if (!item || !item->data(0, PluginRole).isValid()) {
return;
}
IPlugin* plugin = this->plugin(item);
@@ -278,7 +278,7 @@ void PluginsSettingsTab::on_pluginsList_currentItemChanged(QTreeWidgetItem *curr
{
storeSettings(previous);
- if (!current->data(0, ROLE_PLUGIN).isValid()) {
+ if (!current->data(0, PluginRole).isValid()) {
return;
}
@@ -325,8 +325,8 @@ void PluginsSettingsTab::on_pluginsList_currentItemChanged(QTreeWidgetItem *curr
}
}
- QVariantMap settings = current->data(0, ROLE_SETTINGS).toMap();
- QVariantMap descriptions = current->data(0, ROLE_DESCRIPTIONS).toMap();
+ QVariantMap settings = current->data(0, SettingsRole).toMap();
+ QVariantMap descriptions = current->data(0, DescriptionsRole).toMap();
ui->pluginSettingsList->setEnabled(settings.count() != 0);
for (auto iter = settings.begin(); iter != settings.end(); ++iter) {
QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(iter.key()));
@@ -359,14 +359,14 @@ void PluginsSettingsTab::deleteBlacklistItem()
void PluginsSettingsTab::storeSettings(QTreeWidgetItem *pluginItem)
{
- if (pluginItem != nullptr && pluginItem->data(0, ROLE_PLUGIN).isValid()) {
- QVariantMap settings = pluginItem->data(0, ROLE_SETTINGS).toMap();
+ if (pluginItem != nullptr && pluginItem->data(0, PluginRole).isValid()) {
+ QVariantMap settings = pluginItem->data(0, SettingsRole).toMap();
for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) {
const QTreeWidgetItem *item = ui->pluginSettingsList->topLevelItem(i);
settings[item->text(0)] = item->data(1, Qt::DisplayRole);
}
- pluginItem->setData(0, ROLE_SETTINGS, settings);
+ pluginItem->setData(0, SettingsRole, settings);
}
}
diff --git a/src/settingsdialogplugins.h b/src/settingsdialogplugins.h
index 3de0d7dc..c784b83e 100644
--- a/src/settingsdialogplugins.h
+++ b/src/settingsdialogplugins.h
@@ -39,9 +39,11 @@ private slots:
*/
MOBase::IPlugin* plugin(QTreeWidgetItem *pluginItem) const;
- constexpr static int ROLE_PLUGIN = Qt::UserRole;
- constexpr static int ROLE_SETTINGS = Qt::UserRole + 1;
- constexpr static int ROLE_DESCRIPTIONS = Qt::UserRole + 2;
+ enum {
+ PluginRole = Qt::UserRole,
+ SettingsRole = Qt::UserRole + 1,
+ DescriptionsRole = Qt::UserRole + 2
+ };
private:
diff --git a/src/shared/filesorigin.cpp b/src/shared/filesorigin.cpp
index 7476b6c5..049e4258 100644
--- a/src/shared/filesorigin.cpp
+++ b/src/shared/filesorigin.cpp
@@ -33,8 +33,6 @@ FilesOrigin::FilesOrigin(
void FilesOrigin::setPriority(int priority)
{
- m_OriginConnection.lock()->changePriorityLookup(m_Priority, priority);
-
m_Priority = priority;
}
diff --git a/src/shared/filesorigin.h b/src/shared/filesorigin.h
index 222a834f..1aa4c645 100644
--- a/src/shared/filesorigin.h
+++ b/src/shared/filesorigin.h
@@ -22,8 +22,8 @@ public:
FilesOrigin(const FilesOrigin&) = delete;
FilesOrigin& operator=(const FilesOrigin&) = delete;
- // sets priority for this origin, but it will overwrite the existing mapping
- // for this priority, the previous origin will no longer be referenced
+ // sets priority for this origin (does not automatically refresh
+ // the structure)
void setPriority(int priority);
int getPriority() const
diff --git a/src/shared/originconnection.cpp b/src/shared/originconnection.cpp
index c1c096f0..2830e8e3 100644
--- a/src/shared/originconnection.cpp
+++ b/src/shared/originconnection.cpp
@@ -88,19 +88,6 @@ FilesOrigin& OriginConnection::getByName(const std::wstring &name)
}
}
-void OriginConnection::changePriorityLookup(int oldPriority, int newPriority)
-{
- std::scoped_lock lock(m_Mutex);
-
- auto iter = m_OriginsPriorityMap.find(oldPriority);
-
- if (iter != m_OriginsPriorityMap.end()) {
- OriginID idx = iter->second;
- m_OriginsPriorityMap.erase(iter);
- m_OriginsPriorityMap[newPriority] = idx;
- }
-}
-
void OriginConnection::changeNameLookup(const std::wstring &oldName, const std::wstring &newName)
{
std::scoped_lock lock(m_Mutex);
@@ -137,7 +124,6 @@ FilesOrigin& OriginConnection::createOriginNoLock(
.first;
m_OriginsNameMap.insert({originName, newID});
- m_OriginsPriorityMap.insert({priority, newID});
return itor->second;
}
diff --git a/src/shared/originconnection.h b/src/shared/originconnection.h
index 5fecd63a..ef8692bb 100644
--- a/src/shared/originconnection.h
+++ b/src/shared/originconnection.h
@@ -40,7 +40,6 @@ private:
std::atomic<OriginID> m_NextID;
std::map<OriginID, FilesOrigin> m_Origins;
std::map<std::wstring, OriginID> m_OriginsNameMap;
- std::map<int, OriginID> m_OriginsPriorityMap;
mutable std::mutex m_Mutex;
OriginID createID();