From 373b659dcbcac5dfc081ca7fa5f78788166a4e39 Mon Sep 17 00:00:00 2001 From: Mikaƫl Capelle Date: Wed, 30 Dec 2020 00:28:48 +0100 Subject: Fix and move stuff around. - Move selection-related code from ModList to ModListView. - Fix move-selection for multi-selection. - Fix refresh of mod list on toggle selection. --- src/mainwindow.cpp | 52 +-------------- src/mainwindow.h | 1 - src/modlist.cpp | 112 +++++++------------------------ src/modlist.h | 19 +----- src/modlistbypriorityproxy.cpp | 17 +++++ src/modlistbypriorityproxy.h | 4 ++ src/modlistsortproxy.cpp | 7 -- src/modlistsortproxy.h | 2 - src/modlistview.cpp | 145 +++++++++++++++++++++++++++++++++++++---- src/modlistview.h | 11 ++++ src/organizercore.cpp | 2 - 11 files changed, 195 insertions(+), 177 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 63c9a8be..519799d2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -540,9 +540,12 @@ void MainWindow::setupModList() { ui->modList->setup(m_OrganizerCore, ui); + connect(ui->modList, &ModListView::removeSelectedMods, [=]() { removeMod_clicked(-1); }); + // keep here for now connect(ui->modList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::modlistSelectionsChanged); + connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); }); } void MainWindow::resetActionIcons() @@ -2280,54 +2283,6 @@ void MainWindow::esplist_changed() updatePluginCount(); } -void MainWindow::onModPrioritiesChanged(std::vector const& indices) -{ - for (unsigned int i = 0; i < m_OrganizerCore.currentProfile()->numMods(); ++i) { - int priority = m_OrganizerCore.currentProfile()->getModPriority(i); - if (m_OrganizerCore.currentProfile()->modEnabled(i)) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - // priorities in the directory structure are one higher because data is 0 - m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(modInfo->internalName())).setPriority(priority + 1); - } - } - m_OrganizerCore.refreshBSAList(); - m_OrganizerCore.currentProfile()->writeModlist(); - m_ArchiveListWriter.write(); - m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins(); - - { // refresh selection - QModelIndex current = ui->modList->currentIndex(); - if (current.isValid()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(current.data(ModList::IndexRole).toInt()); - // clear caches on all mods conflicting with the moved mod - for (int i : modInfo->getModOverwrite()) { - ModInfo::getByIndex(i)->clearCaches(); - } - for (int i : modInfo->getModOverwritten()) { - ModInfo::getByIndex(i)->clearCaches(); - } - for (int i : modInfo->getModArchiveOverwrite()) { - ModInfo::getByIndex(i)->clearCaches(); - } - for (int i : modInfo->getModArchiveOverwritten()) { - ModInfo::getByIndex(i)->clearCaches(); - } - for (int i : modInfo->getModArchiveLooseOverwrite()) { - ModInfo::getByIndex(i)->clearCaches(); - } - for (int i : modInfo->getModArchiveLooseOverwritten()) { - ModInfo::getByIndex(i)->clearCaches(); - } - // update conflict check on the moved mod - modInfo->doConflictCheck(); - m_OrganizerCore.modList()->setOverwriteMarkers(modInfo->getModOverwrite(), modInfo->getModOverwritten()); - m_OrganizerCore.modList()->setArchiveOverwriteMarkers(modInfo->getModArchiveOverwrite(), modInfo->getModArchiveOverwritten()); - m_OrganizerCore.modList()->setArchiveLooseOverwriteMarkers(modInfo->getModArchiveLooseOverwrite(), modInfo->getModArchiveLooseOverwritten()); - ui->modList->verticalScrollBar()->repaint(); - } - } -} - void MainWindow::modInstalled(const QString &modName) { unsigned int index = ModInfo::getIndex(modName); @@ -2529,7 +2484,6 @@ void MainWindow::removeMod_clicked(int modIndex) } } - void MainWindow::modRemoved(const QString &fileName) { if (!fileName.isEmpty() && !QFileInfo(fileName).isAbsolute()) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 26355153..97bca68b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -148,7 +148,6 @@ public: ModInfo::Ptr previousModInList(int modIndex); public slots: - void onModPrioritiesChanged(std::vector const& indices); void esplist_changed(); void refresherProgress(const DirectoryRefreshProgress* p); diff --git a/src/modlist.cpp b/src/modlist.cpp index 1f2f1171..a192390d 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1510,100 +1510,58 @@ QModelIndex ModList::indexToProxy(QAbstractItemModel* proxyModel, const QModelIn return QModelIndex(); } -bool ModList::moveSelection(QAbstractItemView *itemView, int direction) +void ModList::moveMods(const QModelIndexList& indices, int offset) { - QItemSelectionModel *selectionModel = itemView->selectionModel(); - int currentIndex = itemView->currentIndex().data(IndexRole).toInt(); - - const QAbstractProxyModel *proxyModel = qobject_cast(selectionModel->model()); - const QSortFilterProxyModel *filterModel = nullptr; - - emit layoutAboutToBeChanged(); - - while ((filterModel == nullptr) && (proxyModel != nullptr)) { - filterModel = qobject_cast(proxyModel); - if (filterModel == nullptr) { - proxyModel = qobject_cast(proxyModel->sourceModel()); - } - } - if (filterModel == nullptr) { - return true; - } - - int offset = -1; - if (((direction < 0) && (filterModel->sortOrder() == Qt::DescendingOrder)) || - ((direction > 0) && (filterModel->sortOrder() == Qt::AscendingOrder))) { - offset = 1; - } - - QModelIndexList rows = selectionModel->selectedRows(); - if (direction > 0) { - for (int i = 0; i < rows.size() / 2; ++i) { - rows.swapItemsAt(i, rows.size() - i - 1); - } - } + // retrieve the mod index and sort them by priority to avoid issue + // when moving them std::vector allIndex; - for (QModelIndex idx : rows) { + for (auto& idx : indices) { auto index = idx.data(IndexRole).toInt(); allIndex.push_back(index); + } + std::sort(allIndex.begin(), allIndex.end(), [=](int lhs, int rhs) { + bool cmp = m_Profile->getModPriority(lhs) < m_Profile->getModPriority(rhs); + return offset > 0 ? !cmp : cmp; + }); + + emit layoutAboutToBeChanged(); + + std::vector notify; + for (auto index : allIndex) { int newPriority = m_Profile->getModPriority(index) + offset; if ((newPriority >= 0) && (newPriority < static_cast(m_Profile->numRegularMods()))) { m_Profile->setModPriority(index, newPriority); - notifyChange(index); + notify.push_back(index); } } emit layoutChanged(); - emit modPrioritiesChanged(allIndex); - - // reset the selection and the index - itemView->setCurrentIndex(indexToProxy(itemView->model(), index(currentIndex, 0))); - for (auto idx : allIndex) { - itemView->selectionModel()->select( - indexToProxy(itemView->selectionModel()->model(), index(idx, 0)), - QItemSelectionModel::Select | QItemSelectionModel::Rows); + for (auto index : notify) { + notifyChange(index); } - return true; -} - -bool ModList::deleteSelection(QAbstractItemView *itemView) -{ - QItemSelectionModel *selectionModel = itemView->selectionModel(); - - QModelIndexList rows = selectionModel->selectedRows(); - if (rows.count() > 1) { - emit removeSelectedMods(); - } else if (rows.count() == 1) { - removeRow(rows[0].data(IndexRole).toInt(), QModelIndex()); - } - return true; + emit modPrioritiesChanged(allIndex); } -bool ModList::toggleSelection(QAbstractItemView *itemView) +bool ModList::toggleState(const QModelIndexList& indices) { emit aboutToChangeData(); - QItemSelectionModel *selectionModel = itemView->selectionModel(); - QList modsToEnable; QList modsToDisable; - QModelIndexList dirtyMods; - for (QModelIndex idx : selectionModel->selectedRows()) { - int modId = idx.data(IndexRole).toInt(); - if (m_Profile->modEnabled(modId)) { - modsToDisable.append(modId); - dirtyMods.append(idx); + for (auto index : indices) { + auto idx = index.data(IndexRole).toInt(); + if (m_Profile->modEnabled(idx)) { + modsToDisable.append(idx); } else { - modsToEnable.append(modId); - dirtyMods.append(idx); + modsToEnable.append(idx); } } m_Profile->setModsEnabled(modsToEnable, modsToDisable); - emit modlistChanged(dirtyMods, 0); + emit modlistChanged(indices, 0); emit tutorialModlistUpdate(); m_Modified = true; @@ -1616,26 +1574,6 @@ bool ModList::toggleSelection(QAbstractItemView *itemView) return true; } -bool ModList::eventFilter(QObject *obj, QEvent *event) -{ - if ((event->type() == QEvent::KeyPress) && (m_Profile != nullptr)) { - QAbstractItemView *itemView = qobject_cast(obj); - QKeyEvent *keyEvent = static_cast(event); - - if ((itemView != nullptr) - && (keyEvent->modifiers() == Qt::ControlModifier) - && ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) { - return moveSelection(itemView, keyEvent->key() == Qt::Key_Up ? -1 : 1); - } else if (keyEvent->key() == Qt::Key_Delete) { - return deleteSelection(itemView); - } else if (keyEvent->key() == Qt::Key_Space) { - return toggleSelection(itemView); - } - return QAbstractItemModel::eventFilter(obj, event); - } - return QAbstractItemModel::eventFilter(obj, event); -} - //note: caller needs to make sure sort proxy is updated void ModList::enableSelected(const QItemSelectionModel *selectionModel) { diff --git a/src/modlist.h b/src/modlist.h index edf7d53a..778f1fee 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -221,6 +221,9 @@ public slots: void enableSelected(const QItemSelectionModel *selectionModel); void disableSelected(const QItemSelectionModel *selectionModel); + void moveMods(const QModelIndexList& indices, int offset); + bool toggleState(const QModelIndexList& indices); + signals: /** @@ -290,11 +293,6 @@ signals: */ void tutorialModlistUpdate(); - /** - * @brief emitted to have all selected mods deleted - */ - void removeSelectedMods(); - /** * @brief fileMoved emitted when a file is moved from one mod to another * @param relativePath relative path of the file moved @@ -316,11 +314,6 @@ signals: // download list void downloadArchiveDropped(int row, int priority); -protected: - - // event filter, handles event from the header and the tree view itself - bool eventFilter(QObject *obj, QEvent *event); - private: QVariant getOverwriteData(int column, int role) const; @@ -344,12 +337,6 @@ private: MOBase::IModList::ModStates state(unsigned int modIndex) const; - bool moveSelection(QAbstractItemView *itemView, int direction); - - bool deleteSelection(QAbstractItemView *itemView); - - bool toggleSelection(QAbstractItemView *itemView); - private slots: private: diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index f898b85b..dc16d8ea 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -23,6 +23,7 @@ void ModListByPriorityProxy::setSourceModel(QAbstractItemModel* model) connect(sourceModel(), &QAbstractItemModel::layoutChanged, this, [this]() { buildTree(); }, Qt::UniqueConnection); connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, [this]() { buildTree(); }, Qt::UniqueConnection); connect(sourceModel(), &QAbstractItemModel::modelReset, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); + connect(sourceModel(), &QAbstractItemModel::dataChanged, this, &ModListByPriorityProxy::modelDataChanged, Qt::UniqueConnection); refresh(); } } @@ -93,6 +94,22 @@ void ModListByPriorityProxy::expandItems(const QModelIndex& index) const } } +void ModListByPriorityProxy::modelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) +{ + QModelIndex proxyTopLeft = mapFromSource(topLeft); + if (!proxyTopLeft.isValid()) { + return; + } + + if (topLeft == bottomRight) { + emit dataChanged(proxyTopLeft, proxyTopLeft); + } + else { + QModelIndex proxyBottomRight = mapFromSource(bottomRight); + emit dataChanged(proxyTopLeft, proxyBottomRight); + } +} + QModelIndex ModListByPriorityProxy::mapFromSource(const QModelIndex& sourceIndex) const { if (!sourceIndex.isValid()) { diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h index 26f60bc7..00848e2a 100644 --- a/src/modlistbypriorityproxy.h +++ b/src/modlistbypriorityproxy.h @@ -58,6 +58,10 @@ public slots: void expanded(const QModelIndex& index); void collapsed(const QModelIndex& index); +protected: + + void modelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles = QVector()); + private: void buildTree(); diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index a7d0b27a..ed752d7a 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -73,13 +73,6 @@ void ModListSortProxy::setCriteria(const std::vector& criteria) } } -Qt::ItemFlags ModListSortProxy::flags(const QModelIndex &modelIndex) const -{ - Qt::ItemFlags flags = sourceModel()->flags(mapToSource(modelIndex)); - - return flags; -} - unsigned long ModListSortProxy::flagsId(const std::vector &flags) const { unsigned long result = 0; diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index 9a4140f6..fed05188 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -77,8 +77,6 @@ public: void setProfile(Profile *profile); - - Qt::ItemFlags flags(const QModelIndex &modelIndex) const override; bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; diff --git a/src/modlistview.cpp b/src/modlistview.cpp index dbd09884..bda7ac4d 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -5,6 +5,8 @@ #include +#include + #include "ui_mainwindow.h" #include "organizercore.h" @@ -15,6 +17,8 @@ #include "modflagicondelegate.h" #include "modconflicticondelegate.h" #include "genericicondelegate.h" +#include "shared/directoryentry.h" +#include "shared/filesorigin.h" class ModListProxyStyle : public QProxyStyle { public: @@ -346,18 +350,63 @@ void ModListView::expandItem(const QModelIndex& 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))); - } + if (hasCollapsibleSeparators()) { + for (auto index : indices) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + if (modInfo->isSeparator()) { + expand(indexModelToView(m_core->modList()->index(index, 0))); } } + } + + for (unsigned int i = 0; i < m_core->currentProfile()->numMods(); ++i) { + int priority = m_core->currentProfile()->getModPriority(i); + if (m_core->currentProfile()->modEnabled(i)) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + // priorities in the directory structure are one higher because data is 0 + m_core->directoryStructure()->getOriginByName(MOBase::ToWString(modInfo->internalName())).setPriority(priority + 1); + } + } + m_core->refreshBSAList(); + m_core->currentProfile()->writeModlist(); + m_core->directoryStructure()->getFileRegister()->sortOrigins(); + + if (m_sortProxy) { m_sortProxy->invalidate(); } + + { // refresh selection + QModelIndex current = currentIndex(); + if (current.isValid()) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(current.data(ModList::IndexRole).toInt()); + // clear caches on all mods conflicting with the moved mod + for (int i : modInfo->getModOverwrite()) { + ModInfo::getByIndex(i)->clearCaches(); + } + for (int i : modInfo->getModOverwritten()) { + ModInfo::getByIndex(i)->clearCaches(); + } + for (int i : modInfo->getModArchiveOverwrite()) { + ModInfo::getByIndex(i)->clearCaches(); + } + for (int i : modInfo->getModArchiveOverwritten()) { + ModInfo::getByIndex(i)->clearCaches(); + } + for (int i : modInfo->getModArchiveLooseOverwrite()) { + ModInfo::getByIndex(i)->clearCaches(); + } + for (int i : modInfo->getModArchiveLooseOverwritten()) { + ModInfo::getByIndex(i)->clearCaches(); + } + // update conflict check on the moved mod + modInfo->doConflictCheck(); + m_core->modList()->setOverwriteMarkers(modInfo->getModOverwrite(), modInfo->getModOverwritten()); + m_core->modList()->setArchiveOverwriteMarkers(modInfo->getModArchiveOverwrite(), modInfo->getModArchiveOverwritten()); + m_core->modList()->setArchiveLooseOverwriteMarkers(modInfo->getModArchiveLooseOverwrite(), modInfo->getModArchiveLooseOverwritten()); + verticalScrollBar()->repaint(); + } + } } void ModListView::onModInstalled(const QString& modName) @@ -573,9 +622,6 @@ void ModListView::setup(OrganizerCore& core, Ui::MainWindow* mwui) 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) { @@ -603,9 +649,6 @@ void ModListView::setup(OrganizerCore& core, Ui::MainWindow* mwui) // 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); }); @@ -684,3 +727,79 @@ void ModListView::timerEvent(QTimerEvent* event) QTreeView::timerEvent(event); } } + +bool ModListView::moveSelection(int key) +{ + QModelIndex cindex = indexViewToModel(currentIndex()); + QModelIndexList sourceRows; + for (auto& index : selectionModel()->selectedRows()) { + sourceRows.append(indexViewToModel(index)); + } + + int offset = key == Qt::Key_Up ? -1 : 1; + if (m_sortProxy->sortOrder() == Qt::DescendingOrder) { + offset = -offset; + } + + m_core->modList()->moveMods(sourceRows, offset); + + // reset the selection and the index + setCurrentIndex(indexModelToView(cindex)); + for (auto idx : sourceRows) { + selectionModel()->select(indexModelToView(idx), QItemSelectionModel::Select | QItemSelectionModel::Rows); + } + + return true; +} + +bool ModListView::removeSelection() +{ + if (selectionModel()->hasSelection()) { + QModelIndexList rows = selectionModel()->selectedRows(); + if (rows.count() > 1) { + emit removeSelectedMods(); + } + else if (rows.count() == 1) { + // this does not work, I don't know why + // model()->removeRow(rows[0].row(), rows[0].parent()); + m_core->modList()->removeRow(indexViewToModel(rows[0]).row()); + } + } + return true; +} + +bool ModListView::toggleSelectionState() +{ + if (!selectionModel()->hasSelection()) { + return true; + } + + QModelIndexList selected; + for (QModelIndex idx : selectionModel()->selectedRows()) { + selected.append(indexViewToModel(idx)); + } + + return m_core->modList()->toggleState(selected); +} + +bool ModListView::event(QEvent* event) +{ + Profile* profile = m_core->currentProfile(); + if (event->type() == QEvent::KeyPress && profile) { + QKeyEvent* keyEvent = static_cast(event); + + if (keyEvent->modifiers() == Qt::ControlModifier + && sortColumn() == ModList::COL_PRIORITY + && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) { + return moveSelection(keyEvent->key()); + } + else if (keyEvent->key() == Qt::Key_Delete) { + return removeSelection(); + } + else if (keyEvent->key() == Qt::Key_Space) { + return toggleSelectionState(); + } + return QTreeView::event(event); + } + return QTreeView::event(event); +} diff --git a/src/modlistview.h b/src/modlistview.h index 2ea5891c..88028428 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -74,6 +74,10 @@ signals: void dragEntered(const QMimeData* mimeData); void dropEntered(const QMimeData* mimeData, DropPosition position); + // emitted when selected mods must be removed + // + void removeSelectedMods(); + public slots: // invalidate the top-level model @@ -121,10 +125,17 @@ protected: // QModelIndexList selectedIndexes() const; + bool moveSelection(int key); + bool removeSelection(); + bool toggleSelectionState(); + void timerEvent(QTimerEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; void dropEvent(QDropEvent* event) override; + bool event(QEvent* event) override; + +protected slots: private: diff --git a/src/organizercore.cpp b/src/organizercore.cpp index ecd4b34e..334a02de 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -249,8 +249,6 @@ void OrganizerCore::setUserInterface(IUserInterface* ui) SLOT(modRemoved(QString))); connect(&m_InstallationManager, SIGNAL(modReplaced(QString)), w, SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(removeSelectedMods()), w, - SLOT(removeMod_clicked())); connect(&m_ModList, SIGNAL(clearOverwrite()), w, SLOT(clearOverwrite())); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), w, -- cgit v1.3.1