summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikaël Capelle <capelle.mikael@gmail.com>2020-12-29 19:36:17 +0100
committerMikaël Capelle <capelle.mikael@gmail.com>2021-01-02 15:38:16 +0100
commit7e2c52133960b7d1bbb0fe72a37cee1c2b67c0ec (patch)
tree686e359ad1576ddc7bff6d4177029dd1a760c5fd
parent68c9ae8592a51b87dc3228b8ebd13737bf2dc7f5 (diff)
Refactoring and fixes.
- Move codes from MainWindow to ModListView. - Fix enable/disable all visible. - Fix changing style of the mod list.
-rw-r--r--src/mainwindow.cpp465
-rw-r--r--src/mainwindow.h14
-rw-r--r--src/modlistsortproxy.cpp26
-rw-r--r--src/modlistsortproxy.h11
-rw-r--r--src/modlistview.cpp519
-rw-r--r--src/modlistview.h119
6 files changed, 679 insertions, 475 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 39ecfa95..99c5293d 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -47,6 +47,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "editexecutablesdialog.h"
#include "categories.h"
#include "categoriesdialog.h"
+#include "genericicondelegate.h"
#include "modinfodialog.h"
#include "overwriteinfodialog.h"
#include "downloadlist.h"
@@ -56,9 +57,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "motddialog.h"
#include "filedialogmemory.h"
#include "tutorialmanager.h"
-#include "modflagicondelegate.h"
-#include "modconflicticondelegate.h"
-#include "genericicondelegate.h"
#include "selectiondialog.h"
#include "csvbuilder.h"
#include "savetextasdialog.h"
@@ -255,7 +253,6 @@ MainWindow::MainWindow(Settings &settings
, m_linksSeparator(nullptr)
, m_Tutorial(this, "MainWindow")
, m_OldProfileIndex(-1)
- , m_ModListSortProxy(nullptr)
, m_OldExecutableIndex(-1)
, m_CategoryFactory(CategoryFactory::instance())
, m_ContextItem(nullptr)
@@ -484,6 +481,7 @@ MainWindow::MainWindow(Settings &settings
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Enter), this, SLOT(openExplorer_activated()));
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return), this, SLOT(openExplorer_activated()));
+ new QShortcut(QKeySequence::Refresh, this, SLOT(refreshProfile_activated()));
setFilterShortcuts(ui->modList, ui->modFilterEdit);
setFilterShortcuts(ui->espList, ui->espFilterEdit);
@@ -536,134 +534,19 @@ MainWindow::MainWindow(Settings &settings
updatePinnedExecutables();
resetActionIcons();
updatePluginCount();
- updateModCount();
processUpdates();
+ ui->modList->updateModCount();
ui->statusBar->updateNormalMessage(m_OrganizerCore);
}
-void MainWindow::updateModListByPriorityProxy()
-{
- if (ui->groupCombo->currentIndex() != 0) {
- return;
- }
- if (m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY && m_ModListSortProxy->sortOrder() == Qt::AscendingOrder) {
- m_ModListSortProxy->setSourceModel(m_ModListByPriorityProxy);
- m_ModListByPriorityProxy->refresh();
- }
- else {
- m_ModListSortProxy->setSourceModel(m_OrganizerCore.modList());
- }
-}
-
void MainWindow::setupModList()
{
- m_ModListByPriorityProxy = new ModListByPriorityProxy(m_OrganizerCore.currentProfile(), &m_OrganizerCore);
- connect(ui->modList, SIGNAL(expanded(QModelIndex)), m_ModListByPriorityProxy, SLOT(expanded(QModelIndex)));
- connect(ui->modList, SIGNAL(collapsed(QModelIndex)), m_ModListByPriorityProxy, SLOT(collapsed(QModelIndex)));
- connect(m_ModListByPriorityProxy, SIGNAL(expandItem(QModelIndex)), this, SLOT(expandModList(QModelIndex)));
-
- m_ModListSortProxy = new ModListSortProxy(m_OrganizerCore.currentProfile(), &m_OrganizerCore);
- ui->modList->setModel(m_ModListSortProxy);
-
- m_ModListByPriorityProxy->setSourceModel(m_OrganizerCore.modList());
-
- connect(m_ModListSortProxy, &QAbstractItemModel::layoutAboutToBeChanged,
- this, [this](const QList<QPersistentModelIndex>& parents, QAbstractItemModel::LayoutChangeHint hint) {
- if (hint == QAbstractItemModel::VerticalSortHint) {
- updateModListByPriorityProxy();
- }
- });
-
- ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder);
-
- connect(ui->modList, &ModListView::dragEntered, m_OrganizerCore.modList(), &ModList::onDragEnter);
- connect(ui->modList, &ModListView::dropEntered, m_ModListByPriorityProxy, &ModListByPriorityProxy::onDropEnter);
- connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, this, &MainWindow::onModPrioritiesChanged);
-
- connect(
- ui->modList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
- this, SLOT(modListSortIndicatorChanged(int,Qt::SortOrder)));
-
- connect(
- ui->modList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
- this, SLOT(modlistSelectionsChanged(QItemSelection)));
-
- connect(
- ui->modList->header(), SIGNAL(sectionResized(int, int, int)),
- this, SLOT(modListSectionResized(int, int, int)));
-
-
- GenericIconDelegate *contentDelegate = new GenericIconDelegate(
- ui->modList, Qt::UserRole + 3, ModList::COL_CONTENT, 150);
-
- connect(
- ui->modList->header(), SIGNAL(sectionResized(int,int,int)),
- contentDelegate, SLOT(columnResized(int,int,int)));
-
-
- ModFlagIconDelegate *flagDelegate = new ModFlagIconDelegate(
- ui->modList, ModList::COL_FLAGS, 120);
-
- connect(
- ui->modList->header(), SIGNAL(sectionResized(int,int,int)),
- flagDelegate, SLOT(columnResized(int,int,int)));
-
-
- ModConflictIconDelegate* conflictFlagDelegate = new ModConflictIconDelegate(
- ui->modList, ModList::COL_CONFLICTFLAGS, 80);
-
- connect(
- ui->modList->header(), SIGNAL(sectionResized(int, int, int)),
- conflictFlagDelegate, SLOT(columnResized(int, int, int)));
-
-
- ui->modList->setItemDelegateForColumn(ModList::COL_FLAGS, flagDelegate);
- ui->modList->setItemDelegateForColumn(ModList::COL_CONFLICTFLAGS, conflictFlagDelegate);
- ui->modList->setItemDelegateForColumn(ModList::COL_CONTENT, contentDelegate);
- ui->modList->header()->installEventFilter(m_OrganizerCore.modList());
-
-
- if (m_OrganizerCore.settings().geometry().restoreState(ui->modList->header())) {
- // hack: force the resize-signal to be triggered because restoreState doesn't seem to do that
- for (int column = 0; column <= ModList::COL_LASTCOLUMN; ++column) {
- int sectionSize = ui->modList->header()->sectionSize(column);
- ui->modList->header()->resizeSection(column, sectionSize + 1);
- ui->modList->header()->resizeSection(column, sectionSize);
- }
- } else {
- // hide these columns by default
- ui->modList->header()->setSectionHidden(ModList::COL_CONTENT, true);
- ui->modList->header()->setSectionHidden(ModList::COL_MODID, true);
- ui->modList->header()->setSectionHidden(ModList::COL_GAME, true);
- ui->modList->header()->setSectionHidden(ModList::COL_INSTALLTIME, true);
- ui->modList->header()->setSectionHidden(ModList::COL_NOTES, true);
-
- // resize mod list to fit content
- for (int i = 0; i < ui->modList->header()->count(); ++i) {
- ui->modList->header()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
- }
-
- ui->modList->header()->setSectionResizeMode(ModList::COL_NAME, QHeaderView::Stretch);
- }
-
- // prevent the name-column from being hidden
- ui->modList->header()->setSectionHidden(ModList::COL_NAME, false);
-
- ui->modList->installEventFilter(m_OrganizerCore.modList());
-
- connect(m_OrganizerCore.modList(), &ModList::downloadArchiveDropped, this, [this](int row, int priority) {
- m_OrganizerCore.installDownload(row, priority);
- });
+ ui->modList->setup(m_OrganizerCore, ui);
- connect(m_ModListSortProxy, &ModListSortProxy::filterActive, this, &MainWindow::modFilterActive);
- connect(ui->modFilterEdit, &QLineEdit::textChanged, m_ModListSortProxy, &ModListSortProxy::updateFilter);
- connect(m_ModListSortProxy, &QAbstractItemModel::layoutChanged, this, &MainWindow::updateModCount);
- connect(m_ModListSortProxy, &QAbstractItemModel::layoutChanged, this, [&]() {
- if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy) {
- m_ModListByPriorityProxy->refreshExpandedItems();
- }
- });
+ // keep here for now
+ connect(ui->modList->selectionModel(), &QItemSelectionModel::selectionChanged,
+ this, &MainWindow::modlistSelectionsChanged);
}
void MainWindow::resetActionIcons()
@@ -733,7 +616,6 @@ void MainWindow::resetActionIcons()
updateProblemsButton();
}
-
MainWindow::~MainWindow()
{
try {
@@ -814,6 +696,7 @@ void MainWindow::allowListResize()
void MainWindow::updateStyle(const QString&)
{
resetActionIcons();
+ ui->modList->refreshStyle();
}
void MainWindow::resizeEvent(QResizeEvent *event)
@@ -1273,22 +1156,6 @@ void MainWindow::createHelpMenu()
menu->addAction(tr("About Qt"), qApp, SLOT(aboutQt()));
}
-void MainWindow::modFilterActive(bool filterActive)
-{
- ui->clearFiltersButton->setVisible(filterActive);
- if (filterActive) {
-// m_OrganizerCore.modList()->setOverwriteMarkers(std::set<unsigned int>(), std::set<unsigned int>());
- ui->modList->setStyleSheet("QTreeView { border: 2px ridge #f00; }");
- ui->activeModsCounter->setStyleSheet("QLCDNumber { border: 2px ridge #f00; }");
- } else if (ui->groupCombo->currentIndex() != 0) {
- ui->modList->setStyleSheet("QTreeView { border: 2px ridge #337733; }");
- ui->activeModsCounter->setStyleSheet("");
- } else {
- ui->modList->setStyleSheet("");
- ui->activeModsCounter->setStyleSheet("");
- }
-}
-
void MainWindow::espFilterChanged(const QString &filter)
{
if (!filter.isEmpty()) {
@@ -1301,13 +1168,6 @@ void MainWindow::espFilterChanged(const QString &filter)
updatePluginCount();
}
-void MainWindow::expandModList(const QModelIndex &index)
-{
- if (index.model() == m_ModListSortProxy->sourceModel()) {
- ui->modList->expand(m_ModListSortProxy->mapFromSource(index));
- }
-}
-
bool MainWindow::addProfile()
{
QComboBox *profileBox = findChild<QComboBox*>("profileBox");
@@ -1706,13 +1566,11 @@ void MainWindow::startExeAction()
void MainWindow::activateSelectedProfile()
{
m_OrganizerCore.setCurrentProfile(ui->profileBox->currentText());
-
- m_ModListSortProxy->setProfile(m_OrganizerCore.currentProfile());
- m_ModListByPriorityProxy->setProfile(m_OrganizerCore.currentProfile());
+ ui->modList->setProfile(m_OrganizerCore.currentProfile());
m_SavesTab->refreshSaveList();
m_OrganizerCore.refresh();
- updateModCount();
+ ui->modList->updateModCount();
updatePluginCount();
ui->statusBar->updateNormalMessage(m_OrganizerCore);
}
@@ -1746,22 +1604,9 @@ void MainWindow::on_profileBox_currentIndexChanged(int index)
if (ui->profileBox->currentIndex() == 0) {
ui->profileBox->setCurrentIndex(previousIndex);
-
- std::optional<QString> newSelection;
-
- ProfilesDialog dlg(ui->profileBox->currentText(), m_OrganizerCore, this);
- dlg.exec();
- newSelection = dlg.selectedProfile();
-
+ ProfilesDialog(ui->profileBox->currentText(), m_OrganizerCore, this).exec();
while (!refreshProfiles()) {
- ProfilesDialog dlg(ui->profileBox->currentText(), m_OrganizerCore, this);
- dlg.exec();
- newSelection = dlg.selectedProfile();
- }
-
- if (newSelection) {
- ui->profileBox->setCurrentText(*newSelection);
- activateSelectedProfile();
+ ProfilesDialog(ui->profileBox->currentText(), m_OrganizerCore, this).exec();
}
} else {
activateSelectedProfile();
@@ -2339,11 +2184,6 @@ void MainWindow::on_actionInstallMod_triggered()
installMod();
}
-void MainWindow::on_action_Refresh_triggered()
-{
- refreshProfile_activated();
-}
-
void MainWindow::on_actionAdd_Profile_triggered()
{
for (;;) {
@@ -2447,16 +2287,6 @@ void MainWindow::esplist_changed()
void MainWindow::onModPrioritiesChanged(std::vector<int> const& indices)
{
- // expand separator whose priority has changed
- if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy) {
- for (auto index : indices) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
- if (modInfo->isSeparator()) {
- ui->modList->expand(m_ModListSortProxy->mapFromSource(m_ModListByPriorityProxy->mapFromSource(m_OrganizerCore.modList()->index(index, 0))));
- }
- }
- }
-
for (unsigned int i = 0; i < m_OrganizerCore.currentProfile()->numMods(); ++i) {
int priority = m_OrganizerCore.currentProfile()->getModPriority(i);
if (m_OrganizerCore.currentProfile()->modEnabled(i)) {
@@ -2498,9 +2328,6 @@ void MainWindow::onModPrioritiesChanged(std::vector<int> const& indices)
m_OrganizerCore.modList()->setOverwriteMarkers(modInfo->getModOverwrite(), modInfo->getModOverwritten());
m_OrganizerCore.modList()->setArchiveOverwriteMarkers(modInfo->getModArchiveOverwrite(), modInfo->getModArchiveOverwritten());
m_OrganizerCore.modList()->setArchiveLooseOverwriteMarkers(modInfo->getModArchiveLooseOverwrite(), modInfo->getModArchiveLooseOverwritten());
- if (m_ModListSortProxy != nullptr) {
- m_ModListSortProxy->invalidate();
- }
ui->modList->verticalScrollBar()->repaint();
}
}
@@ -2514,25 +2341,11 @@ void MainWindow::modInstalled(const QString &modName)
return;
}
- QModelIndex qIndex = m_OrganizerCore.modList()->index(index, 0);
-
- if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy) {
- qIndex = m_ModListByPriorityProxy->mapFromSource(qIndex);
- ui->modList->expand(m_ModListSortProxy->mapFromSource(qIndex));
- }
-
- qIndex = m_ModListSortProxy->mapFromSource(qIndex);
-
// force an update to happen
std::multimap<QString, int> IDs;
ModInfo::Ptr info = ModInfo::getByIndex(index);
IDs.insert(std::make_pair<QString, int>(info->gameName(), info->nexusId()));
modUpdateCheck(IDs);
-
- ui->modList->setFocus(Qt::OtherFocusReason);
- ui->modList->scrollTo(qIndex);
- // ui->modList->setCurrentIndex(qIndex);
- ui->modList->selectionModel()->select(qIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
}
void MainWindow::showMessage(const QString &message)
@@ -2626,9 +2439,7 @@ void MainWindow::restoreBackup_clicked()
if (!modDir.rename(modInfo->absolutePath(), destinationPath)) {
reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath));
}
-
m_OrganizerCore.refresh();
- updateModCount();
}
}
}
@@ -2637,13 +2448,13 @@ void MainWindow::restoreBackup_clicked()
void MainWindow::modlistChanged(const QModelIndex&, int)
{
m_OrganizerCore.currentProfile()->writeModlist();
- updateModCount();
+ ui->modList->updateModCount();
}
void MainWindow::modlistChanged(const QModelIndexList&, int)
{
m_OrganizerCore.currentProfile()->writeModlist();
- updateModCount();
+ ui->modList->updateModCount();
}
void MainWindow::modlistSelectionsChanged(const QItemSelection &selected)
@@ -2672,17 +2483,6 @@ void MainWindow::esplistSelectionsChanged(const QItemSelection &selected)
ui->modList->verticalScrollBar()->repaint();
}
-void MainWindow::modListSortIndicatorChanged(int, Qt::SortOrder)
-{
- ui->modList->verticalScrollBar()->repaint();
-}
-
-void MainWindow::modListSectionResized(int logicalIndex, int oldSize, int newSize)
-{
- bool enabled = (newSize != 0);
- qobject_cast<ModListSortProxy *>(ui->modList->model())->setColumnVisible(logicalIndex, enabled);
-}
-
void MainWindow::removeMod_clicked()
{
const int max_items = 20;
@@ -2727,7 +2527,7 @@ void MainWindow::removeMod_clicked()
} else {
m_OrganizerCore.modList()->removeRow(m_ContextRow, QModelIndex());
}
- updateModCount();
+ ui->modList->updateModCount();
updatePluginCount();
} catch (const std::exception &e) {
reportError(tr("failed to remove mod: %1").arg(e.what()));
@@ -2778,9 +2578,7 @@ void MainWindow::backupMod_clicked()
QMessageBox::information(this, tr("Failed"),
tr("Failed to create backup."));
}
-
m_OrganizerCore.refresh();
- updateModCount();
}
@@ -2964,72 +2762,22 @@ void MainWindow::setWindowEnabled(bool enabled)
setEnabled(enabled);
}
-
ModInfo::Ptr MainWindow::nextModInList()
{
- const QModelIndex start = m_ModListSortProxy->mapFromSource(
- m_OrganizerCore.modList()->index(m_ContextRow, 0));
-
- auto index = start;
-
- for (;;) {
- index = m_ModListSortProxy->index((index.row() + 1) % m_ModListSortProxy->rowCount(), 0);
- m_ContextRow = m_ModListSortProxy->mapToSource(index).row();
-
- if (index == start || !index.isValid()) {
- // wrapped around, give up
- break;
- }
-
- ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow);
-
- // skip overwrite and backups and separators
- if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) ||
- mod->hasFlag(ModInfo::FLAG_BACKUP) ||
- mod->hasFlag(ModInfo::FLAG_SEPARATOR)) {
- continue;
- }
-
- return mod;
+ int index = ui->modList->nextMod(m_ContextRow);
+ if (index == -1) {
+ return {};
}
-
- return {};
+ return ModInfo::getByIndex(index);
}
ModInfo::Ptr MainWindow::previousModInList()
{
- const QModelIndex start = m_ModListSortProxy->mapFromSource(
- m_OrganizerCore.modList()->index(m_ContextRow, 0));
-
- auto index = start;
-
- for (;;) {
- int row = index.row() - 1;
- if (row == -1) {
- row = m_ModListSortProxy->rowCount() - 1;
- }
-
- index = m_ModListSortProxy->index(row, 0);
- m_ContextRow = m_ModListSortProxy->mapToSource(index).row();
-
- if (index == start || !index.isValid()) {
- // wrapped around, give up
- break;
- }
-
- // skip overwrite and backups and separators
- ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow);
-
- if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) ||
- mod->hasFlag(ModInfo::FLAG_BACKUP) ||
- mod->hasFlag(ModInfo::FLAG_SEPARATOR)) {
- continue;
- }
-
- return mod;
+ int index = ui->modList->prevMod(m_ContextRow);
+ if (index == -1) {
+ return {};
}
-
- return {};
+ return ModInfo::getByIndex(index);
}
void MainWindow::displayModInformation(const QString &modName, ModInfoTabIDs tabID)
@@ -3396,89 +3144,6 @@ void MainWindow::refreshProfile_activated()
m_OrganizerCore.profileRefresh();
}
-void MainWindow::updateModCount()
-{
- TimeThis tt("updateModCount");
-
- 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_OrganizerCore.modList()->allMods();
-
- 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_OrganizerCore.currentProfile()->modEnabled(modIndex);
- isVisible = m_ModListSortProxy->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;
- }
- }
-
- 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 (isVisible)
- visRegularCount++;
- regularCount++;
- }
- }
-
- ui->activeModsCounter->display(visActiveCount);
- ui->activeModsCounter->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>"
- "<tr><td>Unmanaged/DLCs:&emsp;</td><td align=right>%5</td><td align=right>%6</td></tr>"
- "<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)
- );
-}
-
void MainWindow::updatePluginCount()
{
int activeMasterCount = 0;
@@ -3561,7 +3226,7 @@ void MainWindow::createEmptyMod_clicked()
}
int newPriority = -1;
- if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY) {
+ if (m_ContextRow >= 0 && ui->modList->sortColumn() == ModList::COL_PRIORITY) {
newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow);
}
@@ -3602,7 +3267,7 @@ void MainWindow::createSeparator_clicked()
}
int newPriority = -1;
- if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY)
+ if (m_ContextRow >= 0 && ui->modList->sortColumn() == ModList::COL_PRIORITY)
{
newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow);
}
@@ -3845,7 +3510,7 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index)
reportError(e.what());
}
}
- else if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy && modInfo->isSeparator()) {
+ else if (ui->modList->hasCollapsibleSeparators() && modInfo->isSeparator()) {
ui->modList->setExpanded(index, !ui->modList->isExpanded(index));
}
else {
@@ -4176,7 +3841,7 @@ void MainWindow::checkModsForUpdates()
}
if (updatesAvailable || checkingModsForUpdate) {
- m_ModListSortProxy->setCriteria({{
+ ui->modList->setFilterCriteria({{
ModListSortProxy::TypeSpecial,
CategoryFactory::UpdateAvailable,
false}
@@ -4231,8 +3896,7 @@ void MainWindow::ignoreUpdate() {
ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow);
info->ignoreUpdate(true);
}
- if (m_ModListSortProxy != nullptr)
- m_ModListSortProxy->invalidate();
+ ui->modList->invalidate();
}
void MainWindow::checkModUpdates_clicked()
@@ -4264,8 +3928,7 @@ void MainWindow::unignoreUpdate()
ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow);
info->ignoreUpdate(false);
}
- if (m_ModListSortProxy != nullptr)
- m_ModListSortProxy->invalidate();
+ ui->modList->invalidate();
}
void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu,
@@ -4311,7 +3974,7 @@ void MainWindow::enableVisibleMods()
{
if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really enable all visible mods?"),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- m_ModListSortProxy->enableAllVisible();
+ ui->modList->enableAllVisible();
}
}
@@ -4319,7 +3982,7 @@ void MainWindow::disableVisibleMods()
{
if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really disable all visible mods?"),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- m_ModListSortProxy->disableAllVisible();
+ ui->modList->disableAllVisible();
}
}
@@ -4516,7 +4179,7 @@ void MainWindow::exportModListCSV()
if ((selectedRowID == 1) && !enabled) {
continue;
}
- else if ((selectedRowID == 2) && !m_ModListSortProxy->filterMatchesMod(info, enabled)) {
+ else if ((selectedRowID == 2) && !ui->modList->isModVisible(iter.second)) {
continue;
}
std::vector<ModInfo::EFlag> flags = info->getFlags();
@@ -4616,7 +4279,7 @@ void MainWindow::initModListContextMenu(QMenu *menu)
void MainWindow::addModSendToContextMenu(QMenu *menu)
{
- if (m_ModListSortProxy->sortColumn() != ModList::COL_PRIORITY)
+ if (ui->modList->sortColumn() != ModList::COL_PRIORITY)
return;
QMenu *sub_menu = new QMenu(menu);
@@ -4668,7 +4331,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
allMods->setTitle(tr("All Mods"));
menu.addMenu(allMods);
- if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy) {
+ if (ui->modList->hasCollapsibleSeparators()) {
menu.addAction(tr("Collapse all"), ui->modList, &QTreeView::collapseAll);
menu.addAction(tr("Expand all"), ui->modList, &QTreeView::expandAll);
}
@@ -5147,19 +4810,13 @@ void MainWindow::sendSelectedPluginsToPriority_clicked()
void MainWindow::enableSelectedMods_clicked()
{
- m_OrganizerCore.modList()->enableSelected(ui->modList->selectionModel());
- if (m_ModListSortProxy != nullptr) {
- m_ModListSortProxy->invalidate();
- }
+ ui->modList->enableSelected();
}
void MainWindow::disableSelectedMods_clicked()
{
- m_OrganizerCore.modList()->disableSelected(ui->modList->selectionModel());
- if (m_ModListSortProxy != nullptr) {
- m_ModListSortProxy->invalidate();
- }
+ ui->modList->disableSelected();
}
void MainWindow::updateAvailable()
@@ -5363,8 +5020,7 @@ void MainWindow::nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVa
return std::make_pair(gameNameReal, ModInfo::filteredMods(gameNameReal, resultList, userData.toBool(), true));
});
watcher->setFuture(future);
- if (m_ModListSortProxy != nullptr)
- m_ModListSortProxy->invalidate();
+ ui->modList->invalidate();
}
void MainWindow::finishUpdateInfo()
@@ -5467,8 +5123,7 @@ void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userD
if (foundUpdate) {
// Just get the standard data updates for endorsements and descriptions
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
- if (m_ModListSortProxy != nullptr)
- m_ModListSortProxy->invalidate();
+ ui->modList->invalidate();
} else {
// Scrape mod data here so we can use the mod version if no file update was located
requiresInfo = true;
@@ -5519,8 +5174,9 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC));
mod->saveMeta();
}
- if (foundUpdate && m_ModListSortProxy != nullptr)
- m_ModListSortProxy->invalidate();
+ if (foundUpdate) {
+ ui->modList->invalidate();
+ }
}
void MainWindow::nxmEndorsementToggled(QString, int, QVariant, QVariant resultData, int)
@@ -5829,7 +5485,7 @@ void MainWindow::refreshFilters()
void MainWindow::onFiltersCriteria(const std::vector<ModListSortProxy::Criteria>& criteria)
{
- m_ModListSortProxy->setCriteria(criteria);
+ ui->modList->setFilterCriteria(criteria);
QString label = "?";
@@ -5858,7 +5514,7 @@ void MainWindow::onFiltersCriteria(const std::vector<ModListSortProxy::Criteria>
void MainWindow::onFiltersOptions(
ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep)
{
- m_ModListSortProxy->setOptions(mode, sep);
+ ui->modList->setFilterOptions(mode, sep);
}
void MainWindow::updateESPLock(bool locked)
@@ -5987,41 +5643,6 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos)
}
}
-void MainWindow::on_groupCombo_currentIndexChanged(int index)
-{
- if (m_ModListSortProxy == nullptr) {
- return;
- }
- QAbstractProxyModel *newModel = nullptr;
- switch (index) {
- case 1: {
- newModel = new QtGroupingProxy(m_OrganizerCore.modList(), QModelIndex(), ModList::COL_CATEGORY, Qt::UserRole,
- 0, Qt::UserRole + 2);
- } break;
- case 2: {
- newModel = new QtGroupingProxy(m_OrganizerCore.modList(), QModelIndex(), ModList::COL_MODID, Qt::DisplayRole,
- QtGroupingProxy::FLAG_NOGROUPNAME | QtGroupingProxy::FLAG_NOSINGLE,
- Qt::UserRole + 2);
- } break;
- default: {
- newModel = nullptr;
- } break;
- }
-
- if (newModel != nullptr) {
-#ifdef TEST_MODELS
- new ModelTest(newModel, this);
-#endif // TEST_MODELS
- m_ModListSortProxy->setSourceModel(newModel);
- connect(ui->modList, SIGNAL(expanded(QModelIndex)),newModel, SLOT(expanded(QModelIndex)));
- connect(ui->modList, SIGNAL(collapsed(QModelIndex)), newModel, SLOT(collapsed(QModelIndex)));
- connect(newModel, SIGNAL(expandItem(QModelIndex)), this, SLOT(expandModList(QModelIndex)));
- } else {
- updateModListByPriorityProxy();
- }
- modFilterActive(m_ModListSortProxy->isFilterActive());
-}
-
Executable* MainWindow::getSelectedExecutable()
{
const QString name = ui->executablesListBox->itemText(
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 8dd72174..dbd44688 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -297,9 +297,6 @@ private:
QStringList m_DefaultArchives;
- ModListSortProxy *m_ModListSortProxy;
- ModListByPriorityProxy *m_ModListByPriorityProxy;
-
PluginListSortProxy *m_PluginListSortProxy;
int m_OldExecutableIndex;
@@ -521,12 +518,7 @@ private slots:
void modlistChanged(const QModelIndexList &indicies, int role);
void fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName);
-
- void modFilterActive(bool active);
void espFilterChanged(const QString &filter);
-
- void expandModList(const QModelIndex &index);
-
void resizeLists(bool pluginListCustom);
/**
@@ -545,14 +537,10 @@ private slots:
void about();
- void modListSortIndicatorChanged(int column, Qt::SortOrder order);
- void modListSectionResized(int logicalIndex, int oldSize, int newSize);
-
void modlistSelectionsChanged(const QItemSelection &current);
void esplistSelectionsChanged(const QItemSelection &current);
void resetActionIcons();
- void updateModCount();
void updatePluginCount();
private slots: // ui slots
@@ -591,7 +579,6 @@ private slots: // ui slots
void on_espList_customContextMenuRequested(const QPoint &pos);
void on_displayCategoriesBtn_toggled(bool checked);
- void on_groupCombo_currentIndexChanged(int index);
void on_linkButton_pressed();
void on_showHiddenBox_toggled(bool checked);
void on_bsaList_itemChanged(QTreeWidgetItem *item, int column);
@@ -609,7 +596,6 @@ private slots: // ui slots
void readSettings();
void setupModList();
- void updateModListByPriorityProxy();
};
#endif // MAINWINDOW_H
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index daa1478d..a7d0b27a 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -80,32 +80,6 @@ Qt::ItemFlags ModListSortProxy::flags(const QModelIndex &modelIndex) const
return flags;
}
-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();
- modsToEnable.append(modID);
- }
- m_Profile->setModsEnabled(modsToEnable, QList<unsigned int>());
- invalidate();
-}
-
-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();
- modsToDisable.append(modID);
- }
- m_Profile->setModsEnabled(QList<unsigned int>(), modsToDisable);
- invalidate();
-}
-
unsigned long ModListSortProxy::flagsId(const std::vector<ModInfo::EFlag> &flags) const
{
unsigned long result = 0;
diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h
index 811aec66..9a4140f6 100644
--- a/src/modlistsortproxy.h
+++ b/src/modlistsortproxy.h
@@ -84,17 +84,6 @@ public:
virtual void setSourceModel(QAbstractItemModel *sourceModel) override;
-
- /**
- * @brief enable all mods visible under the current filter
- **/
- void enableAllVisible();
-
- /**
- * @brief disable all mods visible under the current filter
- **/
- void disableAllVisible();
-
/**
* @brief tests if a filtere matches for a mod
* @param info mod information
diff --git a/src/modlistview.cpp b/src/modlistview.cpp
index 9192c01a..bccacb24 100644
--- a/src/modlistview.cpp
+++ b/src/modlistview.cpp
@@ -1,11 +1,20 @@
#include "modlistview.h"
-#include <widgetutility.h>
#include <QUrl>
#include <QMimeData>
#include <QProxyStyle>
+#include <widgetutility.h>
+
+#include "ui_mainwindow.h"
+
+#include "organizercore.h"
#include "modlist.h"
+#include "modlistsortproxy.h"
+#include "modlistbypriorityproxy.h"
#include "log.h"
+#include "modflagicondelegate.h"
+#include "modconflicticondelegate.h"
+#include "genericicondelegate.h"
class ModListProxyStyle : public QProxyStyle {
public:
@@ -63,10 +72,516 @@ ModListView::ModListView(QWidget* parent)
MOBase::setCustomizableColumns(this);
setAutoExpandDelay(1000);
- setStyle(new ModListProxyStyle(style()));
+ setStyle(new ModListProxyStyle());
setItemDelegate(new ModListStyledItemDelegated(this));
}
+void ModListView::refreshStyle()
+{
+ // maybe there is a better way but I did not find one
+ QString sheet = styleSheet();
+ setStyleSheet("QTreeView { }");
+ setStyleSheet(sheet);
+}
+
+void ModListView::setProfile(Profile* profile)
+{
+ m_sortProxy->setProfile(profile);
+ m_byPriorityProxy->setProfile(profile);
+}
+
+bool ModListView::hasCollapsibleSeparators() const
+{
+ return m_sortProxy != nullptr && m_sortProxy->sourceModel() == m_byPriorityProxy;
+}
+
+int ModListView::sortColumn() const
+{
+ return m_sortProxy ? m_sortProxy->sortColumn() : -1;
+}
+
+int ModListView::nextMod(int modIndex) const
+{
+ const QModelIndex start = indexModelToView(m_core->modList()->index(modIndex, 0));
+
+ auto index = start;
+
+ for (;;) {
+ index = model()->index((index.row() + 1) % model()->rowCount(), 0);
+ modIndex = indexViewToModel(index).data(ModList::IndexRole).toInt();
+
+ if (index == start || !index.isValid()) {
+ // wrapped around, give up
+ break;
+ }
+
+ ModInfo::Ptr mod = ModInfo::getByIndex(modIndex);
+
+ // skip overwrite and backups and separators
+ if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) ||
+ mod->hasFlag(ModInfo::FLAG_BACKUP) ||
+ mod->hasFlag(ModInfo::FLAG_SEPARATOR)) {
+ continue;
+ }
+
+ return modIndex;
+ }
+
+ return -1;
+}
+
+int ModListView::prevMod(int modIndex) const
+{
+ const QModelIndex start = indexModelToView(m_core->modList()->index(modIndex, 0));
+
+ auto index = start;
+
+ for (;;) {
+ int row = index.row() - 1;
+ if (row == -1) {
+ row = model()->rowCount() - 1;
+ }
+
+ index = model()->index(row, 0);
+ modIndex = indexViewToModel(index).data(ModList::IndexRole).toInt();
+
+ if (index == start || !index.isValid()) {
+ // wrapped around, give up
+ break;
+ }
+
+ // skip overwrite and backups and separators
+ ModInfo::Ptr mod = ModInfo::getByIndex(modIndex);
+
+ if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) ||
+ mod->hasFlag(ModInfo::FLAG_BACKUP) ||
+ mod->hasFlag(ModInfo::FLAG_SEPARATOR)) {
+ continue;
+ }
+
+ return modIndex;
+ }
+
+ return -1;
+}
+
+void ModListView::invalidate()
+{
+ if (m_sortProxy) {
+ m_sortProxy->invalidate();
+ }
+}
+
+void ModListView::enableAllVisible()
+{
+ Profile* profile = m_core->currentProfile();
+
+ QList<unsigned int> modsToEnable;
+ for (auto& index : allIndex(model())) {
+ modsToEnable.append(index.data(ModList::IndexRole).toInt());
+ }
+ profile->setModsEnabled(modsToEnable, {});
+ invalidate();
+}
+
+void ModListView::disableAllVisible()
+{
+ MOBase::log::debug("disableAllVisible: {}", model()->rowCount());
+ Profile* profile = m_core->currentProfile();
+
+ QList<unsigned int> modsToDisable;
+ for (auto& index : allIndex(model())) {
+ modsToDisable.append(index.data(ModList::IndexRole).toInt());
+ }
+ profile->setModsEnabled({}, modsToDisable);
+ invalidate();
+}
+
+void ModListView::enableSelected()
+{
+ Profile* profile = m_core->currentProfile();
+ if (selectionModel()->hasSelection()) {
+ QList<unsigned int> modsToEnable;
+ for (auto row : selectionModel()->selectedRows(ModList::COL_PRIORITY)) {
+ int modID = profile->modIndexByPriority(row.data().toInt());
+ modsToEnable.append(modID);
+ }
+ profile->setModsEnabled(modsToEnable, {});
+ }
+ invalidate();
+}
+
+void ModListView::disableSelected()
+{
+ Profile* profile = m_core->currentProfile();
+ if (selectionModel()->hasSelection()) {
+ QList<unsigned int> modsToDisable;
+ for (auto row : selectionModel()->selectedRows(ModList::COL_PRIORITY)) {
+ int modID = profile->modIndexByPriority(row.data().toInt());
+ modsToDisable.append(modID);
+ }
+ profile->setModsEnabled({}, modsToDisable);
+ }
+ invalidate();
+}
+
+void ModListView::setFilterCriteria(const std::vector<ModListSortProxy::Criteria>& criteria)
+{
+ m_sortProxy->setCriteria(criteria);
+}
+
+void ModListView::setFilterOptions(ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep)
+{
+ m_sortProxy->setOptions(mode, sep);
+}
+
+bool ModListView::isModVisible(unsigned int index) const
+{
+ return m_sortProxy->filterMatchesMod(ModInfo::getByIndex(index), m_core->currentProfile()->modEnabled(index));
+}
+
+bool ModListView::isModVisible(ModInfo::Ptr mod) const
+{
+ return m_sortProxy->filterMatchesMod(mod, m_core->currentProfile()->modEnabled(ModInfo::getIndex(mod->name())));
+}
+
+QModelIndex ModListView::indexModelToView(const QModelIndex& index) const
+{
+ if (index.model() != m_core->modList()) {
+ return QModelIndex();
+ }
+
+ // we need to stack the proxy
+ std::vector<QAbstractProxyModel*> proxies;
+ {
+ auto* currentModel = model();
+ while (auto* proxy = qobject_cast<QAbstractProxyModel*>(currentModel)) {
+ proxies.push_back(proxy);
+ currentModel = proxy->sourceModel();
+ }
+ }
+
+ if (proxies.empty() || proxies.back()->sourceModel() != m_core->modList()) {
+ return QModelIndex();
+ }
+
+ auto qindex = index;
+ for (auto rit = proxies.rbegin(); rit != proxies.rend(); ++rit) {
+ qindex = (*rit)->mapFromSource(qindex);
+ }
+
+ return qindex;
+}
+
+QModelIndex ModListView::indexViewToModel(const QModelIndex& index) const
+{
+ if (index.model() == m_core->modList()) {
+ return index;
+ }
+ else if (auto* proxy = qobject_cast<const QAbstractProxyModel*>(index.model())) {
+ return indexViewToModel(proxy->mapToSource(index));
+ }
+ else {
+ return QModelIndex();
+ }
+}
+
+std::vector<QModelIndex> ModListView::allIndex(
+ const QAbstractItemModel* model, int column, const QModelIndex& parent) const
+{
+ std::vector<QModelIndex> index;
+ for (std::size_t i = 0; i < model->rowCount(parent); ++i) {
+ index.push_back(model->index(i, column, parent));
+
+ auto cindex = allIndex(model, column, index.back());
+ index.insert(index.end(), cindex.begin(), cindex.end());
+ }
+ return index;
+}
+
+void ModListView::expandItem(const QModelIndex& index) {
+ if (index.model() == m_sortProxy->sourceModel()) {
+ expand(m_sortProxy->mapFromSource(index));
+ }
+ else if (index.model() == model()) {
+ expand(index);
+ }
+}
+
+void ModListView::onModPrioritiesChanged(std::vector<int> const& indices)
+{
+ if (m_sortProxy != nullptr) {
+ // expand separator whose priority has changed
+ if (hasCollapsibleSeparators()) {
+ for (auto index : indices) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
+ if (modInfo->isSeparator()) {
+ expand(indexModelToView(m_core->modList()->index(index, 0)));
+ }
+ }
+ }
+ m_sortProxy->invalidate();
+ }
+}
+
+void ModListView::onModInstalled(const QString& modName)
+{
+ unsigned int index = ModInfo::getIndex(modName);
+
+ if (index == UINT_MAX) {
+ return;
+ }
+
+ QModelIndex qIndex = indexModelToView(m_core->modList()->index(index, 0));
+
+ if (hasCollapsibleSeparators()) {
+ expand(qIndex);
+ }
+
+ // focus, scroll to and select
+ setFocus(Qt::OtherFocusReason);
+ scrollTo(qIndex);
+ setCurrentIndex(qIndex);
+ selectionModel()->select(qIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
+}
+
+void ModListView::onModFilterActive(bool filterActive)
+{
+ ui.clearFilters->setVisible(filterActive);
+ if (filterActive) {
+ setStyleSheet("QTreeView { border: 2px ridge #f00; }");
+ ui.counter->setStyleSheet("QLCDNumber { border: 2px ridge #f00; }");
+ }
+ else if (ui.groupBy->currentIndex() != GroupBy::NONE) {
+ setStyleSheet("QTreeView { border: 2px ridge #337733; }");
+ ui.counter->setStyleSheet("");
+ }
+ else {
+ setStyleSheet("");
+ ui.counter->setStyleSheet("");
+ }
+}
+
+void ModListView::updateModCount()
+{
+ 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();
+
+ 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;
+ }
+ }
+
+ 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 (isVisible)
+ visRegularCount++;
+ regularCount++;
+ }
+ }
+
+ ui.counter->display(visActiveCount);
+ 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>"
+ "<tr><td>Unmanaged/DLCs:&emsp;</td><td align=right>%5</td><td align=right>%6</td></tr>"
+ "<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)
+ );
+}
+
+void ModListView::updateGroupByProxy(int groupIndex)
+{
+ // if the index is -1, we do not refresh unless we are grouping
+ // by separator
+ if (groupIndex == -1) {
+ if (ui.groupBy->currentIndex() != GroupBy::NONE) {
+ return;
+ }
+ groupIndex = ui.groupBy->currentIndex();
+ }
+
+ if (groupIndex == GroupBy::CATEGORY) {
+ m_byCategoryProxy->setGroupedColumn(ModList::COL_CATEGORY);
+ m_sortProxy->setSourceModel(m_byCategoryProxy);
+ }
+ else if (groupIndex == GroupBy::NEXUS_ID) {
+ m_byNexusIdProxy->setGroupedColumn(ModList::COL_MODID);
+ m_sortProxy->setSourceModel(m_byNexusIdProxy);
+ }
+ else if (m_sortProxy->sortColumn() == ModList::COL_PRIORITY
+ && m_sortProxy->sortOrder() == Qt::AscendingOrder) {
+ m_sortProxy->setSourceModel(m_byPriorityProxy);
+ m_byPriorityProxy->refresh();
+ }
+ else {
+ m_sortProxy->setSourceModel(m_core->modList());
+ }
+}
+
+void ModListView::setup(OrganizerCore& core, Ui::MainWindow* mwui)
+{
+ // attributes
+ m_core = &core;
+ ui = { mwui->groupCombo, mwui->activeModsCounter, mwui->modFilterEdit, mwui->clearFiltersButton };
+
+ connect(m_core, &OrganizerCore::modInstalled, this, &ModListView::onModInstalled);
+ connect(core.modList(), &ModList::modPrioritiesChanged, this, &ModListView::onModPrioritiesChanged);
+
+ m_byPriorityProxy = new ModListByPriorityProxy(core.currentProfile(), &core);
+ m_byPriorityProxy->setSourceModel(core.modList());
+ connect(this, &QTreeView::expanded, m_byPriorityProxy, &ModListByPriorityProxy::expanded);
+ connect(this, &QTreeView::collapsed, m_byPriorityProxy, &ModListByPriorityProxy::collapsed);
+ connect(m_byPriorityProxy, &ModListByPriorityProxy::expandItem, this, &ModListView::expandItem);
+
+ m_byCategoryProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_CATEGORY,
+ Qt::UserRole, 0, Qt::UserRole + 2);
+ connect(this, &QTreeView::expanded, m_byCategoryProxy, &QtGroupingProxy::expanded);
+ connect(this, &QTreeView::collapsed, m_byCategoryProxy, &QtGroupingProxy::collapsed);
+ connect(m_byCategoryProxy, &QtGroupingProxy::expandItem, this, &ModListView::expandItem);
+ m_byNexusIdProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_MODID, Qt::DisplayRole,
+ QtGroupingProxy::FLAG_NOGROUPNAME | QtGroupingProxy::FLAG_NOSINGLE, Qt::UserRole + 2);
+ connect(this, &QTreeView::expanded, m_byNexusIdProxy, &QtGroupingProxy::expanded);
+ connect(this, &QTreeView::collapsed, m_byNexusIdProxy, &QtGroupingProxy::collapsed);
+ connect(m_byNexusIdProxy, &QtGroupingProxy::expandItem, this, &ModListView::expandItem);
+
+ m_sortProxy = new ModListSortProxy(core.currentProfile(), &core);
+ setModel(m_sortProxy);
+
+ connect(m_sortProxy, &QAbstractItemModel::layoutAboutToBeChanged,
+ this, [this](const QList<QPersistentModelIndex>& parents, QAbstractItemModel::LayoutChangeHint hint) {
+ if (hint == QAbstractItemModel::VerticalSortHint) {
+ updateGroupByProxy(-1);
+ }
+ });
+ sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder);
+
+ connect(ui.groupBy, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [&](int index) {
+ updateGroupByProxy(index);
+ onModFilterActive(m_sortProxy->isFilterActive());
+ });
+
+ connect(this, &ModListView::dragEntered, core.modList(), &ModList::onDragEnter);
+ connect(this, &ModListView::dropEntered, m_byPriorityProxy, &ModListByPriorityProxy::onDropEnter);
+
+ connect(model(), &QAbstractItemModel::layoutChanged, this, &ModListView::updateModCount);
+
+ connect(header(), &QHeaderView::sortIndicatorChanged, this, [&](int, Qt::SortOrder) {
+ verticalScrollBar()->repaint(); });
+ connect(header(), &QHeaderView::sectionResized, this, [&](int logicalIndex, int oldSize, int newSize) {
+ m_sortProxy->setColumnVisible(logicalIndex, newSize != 0); });
+
+ GenericIconDelegate* contentDelegate = new GenericIconDelegate(this, Qt::UserRole + 3, ModList::COL_CONTENT, 150);
+ ModFlagIconDelegate* flagDelegate = new ModFlagIconDelegate(this, ModList::COL_FLAGS, 120);
+ ModConflictIconDelegate* conflictFlagDelegate = new ModConflictIconDelegate(this, ModList::COL_CONFLICTFLAGS, 80);
+
+ connect(header(), &QHeaderView::sectionResized, contentDelegate, &GenericIconDelegate::columnResized);
+ connect(header(), &QHeaderView::sectionResized, flagDelegate, &ModFlagIconDelegate::columnResized);
+ connect(header(), &QHeaderView::sectionResized, conflictFlagDelegate, &ModConflictIconDelegate::columnResized);
+
+ setItemDelegateForColumn(ModList::COL_FLAGS, flagDelegate);
+ setItemDelegateForColumn(ModList::COL_CONFLICTFLAGS, conflictFlagDelegate);
+ setItemDelegateForColumn(ModList::COL_CONTENT, contentDelegate);
+
+ // TODO: Check if this is really useful.
+ header()->installEventFilter(m_core->modList());
+
+ if (m_core->settings().geometry().restoreState(header())) {
+ // hack: force the resize-signal to be triggered because restoreState doesn't seem to do that
+ for (int column = 0; column <= ModList::COL_LASTCOLUMN; ++column) {
+ int sectionSize = header()->sectionSize(column);
+ header()->resizeSection(column, sectionSize + 1);
+ header()->resizeSection(column, sectionSize);
+ }
+ }
+ else {
+ // hide these columns by default
+ header()->setSectionHidden(ModList::COL_CONTENT, true);
+ header()->setSectionHidden(ModList::COL_MODID, true);
+ header()->setSectionHidden(ModList::COL_GAME, true);
+ header()->setSectionHidden(ModList::COL_INSTALLTIME, true);
+ header()->setSectionHidden(ModList::COL_NOTES, true);
+
+ // resize mod list to fit content
+ for (int i = 0; i < header()->count(); ++i) {
+ header()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+
+ header()->setSectionResizeMode(ModList::COL_NAME, QHeaderView::Stretch);
+ }
+
+ // prevent the name-column from being hidden
+ header()->setSectionHidden(ModList::COL_NAME, false);
+
+ // TODO: Move the event filter in ModListView.
+ installEventFilter(core.modList());
+
+ connect(m_core->modList(), &ModList::downloadArchiveDropped, this, [this](int row, int priority) {
+ m_core->installDownload(row, priority);
+ });
+
+ connect(m_sortProxy, &ModListSortProxy::filterActive, this, &ModListView::onModFilterActive);
+ connect(ui.filter, &QLineEdit::textChanged, m_sortProxy, &ModListSortProxy::updateFilter);
+ connect(m_sortProxy, &QAbstractItemModel::layoutChanged, this, [&]() {
+ if (hasCollapsibleSeparators()) {
+ m_byPriorityProxy->refreshExpandedItems();
+ }
+ });
+}
+
QRect ModListView::visualRect(const QModelIndex& index) const
{
QRect rect = QTreeView::visualRect(index);
diff --git a/src/modlistview.h b/src/modlistview.h
index 9cf1c0d9..1e6c5ceb 100644
--- a/src/modlistview.h
+++ b/src/modlistview.h
@@ -1,12 +1,21 @@
#ifndef MODLISTVIEW_H
#define MODLISTVIEW_H
+#include <vector>
+
#include <QTreeView>
#include <QDragEnterEvent>
+#include <QLCDNumber>
+
+#include "qtgroupingproxy.h"
#include "viewmarkingscrollbar.h"
+#include "modlistsortproxy.h"
namespace Ui { class MainWindow; }
+
class OrganizerCore;
+class Profile;
+class ModListByPriorityProxy;
class ModListView : public QTreeView
{
@@ -26,15 +35,81 @@ public:
explicit ModListView(QWidget* parent = 0);
void setModel(QAbstractItemModel* model) override;
+ void setup(OrganizerCore& core, Ui::MainWindow* mwui);
+
+ // set the current profile
+ //
+ void setProfile(Profile* profile);
+
+ // check if collapsible separators are currently used
+ //
+ bool hasCollapsibleSeparators() const;
+
+ // the column by which the mod list is currently sorted
+ //
+ int sortColumn() const;
+
+ // retrieve the next/previous mod in the current view, the given index
+ // should be a mod index (not a model row), and the return value will be
+ // a mod index or -1 if no mod was found
+ //
+ int nextMod(int index) const;
+ int prevMod(int index) const;
+
+ // invalidate the top-level model
+ //
+ void invalidate();
+
+ // enable/disable all visible mods
+ //
+ void enableAllVisible();
+ void disableAllVisible();
+
+ // enable/disable all selected mods
+ //
+ void enableSelected();
+ void disableSelected();
+
+ // set the filter criteria/options for mods
+ //
+ void setFilterCriteria(const std::vector<ModListSortProxy::Criteria>& criteria);
+ void setFilterOptions(ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep);
+
+ // check if the given mod is visible
+ //
+ bool isModVisible(unsigned int index) const;
+ bool isModVisible(ModInfo::Ptr mod) const;
+
+ // re-implemented to fix indentation with collapsible separators
+ //
QRect visualRect(const QModelIndex& index) const override;
+ // refresh the style of the mod list, this needs to be called when the
+ // stylesheet is changed
+ //
+ void refreshStyle();
+
signals:
void dragEntered(const QMimeData* mimeData);
void dropEntered(const QMimeData* mimeData, DropPosition position);
+public slots:
+
+ void updateModCount();
+
protected:
+ // map from/to the view indexes to the model
+ //
+ QModelIndex indexModelToView(const QModelIndex& index) const;
+ QModelIndex indexViewToModel(const QModelIndex& index) const;
+
+ // all index for the given model under the given index, recursively
+ //
+ std::vector<QModelIndex> allIndex(
+ const QAbstractItemModel* model, int column = 0, const QModelIndex& index = QModelIndex()) const;
+
// re-implemented to fake the return value to allow drag-and-drop on
// itself for separators
//
@@ -47,6 +122,50 @@ protected:
private:
+ void onModPrioritiesChanged(std::vector<int> const& indices);
+ void onModInstalled(const QString& modName);
+ void onModFilterActive(bool filterActive);
+
+ // call expand() after fixing the index if it comes from the source
+ // of the proxy
+ //
+ void expandItem(const QModelIndex& index);
+
+ // refresh the group-by proxy, if the index is -1 will refresh the
+ // current one (e.g. when changing the sort column)
+ //
+ void updateGroupByProxy(int groupIndex);
+
+ enum GroupBy {
+ NONE = 0,
+ CATEGORY = 1,
+ NEXUS_ID = 2
+ };
+
+private:
+
+ struct ModListViewUi
+ {
+ // the group by combo box
+ QComboBox* groupBy;
+
+ // the mod counter
+ QLCDNumber* counter;
+
+ // the text filter and clear filter button
+ QLineEdit* filter;
+ QPushButton* clearFilters;
+ };
+
+ OrganizerCore* m_core;
+ ModListViewUi ui;
+
+ ModListSortProxy* m_sortProxy;
+ ModListByPriorityProxy* m_byPriorityProxy;
+
+ QtGroupingProxy* m_byCategoryProxy;
+ QtGroupingProxy* m_byNexusIdProxy;
+
ViewMarkingScrollBar* m_scrollbar;
bool m_inDragMoveEvent = false;