From 7e2c52133960b7d1bbb0fe72a37cee1c2b67c0ec Mon Sep 17 00:00:00 2001 From: Mikaƫl Capelle Date: Tue, 29 Dec 2020 19:36:17 +0100 Subject: Refactoring and fixes. - Move codes from MainWindow to ModListView. - Fix enable/disable all visible. - Fix changing style of the mod list. --- src/mainwindow.cpp | 465 ++++-------------------------------------- src/mainwindow.h | 14 -- src/modlistsortproxy.cpp | 26 --- src/modlistsortproxy.h | 11 - src/modlistview.cpp | 519 ++++++++++++++++++++++++++++++++++++++++++++++- src/modlistview.h | 119 +++++++++++ 6 files changed, 679 insertions(+), 475 deletions(-) (limited to 'src') 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 . #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 . #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& 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(), std::set()); - 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("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 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 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 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 IDs; ModInfo::Ptr info = ModInfo::getByIndex(index); IDs.insert(std::make_pair(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(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 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 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("" - "" - "" - "" - "" - "" - "
TypeAllVisible
Enabled mods: %1 / %2%3 / %4
Unmanaged/DLCs: %5%6
Mod backups: %7%8
Separators: %9%10
") - .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 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& criteria) { - m_ModListSortProxy->setCriteria(criteria); + ui->modList->setFilterCriteria(criteria); QString label = "?"; @@ -5858,7 +5514,7 @@ void MainWindow::onFiltersCriteria(const std::vector 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 ¤t); void esplistSelectionsChanged(const QItemSelection ¤t); 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 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()); - invalidate(); -} - -void ModListSortProxy::disableAllVisible() -{ - if (m_Profile == nullptr) return; - - QList 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(), modsToDisable); - invalidate(); -} - unsigned long ModListSortProxy::flagsId(const std::vector &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 #include #include #include +#include + +#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 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 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 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 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& 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 proxies; + { + auto* currentModel = model(); + while (auto* proxy = qobject_cast(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(index.model())) { + return indexViewToModel(proxy->mapToSource(index)); + } + else { + return QModelIndex(); + } +} + +std::vector ModListView::allIndex( + const QAbstractItemModel* model, int column, const QModelIndex& parent) const +{ + std::vector 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 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 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 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("" + "" + "" + "" + "" + "" + "
TypeAllVisible
Enabled mods: %1 / %2%3 / %4
Unmanaged/DLCs: %5%6
Mod backups: %7%8
Separators: %9%10
") + .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& parents, QAbstractItemModel::LayoutChangeHint hint) { + if (hint == QAbstractItemModel::VerticalSortHint) { + updateGroupByProxy(-1); + } + }); + sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); + + connect(ui.groupBy, QOverload::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 + #include #include +#include + +#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& 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 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 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; -- cgit v1.3.1