From d0512da75805be194d949a6d6bac8184314da0e6 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sun, 27 Dec 2020 14:55:20 +0100 Subject: Use an intermediate structure to store the separator tree. --- src/modlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index c8056140..4191e2db 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1236,8 +1236,8 @@ void ModList::removeRowForce(int row, const QModelIndex &parent) m_Profile->cancelModlistWrite(); beginRemoveRows(parent, row, row); ModInfo::removeMod(row); - endRemoveRows(); m_Profile->refreshModStatus(); // removes the mod from the status list + endRemoveRows(); m_Profile->writeModlist(); // this ensures the modified list gets written back before new mods can be installed notifyModRemoved(modInfo->name()); -- cgit v1.3.1 From cc5f0c8555fbbcc4b6d0dc29a42b8c7869df1859 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sun, 27 Dec 2020 23:23:56 +0100 Subject: Drag and drop from download view to install + Expand and scroll to mod on install. --- src/downloadlist.cpp | 12 +++++++ src/downloadlist.h | 3 +- src/downloadstab.cpp | 3 ++ src/mainwindow.cpp | 24 ++++++++++---- src/modlist.cpp | 88 +++++++++++++++++++++++++++++++++++++++------------ src/modlist.h | 18 ++++++++--- src/organizercore.cpp | 14 ++++++-- src/organizercore.h | 2 +- 8 files changed, 128 insertions(+), 36 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp index a5c284e6..78e5fd24 100644 --- a/src/downloadlist.cpp +++ b/src/downloadlist.cpp @@ -90,6 +90,18 @@ QVariant DownloadList::headerData(int section, Qt::Orientation orientation, int } } +Qt::ItemFlags DownloadList::flags(const QModelIndex& idx) const +{ + return QAbstractTableModel::flags(idx) | Qt::ItemIsDragEnabled; +} + +QMimeData* DownloadList::mimeData(const QModelIndexList& indexes) const +{ + QMimeData* result = QAbstractItemModel::mimeData(indexes); + result->setData("text/plain", "archive"); + return result; +} + QVariant DownloadList::data(const QModelIndex &index, int role) const { bool pendingDownload = index.row() >= m_Manager->numTotalDownloads(); diff --git a/src/downloadlist.h b/src/downloadlist.h index 2171c013..65d03ab9 100644 --- a/src/downloadlist.h +++ b/src/downloadlist.h @@ -68,11 +68,12 @@ public: * @return number of rows to display **/ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual int columnCount(const QModelIndex &parent) const; QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex& idx) const override; + QMimeData* mimeData(const QModelIndexList& indexes) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; diff --git a/src/downloadstab.cpp b/src/downloadstab.cpp index a0602ede..e04799ec 100644 --- a/src/downloadstab.cpp +++ b/src/downloadstab.cpp @@ -17,6 +17,9 @@ DownloadsTab::DownloadsTab(OrganizerCore& core, Ui::MainWindow* mwui) ui.list->setItemDelegate(new DownloadProgressDelegate( m_core.downloadManager(), ui.list)); + ui.list->setDragEnabled(true); + ui.list->setDragDropMode(QAbstractItemView::DragDropMode::DragDrop); + update(); m_filter.setEdit(ui.filter); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 74f3fd62..ea7d5967 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -578,7 +578,6 @@ void MainWindow::setupModList() ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); - connect( ui->modList, SIGNAL(dropModeUpdate(bool)), m_OrganizerCore.modList(), SLOT(dropModeUpdate(bool))); @@ -653,6 +652,10 @@ void MainWindow::setupModList() 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); + }); } void MainWindow::resetActionIcons() @@ -743,6 +746,7 @@ MainWindow::~MainWindow() } } + void MainWindow::updateWindowTitle(const APIUserAccount& user) { //"\xe2\x80\x93" is an "em dash", a longer "-" @@ -2483,15 +2487,23 @@ void MainWindow::modorder_changed() void MainWindow::modInstalled(const QString &modName) { - QModelIndexList posList = - m_OrganizerCore.modList()->match(m_OrganizerCore.modList()->index(0, 0), Qt::DisplayRole, modName); - if (posList.count() == 1) { - ui->modList->scrollTo(posList.at(0)); + unsigned int index = ModInfo::getIndex(modName); + + if (index == UINT_MAX) { + 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)); } + ui->modList->scrollTo(m_ModListSortProxy->mapFromSource(qIndex)); // force an update to happen std::multimap IDs; - ModInfo::Ptr info = ModInfo::getByIndex(ModInfo::getIndex(modName)); + ModInfo::Ptr info = ModInfo::getByIndex(index); IDs.insert(std::make_pair(info->gameName(), info->nexusId())); modUpdateCheck(IDs); } diff --git a/src/modlist.cpp b/src/modlist.cpp index 4191e2db..ca4741e6 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1087,6 +1087,32 @@ boost::signals2::connection ModList::onModMoved(const std::function(row) >= ModInfo::getNumMods())) { + return -1; + } + + int newPriority = 0; + { + if ((row < 0) || (row > static_cast(m_Profile->numRegularMods()))) { + newPriority = m_Profile->numRegularMods() + 1; + } + else { + newPriority = m_Profile->getModPriority(row); + } + if (newPriority == -1) { + newPriority = m_Profile->numRegularMods() + 1; + } + } + + return newPriority; +} + bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent) { if (row == -1) { @@ -1160,7 +1186,6 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent) { - try { QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); QDataStream stream(&encoded, QIODevice::ReadOnly); @@ -1175,26 +1200,12 @@ bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &par } } - if (row == -1) { - row = parent.row(); - } - - if ((row < 0) || (static_cast(row) >= ModInfo::getNumMods())) { + int newPriority = dropPriority(row, parent); + if (newPriority == -1) { return false; } - - int newPriority = 0; - { - if ((row < 0) || (row > static_cast(m_Profile->numRegularMods()))) { - newPriority = m_Profile->numRegularMods() + 1; - } else { - newPriority = m_Profile->getModPriority(row); - } - if (newPriority == -1) { - newPriority = m_Profile->numRegularMods() + 1; - } - } changeModPriority(sourceRows, newPriority); + } catch (const std::exception &e) { reportError(tr("drag&drop failed: %1").arg(e.what())); } @@ -1202,6 +1213,37 @@ bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &par return false; } +bool ModList::dropArchive(const QMimeData* mimeData, int row, const QModelIndex& parent) +{ + int priority = dropPriority(row, parent); + if (priority == -1) { + return false; + } + + try { + QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); + QDataStream stream(&encoded, QIODevice::ReadOnly); + std::vector sourceRows; + + while (!stream.atEnd()) { + int sourceRow, col; + QMap roleDataMap; + stream >> sourceRow >> col >> roleDataMap; + if (col == 0) { + sourceRows.push_back(sourceRow); + } + } + + if (sourceRows.size() == 1) { + emit downloadArchiveDropped(sourceRows[0], priority); + } + } + catch (const std::exception& e) { + reportError(tr("drag&drop failed: %1").arg(e.what())); + } + + return false; +} bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int, const QModelIndex &parent) { @@ -1214,10 +1256,14 @@ bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int if (mimeData->hasUrls()) { return dropURLs(mimeData, row, parent); } else if (mimeData->hasText()) { - return dropMod(mimeData, row, parent); - } else { - return false; + if (mimeData->text() == "mod") { + return dropMod(mimeData, row, parent); + } + else if (mimeData->text() == "archive") { + return dropArchive(mimeData, row, parent); + } } + return false; } void ModList::removeRowForce(int row, const QModelIndex &parent) diff --git a/src/modlist.h b/src/modlist.h index fad53755..2ca2fff1 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -310,6 +310,10 @@ signals: void postDataChanged(); + // emitted when an item is dropped from the download list, the row is from the + // download list + void downloadArchiveDropped(int row, int priority); + protected: // event filter, handles event from the header and the tree view itself @@ -336,10 +340,6 @@ private: bool renameMod(int index, const QString &newName); - bool dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent); - - bool dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent); - MOBase::IModList::ModStates state(unsigned int modIndex) const; bool moveSelection(QAbstractItemView *itemView, int direction); @@ -367,6 +367,16 @@ private: QFlags state; }; +private: + + bool dropURLs(const QMimeData* mimeData, int row, const QModelIndex& parent); + bool dropMod(const QMimeData* mimeData, int row, const QModelIndex& parent); + bool dropArchive(const QMimeData* mimeData, int row, const QModelIndex& parent); + + // return the priority of the mod for a drop event + // + int dropPriority(int row, const QModelIndex& parent) const; + private: friend class ModListByPriorityProxy; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 74b6ed08..454247b6 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -830,13 +830,13 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, return nullptr; } -void OrganizerCore::installDownload(int index) +ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) { if (m_InstallationManager.isRunning()) { QMessageBox::information( qApp->activeWindow(), tr("Installation cancelled"), tr("Another installation is currently in progress."), QMessageBox::Ok); - return; + return nullptr; } try { @@ -873,10 +873,15 @@ void OrganizerCore::installDownload(int index) refresh(); int modIndex = ModInfo::getIndex(modName); + ModInfo::Ptr modInfo = nullptr; if (modIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + modInfo = ModInfo::getByIndex(modIndex); modInfo->addInstalledFile(modID, fileID); + if (priority != -1) { + m_ModList.changeModPriority(modIndex, priority); + } + if (hasIniTweaks && m_UserInterface != nullptr && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), tr("This mod contains ini tweaks. Do you " @@ -894,6 +899,7 @@ void OrganizerCore::installDownload(int index) } m_DownloadManager.markInstalled(index); emit modInstalled(modName); + return modInfo; } else { m_InstallationManager.notifyInstallationEnd(result, nullptr); @@ -909,6 +915,8 @@ void OrganizerCore::installDownload(int index) } catch (const std::exception &e) { reportError(e.what()); } + + return nullptr; } QString OrganizerCore::resolvePath(const QString &fileName) const diff --git a/src/organizercore.h b/src/organizercore.h index f2b904cd..cb577437 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -376,7 +376,7 @@ public slots: void refreshLists(); - void installDownload(int downloadIndex); + ModInfo::Ptr installDownload(int downloadIndex, int priority = -1); void modStatusChanged(unsigned int index); void modStatusChanged(QList index); -- cgit v1.3.1 From addb38645b41507ae8e0536bb83d3b99d365f664 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 28 Dec 2020 13:44:26 +0100 Subject: Clean drag&drop of URLs and mods/archives. --- src/mainwindow.cpp | 4 - src/modinfo.cpp | 15 ++-- src/modinfo.h | 8 +- src/modlist.cpp | 172 +++++++++++++++++++++++------------------ src/modlist.h | 26 ++++--- src/modlistbypriorityproxy.cpp | 72 +++++++---------- src/modlistbypriorityproxy.h | 1 - src/modlistview.cpp | 38 --------- src/modlistview.h | 6 -- 9 files changed, 156 insertions(+), 186 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ea7d5967..df571a2d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -578,10 +578,6 @@ void MainWindow::setupModList() ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); - connect( - ui->modList, SIGNAL(dropModeUpdate(bool)), - m_OrganizerCore.modList(), SLOT(dropModeUpdate(bool))); - connect( ui->modList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(modListSortIndicatorChanged(int,Qt::SortOrder))); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index a0382fe8..509c3837 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -47,6 +47,7 @@ using namespace MOShared; std::vector ModInfo::s_Collection; +ModInfo::Ptr ModInfo::s_Overwrite; std::map ModInfo::s_ModsByName; std::map, std::vector> ModInfo::s_ModsByModID; int ModInfo::s_NextID; @@ -108,13 +109,14 @@ ModInfo::Ptr ModInfo::createFromPlugin(const QString &modName, return result; } -void ModInfo::createFromOverwrite(PluginContainer *pluginContainer, - const MOBase::IPluginGame* game, - MOShared::DirectoryEntry **directoryStructure) +ModInfo::Ptr ModInfo::createFromOverwrite( + PluginContainer *pluginContainer, const MOBase::IPluginGame* game, + MOShared::DirectoryEntry **directoryStructure) { QMutexLocker locker(&s_Mutex); - - s_Collection.push_back(ModInfo::Ptr(new ModInfoOverwrite(pluginContainer, game, directoryStructure))); + ModInfo::Ptr overwrite = ModInfo::Ptr(new ModInfoOverwrite(pluginContainer, game, directoryStructure)); + s_Collection.push_back(overwrite); + return overwrite; } unsigned int ModInfo::getNumMods() @@ -237,6 +239,7 @@ void ModInfo::updateFromDisc(const QString &modDirectory, QMutexLocker lock(&s_Mutex); s_Collection.clear(); s_NextID = 0; + s_Overwrite = nullptr; { // list all directories in the mod directory and make a mod out of each QDir mods(QDir::fromNativeSeparators(modDirectory)); @@ -263,7 +266,7 @@ void ModInfo::updateFromDisc(const QString &modDirectory, } } - createFromOverwrite(pluginContainer, game, directoryStructure); + s_Overwrite = createFromOverwrite(pluginContainer, game, directoryStructure); std::sort(s_Collection.begin(), s_Collection.end(), ModInfo::ByName); diff --git a/src/modinfo.h b/src/modinfo.h index d04f6657..3981be18 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -177,6 +177,11 @@ public: // Static functions: */ static unsigned int getIndex(const QString &name); + /** + * @brief Retrieve the overwrite mod. + */ + static ModInfo::Ptr getOverwrite() { return s_Overwrite; } + /** * @brief Find the first mod that fulfills the filter function (after no particular order). * @@ -981,6 +986,7 @@ protected: protected: static std::vector s_Collection; + static ModInfo::Ptr s_Overwrite; static std::map s_ModsByName; int m_PrimaryCategory; @@ -992,7 +998,7 @@ protected: private: - static void createFromOverwrite(PluginContainer* pluginContainer, + static ModInfo::Ptr createFromOverwrite(PluginContainer* pluginContainer, const MOBase::IPluginGame* game, MOShared::DirectoryEntry** directoryStructure); diff --git a/src/modlist.cpp b/src/modlist.cpp index ca4741e6..b79f0b0e 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -69,7 +69,6 @@ ModList::ModList(PluginContainer *pluginContainer, OrganizerCore *organizer) , m_Modified(false) , m_InNotifyChange(false) , m_FontMetrics(QFont()) - , m_DropOnItems(false) , m_PluginContainer(pluginContainer) { m_LastCheck.start(); @@ -689,14 +688,10 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const result |= Qt::ItemIsEditable; } } - if (m_DropOnItems - && (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end())) { - result |= Qt::ItemIsDropEnabled; - } - } else { - if (!m_DropOnItems) result |= Qt::ItemIsDropEnabled; } - return result; + + // drop check is handled by canDropMimeData + return result | Qt::ItemIsDropEnabled; } @@ -1113,6 +1108,52 @@ int ModList::dropPriority(int row, const QModelIndex& parent) const return newPriority; } +std::vector ModList::sourceRows(const QMimeData* mimeData) const +{ + QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); + QDataStream stream(&encoded, QIODevice::ReadOnly); + std::vector sourceRows; + + while (!stream.atEnd()) { + int sourceRow, col; + QMap roleDataMap; + stream >> sourceRow >> col >> roleDataMap; + if (col == 0) { + sourceRows.push_back(sourceRow); + } + } + return sourceRows; +} + +std::optional> ModList::relativeUrl(const QUrl& url) const +{ + if (!url.isLocalFile()) { + return {}; + } + + QDir allModsDir(Settings::instance().paths().mods()); + QDir overwriteDir(Settings::instance().paths().overwrite()); + + QFileInfo sourceInfo(url.toLocalFile()); + QString sourceFile = sourceInfo.canonicalFilePath(); + + QString relativePath; + QString originName; + + if (sourceFile.startsWith(allModsDir.canonicalPath())) { + QDir relativeDir(allModsDir.relativeFilePath(sourceFile)); + QStringList splitPath = relativeDir.path().split("/"); + originName = splitPath[0]; + splitPath.pop_front(); + return { { splitPath.join("/"), originName } }; + } + else if (sourceFile.startsWith(overwriteDir.canonicalPath())) { + return { { overwriteDir.relativeFilePath(sourceFile), ModInfo::getOverwrite()->name() } }; + } + + return {}; +} + bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent) { if (row == -1) { @@ -1121,45 +1162,23 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa ModInfo::Ptr modInfo = ModInfo::getByIndex(row); QDir modDir = QDir(modInfo->absolutePath()); - QDir allModsDir(Settings::instance().paths().mods()); - QDir overwriteDir(Settings::instance().paths().overwrite()); - QStringList sourceList; QStringList targetList; QList> relativePathList; - unsigned int overwriteIndex = ModInfo::findMod([] (ModInfo::Ptr mod) -> bool { - std::vector flags = mod->getFlags(); - return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); }); - - QString overwriteName = ModInfo::getByIndex(overwriteIndex)->name(); - for (auto url : mimeData->urls()) { - if (!url.isLocalFile()) { - log::debug("URL drop ignored: \"{}\" is not a local file", url.url()); + auto p = relativeUrl(url); + + if (!p) { + log::debug("URL drop ignored: \"{}\" is not a local file or not a known file to MO", url.url()); continue; } + auto [relativePath, originName] = *p; + QFileInfo sourceInfo(url.toLocalFile()); QString sourceFile = sourceInfo.canonicalFilePath(); - QString relativePath; - QString originName; - - if (sourceFile.startsWith(allModsDir.canonicalPath())) { - QDir relativeDir(allModsDir.relativeFilePath(sourceFile)); - QStringList splitPath = relativeDir.path().split("/"); - originName = splitPath[0]; - splitPath.pop_front(); - relativePath = splitPath.join("/"); - } else if (sourceFile.startsWith(overwriteDir.canonicalPath())) { - originName = overwriteName; - relativePath = overwriteDir.relativeFilePath(sourceFile); - } else { - log::debug("URL drop ignored: \"{}\" is not a known file to MO", sourceFile); - continue; - } - QFileInfo targetInfo(modDir.absoluteFilePath(relativePath)); sourceList << sourceFile; targetList << targetInfo.absoluteFilePath(); @@ -1186,24 +1205,13 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent) { - try { - QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); - QDataStream stream(&encoded, QIODevice::ReadOnly); - std::vector sourceRows; - - while (!stream.atEnd()) { - int sourceRow, col; - QMap roleDataMap; - stream >> sourceRow >> col >> roleDataMap; - if (col == 0) { - sourceRows.push_back(sourceRow); - } - } + int newPriority = dropPriority(row, parent); + if (newPriority == -1) { + return false; + } - int newPriority = dropPriority(row, parent); - if (newPriority == -1) { - return false; - } + try { + std::vector sourceRows = this->sourceRows(mimeData); changeModPriority(sourceRows, newPriority); } catch (const std::exception &e) { @@ -1221,19 +1229,7 @@ bool ModList::dropArchive(const QMimeData* mimeData, int row, const QModelIndex& } try { - QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); - QDataStream stream(&encoded, QIODevice::ReadOnly); - std::vector sourceRows; - - while (!stream.atEnd()) { - int sourceRow, col; - QMap roleDataMap; - stream >> sourceRow >> col >> roleDataMap; - if (col == 0) { - sourceRows.push_back(sourceRow); - } - } - + std::vector sourceRows = this->sourceRows(mimeData); if (sourceRows.size() == 1) { emit downloadArchiveDropped(sourceRows[0], priority); } @@ -1245,6 +1241,41 @@ bool ModList::dropArchive(const QMimeData* mimeData, int row, const QModelIndex& return false; } +bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, int row, int column, const QModelIndex& parent) const +{ + if (action == Qt::IgnoreAction) { + return false; + } + + if (mimeData->hasUrls()) { + for (auto& url : mimeData->urls()) { + if (!relativeUrl(url)) { + return false; + } + } + if (row == -1 && parent.isValid()) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(parent.row()); + return modInfo->isRegular() && !modInfo->isSeparator(); + } + } + else if (mimeData->hasText()) { + // drop on item + if (row == -1 && parent.isValid()) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(parent.row()); + return modInfo->isSeparator(); + } + else if (hasIndex(row, column, parent)) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(row); + return modInfo->isSeparator() || !parent.isValid(); + } + else { + return true; + } + } + + return false; +} + bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int, const QModelIndex &parent) { if (action == Qt::IgnoreAction) { @@ -1407,15 +1438,6 @@ QMap ModList::itemData(const QModelIndex &index) const return result; } - -void ModList::dropModeUpdate(bool dropOnItems) -{ - if (m_DropOnItems != dropOnItems) { - m_DropOnItems = dropOnItems; - } -} - - QString ModList::getColumnName(int column) { switch (column) { diff --git a/src/modlist.h b/src/modlist.h index 2ca2fff1..b2eb6be6 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -199,11 +199,13 @@ public: // implementation of virtual functions of QAbstractItemModel virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const; - virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } - virtual QStringList mimeTypes() const; - virtual QMimeData *mimeData(const QModelIndexList &indexes) const; - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); - virtual bool removeRows(int row, int count, const QModelIndex &parent); + virtual bool removeRows(int row, int count, const QModelIndex& parent); + + Qt::DropActions supportedDropActions() const override { return Qt::MoveAction | Qt::CopyAction; } + QStringList mimeTypes() const override; + QMimeData *mimeData(const QModelIndexList &indexes) 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; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; @@ -213,10 +215,8 @@ public: // implementation of virtual functions of QAbstractItemModel public slots: - void dropModeUpdate(bool dropOnItems); void enableSelected(const QItemSelectionModel *selectionModel); - void disableSelected(const QItemSelectionModel *selectionModel); signals: @@ -369,6 +369,16 @@ private: private: + // retrieve the relative path of file and its origin given a URL from Mime data + // returns an empty optional if the URL is not a valid file for dropping + // + std::optional> relativeUrl(const QUrl&) const; + + // return the source rows from the given mime data for drag&drop of mods or + // installation archives + // + std::vector sourceRows(const QMimeData* mimeData) const; + bool dropURLs(const QMimeData* mimeData, int row, const QModelIndex& parent); bool dropMod(const QMimeData* mimeData, int row, const QModelIndex& parent); bool dropArchive(const QMimeData* mimeData, int row, const QModelIndex& parent); @@ -392,8 +402,6 @@ private: QFontMetrics m_FontMetrics; - bool m_DropOnItems; - std::set m_Overwrite; std::set m_Overwritten; std::set m_ArchiveOverwrite; diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index 1bbf9459..dc51617e 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -210,64 +210,44 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { - // we need to fix the source row + // we need to fix the source model row int sourceRow = -1; - if (row >= 0) { - if (!parent.isValid()) { - if (row < m_Root.children.size()) { - sourceRow = m_Root.children[row]->index; - } - else { - sourceRow = ModInfo::getNumMods(); - } + if (data->hasUrls()) { + if (parent.isValid()) { + sourceRow = static_cast(parent.internalPointer())->index; } - else { - auto* item = static_cast(parent.internalPointer()); - QStringList what; - for (auto& child : item->children) { - what.append(QString::number(child->index)); + } + else { + if (row >= 0) { + if (!parent.isValid()) { + if (row < m_Root.children.size()) { + sourceRow = m_Root.children[row]->index; + } + else { + sourceRow = ModInfo::getNumMods(); + } } + else { + auto* item = static_cast(parent.internalPointer()); - if (row < item->children.size()) { - sourceRow = item->children[row]->index; - } - else if (parent.row() + 1 < m_Root.children.size()) { - sourceRow = m_Root.children[parent.row() + 1]->index; + if (row < item->children.size()) { + sourceRow = item->children[row]->index; + } + else if (parent.row() + 1 < m_Root.children.size()) { + sourceRow = m_Root.children[parent.row() + 1]->index; + } } } - } - else if (parent.isValid()) { - // this is a drop in a separator - sourceRow = m_Root.children[parent.row() + 1]->index; + else if (parent.isValid()) { + // this is a drop in a separator + sourceRow = m_Root.children[parent.row() + 1]->index; + } } return sourceModel()->dropMimeData(data, action, sourceRow, column, QModelIndex()); } -Qt::ItemFlags ModListByPriorityProxy::flags(const QModelIndex& idx) const -{ - if (!idx.isValid()) { - return sourceModel()->flags(QModelIndex()); - } - - // we check the flags of the root node and if drop is not enabled, it - // means we are dragging files. - Qt::ItemFlags rootFlags = sourceModel()->flags(QModelIndex()); - if (!rootFlags.testFlag(Qt::ItemIsDropEnabled)) { - return sourceModel()->flags(mapToSource(idx)); - } - - auto flags = sourceModel()->flags(mapToSource(idx)); - auto* item = static_cast(idx.internalPointer()); - - if (item->mod->isSeparator()) { - flags |= Qt::ItemIsDropEnabled; - } - - return flags; -} - QModelIndex ModListByPriorityProxy::index(int row, int column, const QModelIndex& parent) const { if (!hasIndex(row, column, parent)) { diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h index fdf59182..725a3242 100644 --- a/src/modlistbypriorityproxy.h +++ b/src/modlistbypriorityproxy.h @@ -31,7 +31,6 @@ public: int rowCount(const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; - Qt::ItemFlags flags(const QModelIndex& idx) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& index) const override; bool hasChildren(const QModelIndex& parent) const override; diff --git a/src/modlistview.cpp b/src/modlistview.cpp index fcf34749..27e23417 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -2,37 +2,6 @@ #include #include #include -#include - -class ModListViewStyle: public QProxyStyle { -public: - ModListViewStyle(QStyle *style, int indentation); - - void drawPrimitive (PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget = 0) const; -private: - int m_Indentation; -}; - -ModListViewStyle::ModListViewStyle(QStyle *style, int indentation) - : QProxyStyle(style), m_Indentation(indentation) -{ -} - -void ModListViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const -{ - if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) { - QStyleOption opt(*option); - opt.rect.setLeft(m_Indentation); - if (widget) { - opt.rect.setRight(widget->width() - 5); // 5 is an arbitrary value that seems to work ok - } - QProxyStyle::drawPrimitive(element, &opt, painter, widget); - } else { - QProxyStyle::drawPrimitive(element, option, painter, widget); - } -} ModListView::ModListView(QWidget *parent) : QTreeView(parent) @@ -43,13 +12,6 @@ ModListView::ModListView(QWidget *parent) setAutoExpandDelay(500); } -void ModListView::dragEnterEvent(QDragEnterEvent *event) -{ - emit dropModeUpdate(event->mimeData()->hasUrls()); - - QTreeView::dragEnterEvent(event); -} - void ModListView::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); diff --git a/src/modlistview.h b/src/modlistview.h index 982591a3..bc5654ce 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -12,11 +12,6 @@ public: explicit ModListView(QWidget *parent = 0); void setModel(QAbstractItemModel *model) override; -signals: - void dropModeUpdate(bool dropOnRows); - -public slots: - protected: // replace the auto-expand timer from QTreeView to avoid @@ -25,7 +20,6 @@ protected: void timerEvent(QTimerEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; - void dragEnterEvent(QDragEnterEvent* event) override; private: -- cgit v1.3.1 From 4636d7bd5d092ff47146248232cd73c8d0254d7a Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 28 Dec 2020 13:50:43 +0100 Subject: Small refactoring to avoid duplicated code. --- src/modlist.cpp | 8 ++++---- src/modlist.h | 4 ++-- src/modlistbypriorityproxy.cpp | 37 ++++++++++++++++--------------------- 3 files changed, 22 insertions(+), 27 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index b79f0b0e..a058e71c 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1108,7 +1108,7 @@ int ModList::dropPriority(int row, const QModelIndex& parent) const return newPriority; } -std::vector ModList::sourceRows(const QMimeData* mimeData) const +std::vector ModList::sourceRows(const QMimeData* mimeData) { QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); QDataStream stream(&encoded, QIODevice::ReadOnly); @@ -1125,7 +1125,7 @@ std::vector ModList::sourceRows(const QMimeData* mimeData) const return sourceRows; } -std::optional> ModList::relativeUrl(const QUrl& url) const +std::optional> ModList::relativeUrl(const QUrl& url) { if (!url.isLocalFile()) { return {}; @@ -1211,7 +1211,7 @@ bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &par } try { - std::vector sourceRows = this->sourceRows(mimeData); + std::vector sourceRows = ModList::sourceRows(mimeData); changeModPriority(sourceRows, newPriority); } catch (const std::exception &e) { @@ -1229,7 +1229,7 @@ bool ModList::dropArchive(const QMimeData* mimeData, int row, const QModelIndex& } try { - std::vector sourceRows = this->sourceRows(mimeData); + std::vector sourceRows = ModList::sourceRows(mimeData); if (sourceRows.size() == 1) { emit downloadArchiveDropped(sourceRows[0], priority); } diff --git a/src/modlist.h b/src/modlist.h index b2eb6be6..eebb1105 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -372,12 +372,12 @@ private: // retrieve the relative path of file and its origin given a URL from Mime data // returns an empty optional if the URL is not a valid file for dropping // - std::optional> relativeUrl(const QUrl&) const; + static std::optional> relativeUrl(const QUrl&); // return the source rows from the given mime data for drag&drop of mods or // installation archives // - std::vector sourceRows(const QMimeData* mimeData) const; + static std::vector sourceRows(const QMimeData* mimeData); bool dropURLs(const QMimeData* mimeData, int row, const QModelIndex& parent); bool dropMod(const QMimeData* mimeData, int row, const QModelIndex& parent); diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index dc51617e..eb931aa9 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -160,35 +160,30 @@ bool ModListByPriorityProxy::setData(const QModelIndex& index, const QVariant& v bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const { - bool firstRowSeparator = false; - try { - QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist"); - QDataStream stream(&encoded, QIODevice::ReadOnly); - - int firstRowPriority = INT_MAX; - unsigned int firstRowIndex = -1; - - while (!stream.atEnd()) { - int sourceRow, col; - QMap roleDataMap; - stream >> sourceRow >> col >> roleDataMap; - if (col == 0) { + if (data->hasText()) { + bool firstRowSeparator = false; + + try { + int firstRowPriority = INT_MAX; + unsigned int firstRowIndex = -1; + + for (auto sourceRow : ModList::sourceRows(data)) { if (m_Profile->getModPriority(sourceRow) < firstRowPriority) { firstRowIndex = sourceRow; firstRowPriority = m_Profile->getModPriority(sourceRow); } } - } - firstRowSeparator = firstRowIndex != -1 && ModInfo::getByIndex(firstRowIndex)->isSeparator(); - } - catch (std::exception const&) { + firstRowSeparator = firstRowIndex != -1 && ModInfo::getByIndex(firstRowIndex)->isSeparator(); + } + catch (std::exception const&) { - } + } - // first row is a separator, we can drop it anywhere - if (firstRowSeparator) { - return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); + // first row is a separator, we can drop it anywhere + if (firstRowSeparator) { + return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); + } } if (!parent.isValid() && row >= 0) { -- cgit v1.3.1 From 9d8c64dccbdb5144c6496cbd9ef4eb636f6b4ace Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 28 Dec 2020 15:20:37 +0100 Subject: Do not set Qt::ItemIsDropEnabled all the time. --- src/mainwindow.cpp | 2 ++ src/modlist.cpp | 18 +++++++++++++----- src/modlist.h | 3 ++- src/modlistview.cpp | 6 ++++++ src/modlistview.h | 5 +++++ 5 files changed, 28 insertions(+), 6 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 65ab368d..48a935ae 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -578,6 +578,8 @@ void MainWindow::setupModList() ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); + connect(ui->modList, &ModListView::dragEntered, m_OrganizerCore.modList(), &ModList::onDragEnter); + connect( ui->modList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(modListSortIndicatorChanged(int,Qt::SortOrder))); diff --git a/src/modlist.cpp b/src/modlist.cpp index a058e71c..79dbf815 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -664,7 +664,6 @@ QVariant ModList::headerData(int section, Qt::Orientation orientation, return QAbstractItemModel::headerData(section, orientation, role); } - Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const { Qt::ItemFlags result = QAbstractItemModel::flags(modelIndex); @@ -688,10 +687,15 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const result |= Qt::ItemIsEditable; } } + if (modInfo->isSeparator() || m_DropOnMod) { + result |= Qt::ItemIsDropEnabled; + } + } + else if (!m_DropOnMod) { + result |= Qt::ItemIsDropEnabled; } - // drop check is handled by canDropMimeData - return result | Qt::ItemIsDropEnabled; + return result; } @@ -1241,6 +1245,11 @@ bool ModList::dropArchive(const QMimeData* mimeData, int row, const QModelIndex& return false; } +void ModList::onDragEnter(const QMimeData* mimeData) +{ + m_DropOnMod = mimeData->hasUrls(); +} + bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, int row, int column, const QModelIndex& parent) const { if (action == Qt::IgnoreAction) { @@ -1261,8 +1270,7 @@ bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, else if (mimeData->hasText()) { // drop on item if (row == -1 && parent.isValid()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(parent.row()); - return modInfo->isSeparator(); + return true; } else if (hasIndex(row, column, parent)) { ModInfo::Ptr modInfo = ModInfo::getByIndex(row); diff --git a/src/modlist.h b/src/modlist.h index eebb1105..4e50c959 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -215,7 +215,7 @@ public: // implementation of virtual functions of QAbstractItemModel public slots: - + void onDragEnter(const QMimeData* data); void enableSelected(const QItemSelectionModel *selectionModel); void disableSelected(const QItemSelectionModel *selectionModel); @@ -399,6 +399,7 @@ private: mutable bool m_Modified; bool m_InNotifyChange; + bool m_DropOnMod = false; QFontMetrics m_FontMetrics; diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 27e23417..91bdced3 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -18,6 +18,12 @@ void ModListView::setModel(QAbstractItemModel *model) setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); } +void ModListView::dragEnterEvent(QDragEnterEvent* event) +{ + emit dragEntered(event->mimeData()); + QTreeView::dragEnterEvent(event); +} + void ModListView::dragMoveEvent(QDragMoveEvent* event) { if (autoExpandDelay() >= 0) { diff --git a/src/modlistview.h b/src/modlistview.h index bc5654ce..9546321e 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -12,6 +12,10 @@ public: explicit ModListView(QWidget *parent = 0); void setModel(QAbstractItemModel *model) override; +signals: + + void dragEntered(const QMimeData* mimeData); + protected: // replace the auto-expand timer from QTreeView to avoid @@ -19,6 +23,7 @@ protected: QBasicTimer openTimer; void timerEvent(QTimerEvent* event) override; + void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; private: -- cgit v1.3.1 From b340c564cfd151540bf5b03f3f878153b8f120ee Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 28 Dec 2020 17:15:22 +0100 Subject: Fix keyboard move selection. --- src/mainwindow.cpp | 115 +++++++++++++++++++++++++++++++---------- src/mainwindow.h | 7 ++- src/modlist.cpp | 29 +++++------ src/modlist.h | 15 +++--- src/modlistbypriorityproxy.cpp | 10 ++-- src/modlistbypriorityproxy.h | 1 + src/organizercore.cpp | 2 - 7 files changed, 125 insertions(+), 54 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 48a935ae..61d0e5bb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -548,9 +548,12 @@ MainWindow::MainWindow(Settings &settings 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); - emit m_OrganizerCore.modList()->layoutChanged(); + m_ModListByPriorityProxy->refresh(); } else { m_ModListSortProxy->setSourceModel(m_OrganizerCore.modList()); @@ -579,6 +582,7 @@ void MainWindow::setupModList() ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); connect(ui->modList, &ModListView::dragEntered, m_OrganizerCore.modList(), &ModList::onDragEnter); + connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, this, &MainWindow::onModPrioritiesChanged); connect( ui->modList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), @@ -2435,8 +2439,58 @@ void MainWindow::esplist_changed() updatePluginCount(); } -void MainWindow::modorder_changed() +QModelIndex MainWindow::modViewIndexToModel(const QModelIndex& index) const +{ + auto sindex = index; + + // remove the sort proxy. + sindex = m_ModListSortProxy->mapToSource(index); + + // if there is another proxy + if (auto* proxy = qobject_cast(m_ModListSortProxy->sourceModel())) { + sindex = proxy->mapToSource(sindex); + } + + return sindex; +} + +QModelIndex MainWindow::modModelIndexToView(const QModelIndex& index) const +{ + auto dindex = index; + + // if there is another proxy than the sort + if (auto* proxy = qobject_cast(m_ModListSortProxy->sourceModel())) { + dindex = proxy->mapFromSource(dindex); + } + + // add the sort proxy + dindex = m_ModListSortProxy->mapFromSource(dindex); + + return dindex; + +} + +void MainWindow::onModPrioritiesChanged(std::vector const& indices) { + // if we have collapsible separators, we need to refresh, expand if necessary, + // and recreate the selection + if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy) { + + // manually retain the selection and restore it after + QModelIndex current = modViewIndexToModel(ui->modList->currentIndex()); + std::vector selected; + for (const auto& idx : ui->modList->selectionModel()->selectedRows()) { + selected.push_back(modViewIndexToModel(idx)); + } + + m_ModListByPriorityProxy->refresh(); + + ui->modList->setCurrentIndex(modModelIndexToView(current)); + for (auto idx : selected) { + ui->modList->selectionModel()->select(modModelIndexToView(idx), QItemSelectionModel::Select | QItemSelectionModel::Rows); + } + } + for (unsigned int i = 0; i < m_OrganizerCore.currentProfile()->numMods(); ++i) { int priority = m_OrganizerCore.currentProfile()->getModPriority(i); if (m_OrganizerCore.currentProfile()->modEnabled(i)) { @@ -2453,7 +2507,7 @@ void MainWindow::modorder_changed() { // refresh selection QModelIndex current = ui->modList->currentIndex(); if (current.isValid()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(current.data(Qt::UserRole + 1).toInt()); + 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(); @@ -2478,8 +2532,9 @@ void MainWindow::modorder_changed() 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) + if (m_ModListSortProxy != nullptr) { m_ModListSortProxy->invalidate(); + } ui->modList->verticalScrollBar()->repaint(); } } @@ -2624,7 +2679,7 @@ void MainWindow::modlistSelectionsChanged(const QItemSelection &selected) if (selected.count()) { auto selection = selected.last(); auto index = selection.indexes().last(); - ModInfo::Ptr selectedMod = ModInfo::getByIndex(index.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr selectedMod = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); m_OrganizerCore.modList()->setOverwriteMarkers(selectedMod->getModOverwrite(), selectedMod->getModOverwritten()); m_OrganizerCore.modList()->setArchiveOverwriteMarkers(selectedMod->getModArchiveOverwrite(), selectedMod->getModArchiveOverwritten()); m_OrganizerCore.modList()->setArchiveLooseOverwriteMarkers(selectedMod->getModArchiveLooseOverwrite(), selectedMod->getModArchiveLooseOverwritten()); @@ -2669,7 +2724,7 @@ void MainWindow::removeMod_clicked() int i = 0; for (QModelIndex idx : selection->selectedRows()) { QString name = idx.data().toString(); - if (!ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->isRegular()) { + if (!ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->isRegular()) { continue; } @@ -2684,7 +2739,7 @@ void MainWindow::removeMod_clicked() mods += "
  • ...
  • "; } - modNames.append(ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->name()); + modNames.append(ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->name()); ++i; } if (QMessageBox::question(this, tr("Confirm"), @@ -2776,7 +2831,7 @@ void MainWindow::endorse_clicked() } for (QModelIndex idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(true); + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->endorse(true); } }); } @@ -2786,7 +2841,7 @@ void MainWindow::dontendorse_clicked() QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->setNeverEndorse(); + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->setNeverEndorse(); } } else { @@ -2812,7 +2867,7 @@ void MainWindow::unendorse_clicked() } for (QModelIndex idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(false); + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->endorse(false); } }); } @@ -2831,7 +2886,7 @@ void MainWindow::track_clicked() m_OrganizerCore.loggedInAction(this, [this] { QItemSelectionModel *selection = ui->modList->selectionModel(); for (auto idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(true); + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->track(true); } }); } @@ -2841,7 +2896,7 @@ void MainWindow::untrack_clicked() m_OrganizerCore.loggedInAction(this, [this] { QItemSelectionModel *selection = ui->modList->selectionModel(); for (auto idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(false); + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->track(false); } }); } @@ -3033,7 +3088,7 @@ void MainWindow::ignoreMissingData_clicked() std::vector changed; for (QModelIndex idx : rows) { - int row_idx = idx.data(Qt::UserRole + 1).toInt(); + int row_idx = idx.data(ModList::IndexRole).toInt(); ModInfo::Ptr info = ModInfo::getByIndex(row_idx); info->markValidated(true); changed.push_back(info); @@ -3058,7 +3113,7 @@ void MainWindow::markConverted_clicked() std::vector changed; for (QModelIndex idx : rows) { - int row_idx = idx.data(Qt::UserRole + 1).toInt(); + int row_idx = idx.data(ModList::IndexRole).toInt(); ModInfo::Ptr info = ModInfo::getByIndex(row_idx); info->markConverted(true); changed.push_back(info); @@ -3097,7 +3152,7 @@ void MainWindow::restoreHiddenFiles_clicked() for (QModelIndex idx : selection->selectedRows()) { QString name = idx.data().toString(); - int row_idx = idx.data(Qt::UserRole + 1).toInt(); + int row_idx = idx.data(ModList::IndexRole).toInt(); ModInfo::Ptr modInfo = ModInfo::getByIndex(row_idx); const auto flags = modInfo->getFlags(); @@ -3116,7 +3171,7 @@ void MainWindow::restoreHiddenFiles_clicked() mods += "
  • ...
  • "; } - modNames.append(ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->name()); + modNames.append(ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->name()); ++i; } if (QMessageBox::question(this, tr("Confirm"), @@ -3125,7 +3180,7 @@ void MainWindow::restoreHiddenFiles_clicked() for (QModelIndex idx : selection->selectedRows()) { - int row_idx = idx.data(Qt::UserRole + 1).toInt(); + int row_idx = idx.data(ModList::IndexRole).toInt(); ModInfo::Ptr modInfo = ModInfo::getByIndex(row_idx); const auto flags = modInfo->getFlags(); @@ -3186,7 +3241,7 @@ void MainWindow::visitOnNexus_clicked() QString gameName; for (QModelIndex idx : selection->selectedRows()) { - row_idx = idx.data(Qt::UserRole + 1).toInt(); + row_idx = idx.data(ModList::IndexRole).toInt(); info = ModInfo::getByIndex(row_idx); int modID = info->nexusId(); gameName = info->gameName(); @@ -3224,7 +3279,7 @@ void MainWindow::visitWebPage_clicked() ModInfo::Ptr info; QString gameName; for (QModelIndex idx : selection->selectedRows()) { - row_idx = idx.data(Qt::UserRole + 1).toInt(); + row_idx = idx.data(ModList::IndexRole).toInt(); info = ModInfo::getByIndex(row_idx); const auto url = info->parseCustomURL(); @@ -3245,7 +3300,7 @@ void MainWindow::visitWebPage_clicked() void MainWindow::visitNexusOrWebPage(const QModelIndex& idx) { - int row_idx = idx.data(Qt::UserRole + 1).toInt(); + int row_idx = idx.data(ModList::IndexRole).toInt(); ModInfo::Ptr info = ModInfo::getByIndex(row_idx); if (!info) { @@ -3293,7 +3348,7 @@ void MainWindow::openExplorer_clicked() QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); shell::Explore(info->absolutePath()); } } @@ -3332,7 +3387,7 @@ void MainWindow::openExplorer_activated() if (selection->hasSelection() && selection->selectedRows().count() == 1 ) { QModelIndex idx = selection->currentIndex(); - ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); std::vector flags = modInfo->getFlags(); if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { @@ -3621,7 +3676,7 @@ void MainWindow::setColor_clicked() QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); info->setColor(currentColor); } } @@ -3637,7 +3692,7 @@ void MainWindow::resetColor_clicked() QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); info->setColor(color); } } @@ -4196,7 +4251,7 @@ void MainWindow::ignoreUpdate() { QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); info->ignoreUpdate(true); } } @@ -4214,7 +4269,7 @@ void MainWindow::checkModUpdates_clicked() QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); IDs.insert(std::make_pair(info->gameName(), info->nexusId())); } } else { @@ -4229,7 +4284,7 @@ void MainWindow::unignoreUpdate() QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); info->ignoreUpdate(false); } } @@ -4640,6 +4695,12 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) initModListContextMenu(allMods); allMods->setTitle(tr("All Mods")); menu.addMenu(allMods); + + if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy) { + menu.addAction(tr("Collapse all"), ui->modList, &QTreeView::collapseAll); + menu.addAction(tr("Expand all"), ui->modList, &QTreeView::expandAll); + } + menu.addSeparator(); ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); diff --git a/src/mainwindow.h b/src/mainwindow.h index ccfd4881..61a8b5e3 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -148,7 +148,7 @@ public: ModInfo::Ptr previousModInList(); public slots: - void modorder_changed(); + void onModPrioritiesChanged(std::vector const& indices); void esplist_changed(); void refresherProgress(const DirectoryRefreshProgress* p); @@ -607,8 +607,13 @@ private slots: // ui slots void storeSettings(); void readSettings(); + void setupModList(); void updateModListByPriorityProxy(); + + // map index from the modlist view to the modlist model, handling proxy + QModelIndex modViewIndexToModel(const QModelIndex& index) const; + QModelIndex modModelIndexToView(const QModelIndex& index) const; }; #endif // MAINWINDOW_H diff --git a/src/modlist.cpp b/src/modlist.cpp index 79dbf815..a6286007 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -336,7 +336,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return modInfo->nexusId(); } - } else if (role == Qt::UserRole + 1) { + } else if (role == IndexRole) { return modIndex; } else if (role == Qt::UserRole + 2) { switch (column) { @@ -592,8 +592,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) } if (ok) { m_Profile->setModPriority(modID, newPriority); - - emit modorder_changed(); + emit modPrioritiesChanged({ modID }); result = true; } else { result = false; @@ -765,7 +764,7 @@ void ModList::changeModPriority(std::vector sourceIndices, int newPriority) emit layoutChanged(); - emit modorder_changed(); + emit modPrioritiesChanged(sourceIndices); } @@ -777,8 +776,7 @@ void ModList::changeModPriority(int sourceIndex, int newPriority) m_Profile->setModPriority(sourceIndex, newPriority); emit layoutChanged(); - - emit modorder_changed(); + emit modPrioritiesChanged({ sourceIndex }); } void ModList::setOverwriteMarkers(const std::set &overwrite, const std::set &overwritten) @@ -1525,17 +1523,18 @@ bool ModList::moveSelection(QAbstractItemView *itemView, int direction) rows.swapItemsAt(i, rows.size() - i - 1); } } + std::vector allIndex; for (QModelIndex idx : rows) { - if (filterModel != nullptr) { - idx = filterModel->mapToSource(idx); - } - int newPriority = m_Profile->getModPriority(idx.row()) + offset; + auto index = idx.data(IndexRole).toInt(); + allIndex.push_back(index); + int newPriority = m_Profile->getModPriority(index) + offset; if ((newPriority >= 0) && (newPriority < static_cast(m_Profile->numRegularMods()))) { - m_Profile->setModPriority(idx.row(), newPriority); - notifyChange(idx.row()); + m_Profile->setModPriority(index, newPriority); + notifyChange(index); } } - emit modorder_changed(); + + emit modPrioritiesChanged(allIndex); return true; } @@ -1547,7 +1546,7 @@ bool ModList::deleteSelection(QAbstractItemView *itemView) if (rows.count() > 1) { emit removeSelectedMods(); } else if (rows.count() == 1) { - removeRow(rows[0].data(Qt::UserRole + 1).toInt(), QModelIndex()); + removeRow(rows[0].data(IndexRole).toInt(), QModelIndex()); } return true; } @@ -1562,7 +1561,7 @@ bool ModList::toggleSelection(QAbstractItemView *itemView) QList modsToDisable; QModelIndexList dirtyMods; for (QModelIndex idx : selectionModel->selectedRows()) { - int modId = idx.data(Qt::UserRole + 1).toInt(); + int modId = idx.data(IndexRole).toInt(); if (m_Profile->modEnabled(modId)) { modsToDisable.append(modId); dirtyMods.append(idx); diff --git a/src/modlist.h b/src/modlist.h index 4e50c959..83e8ec9c 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -55,6 +55,10 @@ class ModList : public QAbstractItemModel public: + // role of the index of the mod + // + constexpr static int IndexRole = Qt::UserRole + 1; + enum EColumn { COL_NAME, COL_CONFLICTFLAGS, @@ -70,8 +74,6 @@ public: COL_LASTCOLUMN = COL_NOTES, }; - friend class ModListProxy; - using SignalModInstalled = boost::signals2::signal; using SignalModRemoved = boost::signals2::signal; using SignalModStateChanged = boost::signals2::signal&)>; @@ -222,12 +224,12 @@ public slots: signals: /** - * @brief emitted whenever the sorting in the list was changed by the user + * @brief Emitted whenever the priority of mods changes * - * the sorting of the list can only be manually changed if the list is sorted by priority - * in which case the move is intended to change the priority of a mod + * The sorting of the list can only be manually changed if the list is sorted by priority + * in which case the move is intended to change the priority of a mod. **/ - void modorder_changed(); + void modPrioritiesChanged(std::vector const& index); /** * @brief emitted when the model wants a text to be displayed by the UI @@ -389,6 +391,7 @@ private: private: + friend class ModListProxy; friend class ModListByPriorityProxy; OrganizerCore *m_Organizer; diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index eb931aa9..c4eb14b3 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -20,13 +20,17 @@ void ModListByPriorityProxy::setSourceModel(QAbstractItemModel* model) if (sourceModel()) { m_CollapsedItems.clear(); - connect(sourceModel(), &QAbstractItemModel::layoutChanged, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, [this]() { buildTree(); }, Qt::UniqueConnection); - connect(sourceModel(), &QAbstractItemModel::modelReset, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); - buildTree(); + // connect(sourceModel(), &QAbstractItemModel::modelReset, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); + refresh(); } } +void ModListByPriorityProxy::refresh() +{ + buildTree(); +} + void ModListByPriorityProxy::buildTree() { if (!sourceModel()) return; diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h index 725a3242..19d79f7f 100644 --- a/src/modlistbypriorityproxy.h +++ b/src/modlistbypriorityproxy.h @@ -26,6 +26,7 @@ public: ~ModListByPriorityProxy(); void setProfile(Profile* profile) { m_Profile = profile; } + void refresh(); void setSourceModel(QAbstractItemModel* sourceModel) override; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 454247b6..159296b7 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -255,8 +255,6 @@ void OrganizerCore::setUserInterface(IUserInterface* ui) SLOT(clearOverwrite())); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), w, SLOT(fileMoved(QString, QString, QString))); - connect(&m_ModList, SIGNAL(modorder_changed()), w, - SLOT(modorder_changed())); connect(&m_PluginList, SIGNAL(writePluginsList()), w, SLOT(esplist_changed())); connect(&m_PluginList, SIGNAL(esplist_changed()), w, -- cgit v1.3.1 From 7a65cc64885724c409f2deadaac925c66a9b00ee Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 29 Dec 2020 01:11:56 +0100 Subject: Disable drag&drop of mods in backups. --- src/modlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index a6286007..8c14d509 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1272,7 +1272,7 @@ bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, } else if (hasIndex(row, column, parent)) { ModInfo::Ptr modInfo = ModInfo::getByIndex(row); - return modInfo->isSeparator() || !parent.isValid(); + return !modInfo->isBackup() && (modInfo->isSeparator() || !parent.isValid()); } else { return true; -- cgit v1.3.1 From d979e60aed14b368ac9badf0b88c06f61c17893b Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 29 Dec 2020 01:35:02 +0100 Subject: Fix automatic refresh of the collapsible separator proxy. --- src/mainwindow.cpp | 50 ------------------------------------------ src/mainwindow.h | 4 ---- src/modlist.cpp | 30 +++++++++++++++++++++++++ src/modlist.h | 5 +++++ src/modlistbypriorityproxy.cpp | 3 ++- 5 files changed, 37 insertions(+), 55 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9ca7e534..1891e496 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2439,58 +2439,8 @@ void MainWindow::esplist_changed() updatePluginCount(); } -QModelIndex MainWindow::modViewIndexToModel(const QModelIndex& index) const -{ - auto sindex = index; - - // remove the sort proxy. - sindex = m_ModListSortProxy->mapToSource(index); - - // if there is another proxy - if (auto* proxy = qobject_cast(m_ModListSortProxy->sourceModel())) { - sindex = proxy->mapToSource(sindex); - } - - return sindex; -} - -QModelIndex MainWindow::modModelIndexToView(const QModelIndex& index) const -{ - auto dindex = index; - - // if there is another proxy than the sort - if (auto* proxy = qobject_cast(m_ModListSortProxy->sourceModel())) { - dindex = proxy->mapFromSource(dindex); - } - - // add the sort proxy - dindex = m_ModListSortProxy->mapFromSource(dindex); - - return dindex; - -} - void MainWindow::onModPrioritiesChanged(std::vector const& indices) { - // if we have collapsible separators, we need to refresh, expand if necessary, - // and recreate the selection - if (m_ModListSortProxy->sourceModel() == m_ModListByPriorityProxy) { - - // manually retain the selection and restore it after - QModelIndex current = modViewIndexToModel(ui->modList->currentIndex()); - std::vector selected; - for (const auto& idx : ui->modList->selectionModel()->selectedRows()) { - selected.push_back(modViewIndexToModel(idx)); - } - - m_ModListByPriorityProxy->refresh(); - - ui->modList->setCurrentIndex(modModelIndexToView(current)); - for (auto idx : selected) { - ui->modList->selectionModel()->select(modModelIndexToView(idx), QItemSelectionModel::Select | QItemSelectionModel::Rows); - } - } - for (unsigned int i = 0; i < m_OrganizerCore.currentProfile()->numMods(); ++i) { int priority = m_OrganizerCore.currentProfile()->getModPriority(i); if (m_OrganizerCore.currentProfile()->modEnabled(i)) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 61a8b5e3..8dd72174 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -610,10 +610,6 @@ private slots: // ui slots void setupModList(); void updateModListByPriorityProxy(); - - // map index from the modlist view to the modlist model, handling proxy - QModelIndex modViewIndexToModel(const QModelIndex& index) const; - QModelIndex modModelIndexToView(const QModelIndex& index) const; }; #endif // MAINWINDOW_H diff --git a/src/modlist.cpp b/src/modlist.cpp index 8c14d509..1f2f1171 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1493,14 +1493,33 @@ QString ModList::getColumnToolTip(int column) const } } +QModelIndex ModList::indexToProxy(QAbstractItemModel* proxyModel, const QModelIndex& index) +{ + if (!proxyModel) { + return QModelIndex(); + } + + if (proxyModel == this) { + return index; + } + + if (auto* proxy = qobject_cast(proxyModel)) { + return proxy->mapFromSource(indexToProxy(proxy->sourceModel(), index)); + } + + return QModelIndex(); +} bool ModList::moveSelection(QAbstractItemView *itemView, int direction) { 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) { @@ -1534,7 +1553,18 @@ bool ModList::moveSelection(QAbstractItemView *itemView, int direction) } } + 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); + } + return true; } diff --git a/src/modlist.h b/src/modlist.h index 83e8ec9c..edf7d53a 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -371,6 +371,11 @@ private: private: + // convert an index of the modlist to an index for the given model, assuming + // the given model is a proxy (of a proxy (of... )) the modlist + // + QModelIndex indexToProxy(QAbstractItemModel* proxyModel, const QModelIndex& index); + // retrieve the relative path of file and its origin given a URL from Mime data // returns an empty optional if the URL is not a valid file for dropping // diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index a5f8667f..0b73ba78 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -20,8 +20,9 @@ void ModListByPriorityProxy::setSourceModel(QAbstractItemModel* model) if (sourceModel()) { m_CollapsedItems.clear(); + 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::modelReset, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); refresh(); } } -- cgit v1.3.1 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(-) (limited to 'src/modlist.cpp') 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 From 8eb59316f9140a621bc4cf0d06d7b5b898b50972 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 14:39:57 +0100 Subject: Do not invalidate the sort proxy when not required (keep selection). --- src/mainwindow.cpp | 22 +++++++----- src/modlist.cpp | 31 ++++++---------- src/modlist.h | 13 +++++-- src/modlistsortproxy.cpp | 6 ++-- src/modlistview.cpp | 92 ++++++++++++++++++------------------------------ src/modlistview.h | 8 ++--- 6 files changed, 73 insertions(+), 99 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index df227aab..e01f984a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3833,15 +3833,17 @@ void MainWindow::ignoreUpdate(int modIndex) QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + auto index = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(index); info->ignoreUpdate(true); + m_OrganizerCore.modList()->notifyChange(index); } } else { ModInfo::Ptr info = ModInfo::getByIndex(modIndex); info->ignoreUpdate(true); + m_OrganizerCore.modList()->notifyChange(modIndex); } - ui->modList->invalidate(); } void MainWindow::checkModUpdates_clicked(int modIndex) @@ -3867,13 +3869,14 @@ void MainWindow::unignoreUpdate(int modIndex) for (QModelIndex idx : selection->selectedRows()) { ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); info->ignoreUpdate(false); + m_OrganizerCore.modList()->notifyChange(idx.data(ModList::IndexRole).toInt()); } } else { ModInfo::Ptr info = ModInfo::getByIndex(modIndex); info->ignoreUpdate(false); + m_OrganizerCore.modList()->notifyChange(modIndex); } - ui->modList->invalidate(); } void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, @@ -4970,7 +4973,6 @@ void MainWindow::nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVa return std::make_pair(gameNameReal, ModInfo::filteredMods(gameNameReal, resultList, userData.toBool(), true)); }); watcher->setFuture(future); - ui->modList->invalidate(); } void MainWindow::finishUpdateInfo() @@ -4989,6 +4991,7 @@ void MainWindow::finishUpdateInfo() if (mod->canBeUpdated()) { organizedGames.insert(std::make_pair(mod->gameName().toLower(), mod->nexusId())); } + m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name())); } if (!finalMods.empty() && organizedGames.empty()) @@ -5073,7 +5076,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()); - ui->modList->invalidate(); + m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name())); } else { // Scrape mod data here so we can use the mod version if no file update was located requiresInfo = true; @@ -5087,7 +5090,6 @@ void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userD void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID) { QVariantMap result = resultData.toMap(); - bool foundUpdate = false; QString gameNameReal; for (IPluginGame *game : m_PluginContainer.plugins()) { if (game->gameNexusName() == gameName) { @@ -5097,6 +5099,7 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD } std::vector modsList = ModInfo::getByModID(gameNameReal, modID); for (auto mod : modsList) { + bool foundUpdate = false; QDateTime now = QDateTime::currentDateTimeUtc(); QDateTime updateTarget = mod->getExpires(); if (now >= updateTarget) { @@ -5123,9 +5126,10 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD mod->setLastNexusQuery(QDateTime::currentDateTimeUtc()); mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC)); mod->saveMeta(); - } - if (foundUpdate) { - ui->modList->invalidate(); + + if (foundUpdate) { + m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name())); + } } } diff --git a/src/modlist.cpp b/src/modlist.cpp index a192390d..608a26b4 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1510,7 +1510,7 @@ QModelIndex ModList::indexToProxy(QAbstractItemModel* proxyModel, const QModelIn return QModelIndex(); } -void ModList::moveMods(const QModelIndexList& indices, int offset) +void ModList::shiftMods(const QModelIndexList& indices, int offset) { // retrieve the mod index and sort them by priority to avoid issue // when moving them @@ -1574,28 +1574,17 @@ bool ModList::toggleState(const QModelIndexList& indices) return true; } -//note: caller needs to make sure sort proxy is updated -void ModList::enableSelected(const QItemSelectionModel *selectionModel) +void ModList::setActive(const QModelIndexList& indices, bool active) { - if (selectionModel->hasSelection()) { - QList modsToEnable; - for (auto row : selectionModel->selectedRows(COL_PRIORITY)) { - int modID = m_Profile->modIndexByPriority(row.data().toInt()); - modsToEnable.append(modID); - } - m_Profile->setModsEnabled(modsToEnable, QList()); + QList mods; + for (auto& index : indices) { + mods.append(index.data(IndexRole).toInt()); } -} -//note: caller needs to make sure sort proxy is updated -void ModList::disableSelected(const QItemSelectionModel *selectionModel) -{ - if (selectionModel->hasSelection()) { - QList modsToDisable; - for (auto row : selectionModel->selectedRows(COL_PRIORITY)) { - int modID = m_Profile->modIndexByPriority(row.data().toInt()); - modsToDisable.append(modID); - } - m_Profile->setModsEnabled(QList(), modsToDisable); + if (active) { + m_Profile->setModsEnabled(mods, {}); + } + else { + m_Profile->setModsEnabled({}, mods); } } diff --git a/src/modlist.h b/src/modlist.h index 778f1fee..913d2ea8 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -218,10 +218,17 @@ public: // implementation of virtual functions of QAbstractItemModel public slots: void onDragEnter(const QMimeData* data); - void enableSelected(const QItemSelectionModel *selectionModel); - void disableSelected(const QItemSelectionModel *selectionModel); - void moveMods(const QModelIndexList& indices, int offset); + // enable/disable mods at the given indices. + // + void setActive(const QModelIndexList& indices, bool active); + + // shift the priority of mods at the given indices by the given offset + // + void shiftMods(const QModelIndexList& indices, int offset); + + // toggle the active state of mods at the given indices + // bool toggleState(const QModelIndexList& indices); signals: diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index ed752d7a..93f97895 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -69,7 +69,7 @@ void ModListSortProxy::setCriteria(const std::vector& criteria) if (changed || isForUpdates) { m_Criteria = criteria; updateFilterActive(); - invalidate(); + invalidateFilter(); } } @@ -236,7 +236,7 @@ void ModListSortProxy::updateFilter(const QString& filter) { m_Filter = filter; updateFilterActive(); - invalidate(); + invalidateFilter(); } bool ModListSortProxy::hasConflictFlag(const std::vector &flags) const @@ -555,7 +555,7 @@ void ModListSortProxy::setOptions( if (m_FilterMode != mode || separators != m_FilterSeparators) { m_FilterMode = mode; m_FilterSeparators = separators; - this->invalidate(); + invalidateFilter(); } } diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 74f4b566..c4641934 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -171,64 +171,28 @@ int ModListView::prevMod(int modIndex) const 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(); + m_core->modList()->setActive(indexViewToModel(allIndex(model())), true); } 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(); + m_core->modList()->setActive(indexViewToModel(allIndex(model())), false); } 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, {}); + m_core->modList()->setActive(indexViewToModel(selectionModel()->selectedRows()), true); } - 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); + m_core->modList()->setActive(indexViewToModel(selectionModel()->selectedRows()), false); } - invalidate(); } void ModListView::setFilterCriteria(const std::vector& criteria) @@ -279,6 +243,15 @@ QModelIndex ModListView::indexModelToView(const QModelIndex& index) const return qindex; } +QModelIndexList ModListView::indexModelToView(const QModelIndexList& index) const +{ + QModelIndexList result; + for (auto& idx : index) { + result.append(indexModelToView(idx)); + } + return result; +} + QModelIndex ModListView::indexViewToModel(const QModelIndex& index) const { if (index.model() == m_core->modList()) { @@ -292,6 +265,15 @@ QModelIndex ModListView::indexViewToModel(const QModelIndex& index) const } } +QModelIndexList ModListView::indexViewToModel(const QModelIndexList& index) const +{ + QModelIndexList result; + for (auto& idx : index) { + result.append(indexViewToModel(idx)); + } + return result; +} + QModelIndex ModListView::nextIndex(const QModelIndex& index) const { auto* model = index.model(); @@ -329,15 +311,13 @@ QModelIndex ModListView::prevIndex(const QModelIndex& index) const return prev; } -std::vector ModListView::allIndex( +QModelIndexList ModListView::allIndex( const QAbstractItemModel* model, int column, const QModelIndex& parent) const { - std::vector index; + QModelIndexList 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()); + index.append(model->index(i, column, parent)); + index.append(allIndex(model, column, index.back())); } return index; } @@ -376,10 +356,6 @@ void ModListView::onModPrioritiesChanged(std::vector const& indices) m_core->currentProfile()->writeModlist(); m_core->directoryStructure()->getFileRegister()->sortOrigins(); - if (m_sortProxy) { - m_sortProxy->invalidate(); - } - { // refresh selection QModelIndex current = currentIndex(); if (current.isValid()) { @@ -431,7 +407,7 @@ void ModListView::onModInstalled(const QString& modName) setFocus(Qt::OtherFocusReason); scrollTo(qIndex); setCurrentIndex(qIndex); - selectionModel()->select(qIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows); + selectionModel()->select(qIndex, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); } void ModListView::onModFilterActive(bool filterActive) @@ -666,6 +642,12 @@ void ModListView::setup(OrganizerCore& core, Ui::MainWindow* mwui) }); } +void ModListView::setModel(QAbstractItemModel* model) +{ + QTreeView::setModel(model); + setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); +} + QRect ModListView::visualRect(const QModelIndex& index) const { QRect rect = QTreeView::visualRect(index); @@ -680,12 +662,6 @@ QRect ModListView::visualRect(const QModelIndex& index) const return rect; } -void ModListView::setModel(QAbstractItemModel* model) -{ - QTreeView::setModel(model); - setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); -} - QModelIndexList ModListView::selectedIndexes() const { return m_inDragMoveEvent ? QModelIndexList() : QTreeView::selectedIndexes(); @@ -747,7 +723,7 @@ bool ModListView::moveSelection(int key) offset = -offset; } - m_core->modList()->moveMods(sourceRows, offset); + m_core->modList()->shiftMods(sourceRows, offset); // reset the selection and the index setCurrentIndex(indexModelToView(cindex)); diff --git a/src/modlistview.h b/src/modlistview.h index 88028428..1d8a9b49 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -80,10 +80,6 @@ signals: public slots: - // invalidate the top-level model - // - void invalidate(); - // enable/disable all visible mods // void enableAllVisible(); @@ -108,7 +104,9 @@ protected: // map from/to the view indexes to the model // QModelIndex indexModelToView(const QModelIndex& index) const; + QModelIndexList indexModelToView(const QModelIndexList& index) const; QModelIndex indexViewToModel(const QModelIndex& index) const; + QModelIndexList indexViewToModel(const QModelIndexList& index) const; // returns the next/previous index of the given index // @@ -117,7 +115,7 @@ protected: // all index for the given model under the given index, recursively // - std::vector allIndex( + QModelIndexList 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 -- cgit v1.3.1 From 50a0c95a823dcfa105e4035ffd42e473ef91ec3f Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 15:04:06 +0100 Subject: Remove unused method. --- src/modlist.cpp | 17 ----------------- src/modlist.h | 5 ----- 2 files changed, 22 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index 608a26b4..dc80bb53 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1493,23 +1493,6 @@ QString ModList::getColumnToolTip(int column) const } } -QModelIndex ModList::indexToProxy(QAbstractItemModel* proxyModel, const QModelIndex& index) -{ - if (!proxyModel) { - return QModelIndex(); - } - - if (proxyModel == this) { - return index; - } - - if (auto* proxy = qobject_cast(proxyModel)) { - return proxy->mapFromSource(indexToProxy(proxy->sourceModel(), index)); - } - - return QModelIndex(); -} - void ModList::shiftMods(const QModelIndexList& indices, int offset) { // retrieve the mod index and sort them by priority to avoid issue diff --git a/src/modlist.h b/src/modlist.h index 913d2ea8..405d9e39 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -365,11 +365,6 @@ private: private: - // convert an index of the modlist to an index for the given model, assuming - // the given model is a proxy (of a proxy (of... )) the modlist - // - QModelIndex indexToProxy(QAbstractItemModel* proxyModel, const QModelIndex& index); - // retrieve the relative path of file and its origin given a URL from Mime data // returns an empty optional if the URL is not a valid file for dropping // -- cgit v1.3.1 From eabf64fbc07b457b29aaf5e25fe6e1027b574976 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 16:53:55 +0100 Subject: Clean drag&drop code and add drop of external folder/archives. --- src/modlist.cpp | 270 ++++++++++++++++++++++++----------------- src/modlist.h | 88 ++++++++++++-- src/modlistbypriorityproxy.cpp | 90 +++++++------- src/modlistbypriorityproxy.h | 9 +- src/modlistsortproxy.cpp | 12 +- src/modlistview.cpp | 69 +++++++++-- src/modlistview.h | 6 + src/organizercore.cpp | 142 ++++++++++++---------- src/organizercore.h | 2 + 9 files changed, 442 insertions(+), 246 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index dc80bb53..28e20917 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -61,6 +61,127 @@ along with Mod Organizer. If not, see . using namespace MOBase; +ModList::DropInfo::DropInfo(const QMimeData* mimeData, OrganizerCore& core) : + m_rows{}, m_download{ -1 }, m_localUrls{}, m_url{} +{ + // this only check if the drop is valid, not if the content of the drop + // matches the target, a drop is valid if either + // 1. it contains items from another model (drag&drop in modlist or from download list) + // 2. it contains URLs from MO2 (overwrite or from another mod) + // 3. it contains a single URL to an external folder + // 4. it contains a single URL to a valid archive for MO2 + try { + if (mimeData->hasUrls()) { + for (auto& url : mimeData->urls()) { + auto p = relativeUrl(url); + if (p) { + m_localUrls.push_back(*p); + } + } + + // external drop + if (m_localUrls.empty() && mimeData->urls().size() == 1) { + auto url = mimeData->urls()[0]; + if (url.isLocalFile() && !relativeUrl(url)) { + QFileInfo info(url.toLocalFile()); + if (info.isDir()) { + m_url = url; + } + else if (core.installationManager()->getSupportedExtensions().contains(info.suffix(), Qt::CaseInsensitive)) { + m_url = url; + } + } + } + + } + else if (mimeData->hasText()) { + QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + while (!stream.atEnd()) { + int sourceRow, col; + QMap roleDataMap; + stream >> sourceRow >> col >> roleDataMap; + if (col == 0) { + m_rows.push_back(sourceRow); + } + } + + if (mimeData->text() != "mod") { + if (mimeData->text() == "archive" && m_rows.size() == 1) { + m_download = m_rows[0]; + } + m_rows = {}; + } + } + } + catch (std::exception const&) { + m_rows = {}; + m_download = -1; + m_localUrls.clear(); + m_url = {}; + } +} + +std::optional ModList::DropInfo::relativeUrl(const QUrl& url) const +{ + if (!url.isLocalFile()) { + return {}; + } + + QDir allModsDir(Settings::instance().paths().mods()); + QDir overwriteDir(Settings::instance().paths().overwrite()); + + QFileInfo sourceInfo(url.toLocalFile()); + QString sourceFile = sourceInfo.canonicalFilePath(); + + QString relativePath; + QString originName; + + if (sourceFile.startsWith(allModsDir.canonicalPath())) { + QDir relativeDir(allModsDir.relativeFilePath(sourceFile)); + QStringList splitPath = relativeDir.path().split("/"); + originName = splitPath[0]; + splitPath.pop_front(); + return { { url, splitPath.join("/"), originName } }; + } + else if (sourceFile.startsWith(overwriteDir.canonicalPath())) { + return { { url, overwriteDir.relativeFilePath(sourceFile), ModInfo::getOverwrite()->name() } }; + } + + return {}; +} + +bool ModList::DropInfo::isValid() const +{ + return isLocalFileDrop() || isModDrop() || isDownloadDrop() || m_url.isLocalFile(); +} + +bool ModList::DropInfo::isLocalFileDrop() const +{ + return !m_localUrls.empty(); +} + +bool ModList::DropInfo::isModDrop() const +{ + return !m_rows.empty(); +} + +bool ModList::DropInfo::isDownloadDrop() const +{ + return m_download != -1; +} + +bool ModList::DropInfo::isExternalArchiveDrop() const +{ + return m_url.isLocalFile() && QFileInfo(m_url.toLocalFile()).isFile(); +} + +bool ModList::DropInfo::isExternalFolderDrop() const +{ + return m_url.isLocalFile() && QFileInfo(m_url.toLocalFile()).isDir(); +} + ModList::ModList(PluginContainer *pluginContainer, OrganizerCore *organizer) : QAbstractItemModel(organizer) , m_Organizer(organizer) @@ -1110,53 +1231,12 @@ int ModList::dropPriority(int row, const QModelIndex& parent) const return newPriority; } -std::vector ModList::sourceRows(const QMimeData* mimeData) +ModList::DropInfo ModList::dropInfo(const QMimeData* mimeData) const { - QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); - QDataStream stream(&encoded, QIODevice::ReadOnly); - std::vector sourceRows; - - while (!stream.atEnd()) { - int sourceRow, col; - QMap roleDataMap; - stream >> sourceRow >> col >> roleDataMap; - if (col == 0) { - sourceRows.push_back(sourceRow); - } - } - return sourceRows; + return DropInfo(mimeData, *m_Organizer); } -std::optional> ModList::relativeUrl(const QUrl& url) -{ - if (!url.isLocalFile()) { - return {}; - } - - QDir allModsDir(Settings::instance().paths().mods()); - QDir overwriteDir(Settings::instance().paths().overwrite()); - - QFileInfo sourceInfo(url.toLocalFile()); - QString sourceFile = sourceInfo.canonicalFilePath(); - - QString relativePath; - QString originName; - - if (sourceFile.startsWith(allModsDir.canonicalPath())) { - QDir relativeDir(allModsDir.relativeFilePath(sourceFile)); - QStringList splitPath = relativeDir.path().split("/"); - originName = splitPath[0]; - splitPath.pop_front(); - return { { splitPath.join("/"), originName } }; - } - else if (sourceFile.startsWith(overwriteDir.canonicalPath())) { - return { { overwriteDir.relativeFilePath(sourceFile), ModInfo::getOverwrite()->name() } }; - } - - return {}; -} - -bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent) +bool ModList::dropURLs(const DropInfo& dropInfo, int row, const QModelIndex &parent) { if (row == -1) { row = parent.row(); @@ -1168,23 +1248,15 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa QStringList targetList; QList> relativePathList; - for (auto url : mimeData->urls()) { - auto p = relativeUrl(url); + for (auto localUrl : dropInfo.localUrls()) { - if (!p) { - log::debug("URL drop ignored: \"{}\" is not a local file or not a known file to MO", url.url()); - continue; - } - - auto [relativePath, originName] = *p; - - QFileInfo sourceInfo(url.toLocalFile()); + QFileInfo sourceInfo(localUrl.url.toLocalFile()); QString sourceFile = sourceInfo.canonicalFilePath(); - QFileInfo targetInfo(modDir.absoluteFilePath(relativePath)); + QFileInfo targetInfo(modDir.absoluteFilePath(localUrl.relativePath)); sourceList << sourceFile; targetList << targetInfo.absoluteFilePath(); - relativePathList << QPair(relativePath, originName); + relativePathList << QPair(localUrl.relativePath, localUrl.originName); } if (sourceList.count()) { @@ -1205,47 +1277,9 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa return true; } -bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent) -{ - int newPriority = dropPriority(row, parent); - if (newPriority == -1) { - return false; - } - - try { - std::vector sourceRows = ModList::sourceRows(mimeData); - changeModPriority(sourceRows, newPriority); - - } catch (const std::exception &e) { - reportError(tr("drag&drop failed: %1").arg(e.what())); - } - - return false; -} - -bool ModList::dropArchive(const QMimeData* mimeData, int row, const QModelIndex& parent) -{ - int priority = dropPriority(row, parent); - if (priority == -1) { - return false; - } - - try { - std::vector sourceRows = ModList::sourceRows(mimeData); - if (sourceRows.size() == 1) { - emit downloadArchiveDropped(sourceRows[0], priority); - } - } - catch (const std::exception& e) { - reportError(tr("drag&drop failed: %1").arg(e.what())); - } - - return false; -} - void ModList::onDragEnter(const QMimeData* mimeData) { - m_DropOnMod = mimeData->hasUrls(); + m_DropOnMod = dropInfo(mimeData).isLocalFileDrop(); } bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, int row, int column, const QModelIndex& parent) const @@ -1254,18 +1288,15 @@ bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, return false; } - if (mimeData->hasUrls()) { - for (auto& url : mimeData->urls()) { - if (!relativeUrl(url)) { - return false; - } - } + DropInfo dropInfo(mimeData, *m_Organizer); + + if (dropInfo.isLocalFileDrop()) { if (row == -1 && parent.isValid()) { ModInfo::Ptr modInfo = ModInfo::getByIndex(parent.row()); return modInfo->isRegular() && !modInfo->isSeparator(); } } - else if (mimeData->hasText()) { + else if (dropInfo.isValid()) { // drop on item if (row == -1 && parent.isValid()) { return true; @@ -1288,16 +1319,35 @@ bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int return true; } - if (m_Profile == nullptr) return false; + DropInfo dropInfo(mimeData, *m_Organizer); - if (mimeData->hasUrls()) { - return dropURLs(mimeData, row, parent); - } else if (mimeData->hasText()) { - if (mimeData->text() == "mod") { - return dropMod(mimeData, row, parent); + if (!m_Profile || !dropInfo.isValid()) { + return false; + } + + int dropPriority = this->dropPriority(row, parent); + if (dropPriority == -1) { + return false; + } + + if (dropInfo.isLocalFileDrop()) { + return dropURLs(dropInfo, row, parent); + } + else { + if (dropInfo.isModDrop()) { + changeModPriority(dropInfo.rows(), dropPriority); + } + else if (dropInfo.isDownloadDrop()) { + emit downloadArchiveDropped(dropInfo.download(), dropPriority); } - else if (mimeData->text() == "archive") { - return dropArchive(mimeData, row, parent); + else if (dropInfo.isExternalArchiveDrop()) { + emit externalArchiveDropped(dropInfo.externalUrl(), dropPriority); + } + else if (dropInfo.isExternalFolderDrop()) { + emit externalFolderDropped(dropInfo.externalUrl(), dropPriority); + } + else { + return false; } } return false; diff --git a/src/modlist.h b/src/modlist.h index 405d9e39..815024b9 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -319,8 +319,17 @@ signals: // emitted when an item is dropped from the download list, the row is from the // download list + // void downloadArchiveDropped(int row, int priority); + // emitted when an external archive is dropped on the mod list + // + void externalArchiveDropped(const QUrl& url, int priority); + + // emitted when an external folder is dropped on the mod list + // + void externalFolderDropped(const QUrl& url, int priority); + private: QVariant getOverwriteData(int column, int role) const; @@ -365,19 +374,79 @@ private: private: - // retrieve the relative path of file and its origin given a URL from Mime data - // returns an empty optional if the URL is not a valid file for dropping + // small class that extract information from mimeData // - static std::optional> relativeUrl(const QUrl&); + class DropInfo { + public: + + struct RelativeUrl { + const QUrl url; + const QString relativePath; + const QString originName; + }; + + // returns true if this drop is valid + // + bool isValid() const; + + // returns true if these data corresponds to drag&drop + // of local files (e.g. from overwrite) + // + bool isLocalFileDrop() const; + + // returns true if these data corresponds to drag&drop + // of mod in the list + // + bool isModDrop() const; + + // returns true if these data corresponds to drag&drop + // from the download list + // + bool isDownloadDrop() const; + + // returns true if these data corresponds to dropping + // an archive for installation + // + bool isExternalArchiveDrop() const; + + // returns true if these data corresponds to dropping + // a folder for copy + // + bool isExternalFolderDrop() const; + + const auto& rows() const { return m_rows; } + const auto& download() const { return m_download; } + const auto& localUrls() const { return m_localUrls; } + const auto& externalUrl() const { return m_url; } + + private: + + friend class ModList; + + // retrieve the relative path of file and its origin given a URL from Mime data + // returns an empty optional if the URL is not a valid file for dropping + // + std::optional relativeUrl(const QUrl&) const; + + private: + DropInfo(const QMimeData* mimeData, OrganizerCore& core); + + // rows for drag&drop between views + std::vector m_rows; + int m_download; // -1 if invalid + + // local URLs from the data (relative path + origin name) + std::vector m_localUrls; + + // external URL + QUrl m_url; + }; - // return the source rows from the given mime data for drag&drop of mods or - // installation archives + // create a DropInfo object from the given data // - static std::vector sourceRows(const QMimeData* mimeData); + DropInfo dropInfo(const QMimeData* mimeData) const; - bool dropURLs(const QMimeData* mimeData, int row, const QModelIndex& parent); - bool dropMod(const QMimeData* mimeData, int row, const QModelIndex& parent); - bool dropArchive(const QMimeData* mimeData, int row, const QModelIndex& parent); + bool dropURLs(const DropInfo& dropInfo, int row, const QModelIndex& parent); // return the priority of the mod for a drop event // @@ -386,6 +455,7 @@ private: private: friend class ModListProxy; + friend class ModListSortProxy; friend class ModListByPriorityProxy; OrganizerCore *m_Organizer; diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index dc16d8ea..b1426422 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -2,11 +2,12 @@ #include "modinfo.h" #include "profile.h" +#include "organizercore.h" #include "modlist.h" #include "log.h" -ModListByPriorityProxy::ModListByPriorityProxy(Profile* profile, QObject* parent) : - QAbstractProxyModel(parent), m_Profile(profile) +ModListByPriorityProxy::ModListByPriorityProxy(Profile* profile, OrganizerCore& core, QObject* parent) : + QAbstractProxyModel(parent), m_core(core), m_profile(profile) { } @@ -33,6 +34,11 @@ void ModListByPriorityProxy::refresh() buildTree(); } +void ModListByPriorityProxy::setProfile(Profile* profile) +{ + m_profile = profile; +} + void ModListByPriorityProxy::buildTree() { if (!sourceModel()) return; @@ -46,7 +52,7 @@ void ModListByPriorityProxy::buildTree() TreeItem* root = &m_Root; std::unique_ptr overwrite; std::vector> backups; - for (auto& [priority, index] : m_Profile->getAllIndexesByPriority()) { + for (auto& [priority, index] : m_profile->getAllIndexesByPriority()) { ModInfo::Ptr modInfo = ModInfo::getByIndex(index); TreeItem* item; @@ -189,51 +195,48 @@ bool ModListByPriorityProxy::setData(const QModelIndex& index, const QVariant& v bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const { - std::vector sourceRows; - bool hasSeparator = false; - bool firstRowSeparator = false; - - if (data->hasText()) { - - try { - int firstRowPriority = INT_MAX; - unsigned int firstRowIndex = -1; - - sourceRows = ModList::sourceRows(data); - for (auto sourceRow : sourceRows) { - hasSeparator = hasSeparator || ModInfo::getByIndex(sourceRow)->isSeparator(); - if (m_Profile->getModPriority(sourceRow) < firstRowPriority) { - firstRowIndex = sourceRow; - firstRowPriority = m_Profile->getModPriority(sourceRow); - } - } + auto dropInfo = m_core.modList()->dropInfo(data); - firstRowSeparator = firstRowIndex != -1 && ModInfo::getByIndex(firstRowIndex)->isSeparator(); - } - catch (std::exception const&) { - - } + if (!dropInfo.isValid() || dropInfo.isLocalFileDrop()) { + return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); } - // row = -1 and invalid parent means we're dropping onto an item, we don't want to drop - // separators onto items or items into their own separator - if (row == -1 && parent.isValid()) { - auto* parentItem = static_cast(parent.internalPointer()); - if (hasSeparator) { - return !parentItem->mod->isSeparator(); + if (dropInfo.isModDrop()) { + + bool hasSeparator = false; + unsigned int firstRowIndex = -1; + + int firstRowPriority = INT_MAX; + for (auto sourceRow : dropInfo.rows()) { + hasSeparator = hasSeparator || ModInfo::getByIndex(sourceRow)->isSeparator(); + if (m_profile->getModPriority(sourceRow) < firstRowPriority) { + firstRowIndex = sourceRow; + firstRowPriority = m_profile->getModPriority(sourceRow); + } } - for (auto row : sourceRows) { - auto it = m_IndexToItem.find(row); - if (it != m_IndexToItem.end() && it->second->parent == parentItem) { - return false; + bool firstRowSeparator = firstRowIndex != -1 && ModInfo::getByIndex(firstRowIndex)->isSeparator(); + + // row = -1 and invalid parent means we're dropping onto an item, we don't want to drop + // separators onto items or items into their own separator + if (row == -1 && parent.isValid()) { + auto* parentItem = static_cast(parent.internalPointer()); + if (hasSeparator) { + return !parentItem->mod->isSeparator(); + } + + for (auto row : dropInfo.rows()) { + auto it = m_IndexToItem.find(row); + if (it != m_IndexToItem.end() && it->second->parent == parentItem) { + return false; + } } } - } - // first row is a separator, we can drop it anywhere - if (firstRowSeparator) { - return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); + // first row is a separator, we can drop it anywhere + if (firstRowSeparator) { + return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); + } } // top-level drop is disabled unless it's before the first separator @@ -262,9 +265,10 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { // we need to fix the source model row + auto dropInfo = m_core.modList()->dropInfo(data); int sourceRow = -1; - if (data->hasUrls()) { + if (dropInfo.isLocalFileDrop()) { if (parent.isValid()) { sourceRow = static_cast(parent.internalPointer())->index; } @@ -277,7 +281,7 @@ bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction if (row > 0 && m_Root.children[row - 1]->mod->isSeparator() && !m_Root.children[row - 1]->children.empty() - && m_DropPosition == ModListView::DropPosition::BelowItem) { + && m_dropPosition == ModListView::DropPosition::BelowItem) { sourceRow = m_Root.children[row - 1]->children[0]->index; } } @@ -324,7 +328,7 @@ QModelIndex ModListByPriorityProxy::index(int row, int column, const QModelIndex void ModListByPriorityProxy::onDropEnter(const QMimeData*, ModListView::DropPosition dropPosition) { - m_DropPosition = dropPosition; + m_dropPosition = dropPosition; } void ModListByPriorityProxy::refreshExpandedItems() const diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h index 00848e2a..e5a8adff 100644 --- a/src/modlistbypriorityproxy.h +++ b/src/modlistbypriorityproxy.h @@ -23,10 +23,10 @@ class ModListByPriorityProxy : public QAbstractProxyModel Q_OBJECT public: - explicit ModListByPriorityProxy(Profile* profile, QObject* parent = nullptr); + explicit ModListByPriorityProxy(Profile* profile, OrganizerCore& core, QObject* parent = nullptr); ~ModListByPriorityProxy(); - void setProfile(Profile* profile) { m_Profile = profile; } + void setProfile(Profile* profile); void refresh(); void setSourceModel(QAbstractItemModel* sourceModel) override; @@ -92,8 +92,9 @@ private: std::set m_CollapsedItems; private: - Profile* m_Profile; - ModListView::DropPosition m_DropPosition = ModListView::DropPosition::OnItem; + OrganizerCore& m_core; + Profile* m_profile; + ModListView::DropPosition m_dropPosition = ModListView::DropPosition::OnItem; }; #endif //GROUPINGPROXY_H diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 93f97895..d1ca6d0c 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -611,11 +611,13 @@ bool ModListSortProxy::canDropMimeData(const QMimeData* data, Qt::DropAction act return false; } + auto dropInfo = m_Organizer->modList()->dropInfo(data); + // disable drop install with group proxy, except the one for collapsible separator // - it would be nice to be able to "install to category" or something like that but // it's a bit more complicated since the drop position is based on the category, so // just disabling for now - if (data->hasText() && data->text() == "archive") { + if (dropInfo.isDownloadDrop()) { // maybe there is a cleaner way? if (qobject_cast(sourceModel())) { return false; @@ -628,14 +630,18 @@ bool ModListSortProxy::canDropMimeData(const QMimeData* data, Qt::DropAction act bool ModListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { - if (!data->hasUrls() && (sortColumn() != ModList::COL_PRIORITY)) { + auto dropInfo = m_Organizer->modList()->dropInfo(data); + + if (!dropInfo.isLocalFileDrop() && sortColumn() != ModList::COL_PRIORITY) { QWidget *wid = qApp->activeWindow()->findChild("modList"); MessageDialog::showMessage(tr("Drag&Drop is only supported when sorting by priority"), wid); return false; } - if ((row == -1) && (column == -1)) { + + if (row == -1 && column == -1) { return sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent)); } + // in the regular model, when dropping between rows, the row-value passed to // the sourceModel is inconsistent between ascending and descending ordering. // This should fix that diff --git a/src/modlistview.cpp b/src/modlistview.cpp index c4641934..02b7384a 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "ui_mainwindow.h" @@ -20,6 +21,8 @@ #include "shared/directoryentry.h" #include "shared/filesorigin.h" +using namespace MOBase; + class ModListProxyStyle : public QProxyStyle { public: @@ -70,6 +73,11 @@ public: ModListView::ModListView(QWidget* parent) : QTreeView(parent) + , m_core(nullptr) + , m_sortProxy(nullptr) + , m_byPriorityProxy(nullptr) + , m_byCategoryProxy(nullptr) + , m_byNexusIdProxy(nullptr) , m_scrollbar(new ViewMarkingScrollBar(this->model(), this)) { setVerticalScrollBar(m_scrollbar); @@ -547,7 +555,7 @@ void ModListView::setup(OrganizerCore& core, Ui::MainWindow* mwui) 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 = new ModListByPriorityProxy(core.currentProfile(), core, this); m_byPriorityProxy->setSourceModel(core.modList()); connect(this, &QTreeView::expanded, m_byPriorityProxy, &ModListByPriorityProxy::expanded); connect(this, &QTreeView::collapsed, m_byPriorityProxy, &ModListByPriorityProxy::collapsed); @@ -629,9 +637,13 @@ void ModListView::setup(OrganizerCore& core, Ui::MainWindow* mwui) // prevent the name-column from being hidden header()->setSectionHidden(ModList::COL_NAME, false); - connect(m_core->modList(), &ModList::downloadArchiveDropped, this, [this](int row, int priority) { + connect(m_core->modList(), &ModList::downloadArchiveDropped, [=](int row, int priority) { m_core->installDownload(row, priority); }); + connect(m_core->modList(), &ModList::externalArchiveDropped, [=](const QUrl& url, int priority) { + m_core->installArchive(url.toLocalFile(), priority, false, nullptr); + }); + connect(m_core->modList(), &ModList::externalFolderDropped, this, &ModListView::onExternalFolderDropped); connect(m_sortProxy, &ModListSortProxy::filterActive, this, &ModListView::onModFilterActive); connect(ui.filter, &QLineEdit::textChanged, m_sortProxy, &ModListSortProxy::updateFilter); @@ -710,13 +722,50 @@ void ModListView::timerEvent(QTimerEvent* event) } } +void ModListView::onExternalFolderDropped(const QUrl& url, int priority) +{ + QFileInfo fileInfo(url.toLocalFile()); + + GuessedValue name; + name.setFilter(&fixDirectoryName); + name.update(fileInfo.fileName(), GUESS_PRESET); + + do { + bool ok; + name.update(QInputDialog::getText(this, tr("Copy Folder..."), + tr("This will copy the content of %1 to a new mod.\n" + "Please enter the name:").arg(fileInfo.fileName()), QLineEdit::Normal, name, &ok), + GUESS_USER); + if (!ok) { + return; + } + } while (name->isEmpty()); + + if (m_core->modList()->getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists.")); + return; + } + + IModInterface* newMod = m_core->createMod(name); + if (!newMod) { + return; + } + + if (!copyDir(fileInfo.absoluteFilePath(), newMod->absolutePath(), true)) { + return; + } + + m_core->refresh(); + + if (priority != -1) { + m_core->modList()->changeModPriority(ModInfo::getIndex(name), priority); + } +} + bool ModListView::moveSelection(int key) { QModelIndex cindex = indexViewToModel(currentIndex()); - QModelIndexList sourceRows; - for (auto& index : selectionModel()->selectedRows()) { - sourceRows.append(indexViewToModel(index)); - } + QModelIndexList sourceRows = indexViewToModel(selectionModel()->selectedRows()); int offset = key == Qt::Key_Up ? -1 : 1; if (m_sortProxy->sortOrder() == Qt::DescendingOrder) { @@ -755,13 +804,7 @@ 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); + return m_core->modList()->toggleState(indexViewToModel(selectionModel()->selectedRows())); } bool ModListView::event(QEvent* event) diff --git a/src/modlistview.h b/src/modlistview.h index 1d8a9b49..8e37161b 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -123,6 +123,12 @@ protected: // QModelIndexList selectedIndexes() const; + // drop from external folder + // + void onExternalFolderDropped(const QUrl& url, int priority); + + // method to react to various key events + // bool moveSelection(int key); bool removeSelection(); bool toggleSelectionState(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 334a02de..9e3528ef 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -756,74 +756,12 @@ QString OrganizerCore::pluginDataPath() + "/data"; } -MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, +MOBase::IModInterface *OrganizerCore::installMod(const QString & archivePath, bool reinstallation, ModInfo::Ptr currentMod, const QString &initModName) { - if (m_CurrentProfile == nullptr) { - return nullptr; - } - - if (m_InstallationManager.isRunning()) { - QMessageBox::information( - qApp->activeWindow(), tr("Installation cancelled"), - tr("Another installation is currently in progress."), QMessageBox::Ok); - return nullptr; - } - - bool hasIniTweaks = false; - GuessedValue modName; - if (!initModName.isEmpty()) { - modName.update(initModName, GUESS_USER); - } - m_CurrentProfile->writeModlistNow(); - m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); - m_InstallationManager.notifyInstallationStart(fileName, reinstallation, currentMod); - auto result = m_InstallationManager.install(fileName, modName, hasIniTweaks); - if (result) { - MessageDialog::showMessage(tr("Installation successful"), - qApp->activeWindow()); - refresh(); - - int modIndex = ModInfo::getIndex(modName); - if (modIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - auto dlIdx = m_DownloadManager.indexByName(QFileInfo(fileName).fileName()); - if (dlIdx != -1) { - int modId = m_DownloadManager.getModID(dlIdx); - int fileId = m_DownloadManager.getFileInfo(dlIdx)->fileID; - modInfo->addInstalledFile(modId, fileId); - } - if (hasIniTweaks && (m_UserInterface != nullptr) - && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), - tr("This mod contains ini tweaks. Do you " - "want to configure them now?"), - QMessageBox::Yes | QMessageBox::No) - == QMessageBox::Yes)) { - m_UserInterface->displayModInformation( - modInfo, modIndex, ModInfoTabIDs::IniFiles); - } - m_ModList.notifyModInstalled(modInfo.get()); - m_DownloadManager.markInstalled(fileName); - m_InstallationManager.notifyInstallationEnd(result, modInfo); - emit modInstalled(modName); - return modInfo.data(); - } else { - reportError(tr("mod not found: %1").arg(qUtf8Printable(modName))); - } - } else { - m_InstallationManager.notifyInstallationEnd(result, nullptr); - if (m_InstallationManager.wasCancelled()) { - QMessageBox::information(qApp->activeWindow(), tr("Extraction cancelled"), - tr("The installation was cancelled while extracting files. " - "If this was prior to a FOMOD setup, this warning may be ignored. " - "However, if this was during installation, the mod will likely be missing files."), - QMessageBox::Ok); - refresh(); - } - } - return nullptr; + return installArchive(archivePath, -1, reinstallation, currentMod, initModName).get(); } ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) @@ -915,6 +853,82 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) return nullptr; } +ModInfo::Ptr OrganizerCore::installArchive( + const QString& archivePath, int priority, bool reinstallation, + ModInfo::Ptr currentMod, const QString& initModName) +{ + if (m_CurrentProfile == nullptr) { + return nullptr; + } + + if (m_InstallationManager.isRunning()) { + QMessageBox::information( + qApp->activeWindow(), tr("Installation cancelled"), + tr("Another installation is currently in progress."), QMessageBox::Ok); + return nullptr; + } + + bool hasIniTweaks = false; + GuessedValue modName; + if (!initModName.isEmpty()) { + modName.update(initModName, GUESS_USER); + } + m_CurrentProfile->writeModlistNow(); + m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); + m_InstallationManager.notifyInstallationStart(archivePath, reinstallation, currentMod); + auto result = m_InstallationManager.install(archivePath, modName, hasIniTweaks); + if (result) { + MessageDialog::showMessage(tr("Installation successful"), + qApp->activeWindow()); + refresh(); + + int modIndex = ModInfo::getIndex(modName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + auto dlIdx = m_DownloadManager.indexByName(QFileInfo(archivePath).fileName()); + if (dlIdx != -1) { + int modId = m_DownloadManager.getModID(dlIdx); + int fileId = m_DownloadManager.getFileInfo(dlIdx)->fileID; + modInfo->addInstalledFile(modId, fileId); + } + + if (priority != -1 && !result.mergedOrReplaced()) { + m_ModList.changeModPriority(modIndex, priority); + } + + if (hasIniTweaks && (m_UserInterface != nullptr) + && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), + tr("This mod contains ini tweaks. Do you " + "want to configure them now?"), + QMessageBox::Yes | QMessageBox::No) + == QMessageBox::Yes)) { + m_UserInterface->displayModInformation( + modInfo, modIndex, ModInfoTabIDs::IniFiles); + } + m_ModList.notifyModInstalled(modInfo.get()); + m_DownloadManager.markInstalled(archivePath); + m_InstallationManager.notifyInstallationEnd(result, modInfo); + emit modInstalled(modName); + return modInfo; + } + else { + reportError(tr("mod not found: %1").arg(qUtf8Printable(modName))); + } + } + else { + m_InstallationManager.notifyInstallationEnd(result, nullptr); + if (m_InstallationManager.wasCancelled()) { + QMessageBox::information(qApp->activeWindow(), tr("Extraction cancelled"), + tr("The installation was cancelled while extracting files. " + "If this was prior to a FOMOD setup, this warning may be ignored. " + "However, if this was during installation, the mod will likely be missing files."), + QMessageBox::Ok); + refresh(); + } + } + return nullptr; +} + QString OrganizerCore::resolvePath(const QString &fileName) const { if (m_DirectoryStructure == nullptr) { diff --git a/src/organizercore.h b/src/organizercore.h index cb577437..3fb4f20e 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -377,6 +377,8 @@ public slots: void refreshLists(); ModInfo::Ptr installDownload(int downloadIndex, int priority = -1); + ModInfo::Ptr installArchive(const QString& archivePath, int priority = -1, bool reinstallation = false, + ModInfo::Ptr currentMod = nullptr, const QString& modName = QString()); void modStatusChanged(unsigned int index); void modStatusChanged(QList index); -- cgit v1.3.1 From 89ff59da4613f06a05a633b4aa4387470831ce94 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 19:11:48 +0100 Subject: Add message for invalid drag. Split & clean code. --- src/CMakeLists.txt | 1 + src/downloadlist.cpp | 3 +- src/modlist.cpp | 140 ++------------------------------ src/modlist.h | 93 +++------------------ src/modlistbypriorityproxy.cpp | 5 +- src/modlistdropinfo.cpp | 124 ++++++++++++++++++++++++++++ src/modlistdropinfo.h | 92 +++++++++++++++++++++ src/modlistsortproxy.cpp | 5 +- src/modlistview.cpp | 179 ++++++++++++++++++++++------------------- 9 files changed, 335 insertions(+), 307 deletions(-) create mode 100644 src/modlistdropinfo.cpp create mode 100644 src/modlistdropinfo.h (limited to 'src/modlist.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a913649..93b9e768 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -134,6 +134,7 @@ add_filter(NAME src/modinfo/dialog GROUPS ) add_filter(NAME src/modlist GROUPS modlist + modlistdropinfo modlistsortproxy modlistbypriorityproxy modlistview diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp index 78e5fd24..93f8538c 100644 --- a/src/downloadlist.cpp +++ b/src/downloadlist.cpp @@ -19,6 +19,7 @@ along with Mod Organizer. If not, see . #include "downloadlist.h" #include "downloadmanager.h" +#include "modlistdropinfo.h" #include #include #include @@ -98,7 +99,7 @@ Qt::ItemFlags DownloadList::flags(const QModelIndex& idx) const QMimeData* DownloadList::mimeData(const QModelIndexList& indexes) const { QMimeData* result = QAbstractItemModel::mimeData(indexes); - result->setData("text/plain", "archive"); + result->setData("text/plain", ModListDropInfo::DOWNLOAD_TEXT); return result; } diff --git a/src/modlist.cpp b/src/modlist.cpp index 28e20917..7da6fe3b 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -28,6 +28,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "organizercore.h" #include "modinforegular.h" +#include "modlistdropinfo.h" #include "shared/directoryentry.h" #include "shared/fileentry.h" #include "shared/filesorigin.h" @@ -60,128 +61,6 @@ along with Mod Organizer. If not, see . using namespace MOBase; - -ModList::DropInfo::DropInfo(const QMimeData* mimeData, OrganizerCore& core) : - m_rows{}, m_download{ -1 }, m_localUrls{}, m_url{} -{ - // this only check if the drop is valid, not if the content of the drop - // matches the target, a drop is valid if either - // 1. it contains items from another model (drag&drop in modlist or from download list) - // 2. it contains URLs from MO2 (overwrite or from another mod) - // 3. it contains a single URL to an external folder - // 4. it contains a single URL to a valid archive for MO2 - try { - if (mimeData->hasUrls()) { - for (auto& url : mimeData->urls()) { - auto p = relativeUrl(url); - if (p) { - m_localUrls.push_back(*p); - } - } - - // external drop - if (m_localUrls.empty() && mimeData->urls().size() == 1) { - auto url = mimeData->urls()[0]; - if (url.isLocalFile() && !relativeUrl(url)) { - QFileInfo info(url.toLocalFile()); - if (info.isDir()) { - m_url = url; - } - else if (core.installationManager()->getSupportedExtensions().contains(info.suffix(), Qt::CaseInsensitive)) { - m_url = url; - } - } - } - - } - else if (mimeData->hasText()) { - QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); - QDataStream stream(&encoded, QIODevice::ReadOnly); - - while (!stream.atEnd()) { - int sourceRow, col; - QMap roleDataMap; - stream >> sourceRow >> col >> roleDataMap; - if (col == 0) { - m_rows.push_back(sourceRow); - } - } - - if (mimeData->text() != "mod") { - if (mimeData->text() == "archive" && m_rows.size() == 1) { - m_download = m_rows[0]; - } - m_rows = {}; - } - } - } - catch (std::exception const&) { - m_rows = {}; - m_download = -1; - m_localUrls.clear(); - m_url = {}; - } -} - -std::optional ModList::DropInfo::relativeUrl(const QUrl& url) const -{ - if (!url.isLocalFile()) { - return {}; - } - - QDir allModsDir(Settings::instance().paths().mods()); - QDir overwriteDir(Settings::instance().paths().overwrite()); - - QFileInfo sourceInfo(url.toLocalFile()); - QString sourceFile = sourceInfo.canonicalFilePath(); - - QString relativePath; - QString originName; - - if (sourceFile.startsWith(allModsDir.canonicalPath())) { - QDir relativeDir(allModsDir.relativeFilePath(sourceFile)); - QStringList splitPath = relativeDir.path().split("/"); - originName = splitPath[0]; - splitPath.pop_front(); - return { { url, splitPath.join("/"), originName } }; - } - else if (sourceFile.startsWith(overwriteDir.canonicalPath())) { - return { { url, overwriteDir.relativeFilePath(sourceFile), ModInfo::getOverwrite()->name() } }; - } - - return {}; -} - -bool ModList::DropInfo::isValid() const -{ - return isLocalFileDrop() || isModDrop() || isDownloadDrop() || m_url.isLocalFile(); -} - -bool ModList::DropInfo::isLocalFileDrop() const -{ - return !m_localUrls.empty(); -} - -bool ModList::DropInfo::isModDrop() const -{ - return !m_rows.empty(); -} - -bool ModList::DropInfo::isDownloadDrop() const -{ - return m_download != -1; -} - -bool ModList::DropInfo::isExternalArchiveDrop() const -{ - return m_url.isLocalFile() && QFileInfo(m_url.toLocalFile()).isFile(); -} - -bool ModList::DropInfo::isExternalFolderDrop() const -{ - return m_url.isLocalFile() && QFileInfo(m_url.toLocalFile()).isDir(); -} - ModList::ModList(PluginContainer *pluginContainer, OrganizerCore *organizer) : QAbstractItemModel(organizer) , m_Organizer(organizer) @@ -829,7 +708,7 @@ QStringList ModList::mimeTypes() const QMimeData *ModList::mimeData(const QModelIndexList &indexes) const { QMimeData *result = QAbstractItemModel::mimeData(indexes); - result->setData("text/plain", "mod"); + result->setData("text/plain", ModListDropInfo::MOD_TEXT); return result; } @@ -1231,12 +1110,7 @@ int ModList::dropPriority(int row, const QModelIndex& parent) const return newPriority; } -ModList::DropInfo ModList::dropInfo(const QMimeData* mimeData) const -{ - return DropInfo(mimeData, *m_Organizer); -} - -bool ModList::dropURLs(const DropInfo& dropInfo, int row, const QModelIndex &parent) +bool ModList::dropLocalFiles(const ModListDropInfo& dropInfo, int row, const QModelIndex &parent) { if (row == -1) { row = parent.row(); @@ -1279,7 +1153,7 @@ bool ModList::dropURLs(const DropInfo& dropInfo, int row, const QModelIndex &par void ModList::onDragEnter(const QMimeData* mimeData) { - m_DropOnMod = dropInfo(mimeData).isLocalFileDrop(); + m_DropOnMod = ModListDropInfo(mimeData, *m_Organizer).isLocalFileDrop(); } bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, int row, int column, const QModelIndex& parent) const @@ -1288,7 +1162,7 @@ bool ModList::canDropMimeData(const QMimeData* mimeData, Qt::DropAction action, return false; } - DropInfo dropInfo(mimeData, *m_Organizer); + ModListDropInfo dropInfo(mimeData, *m_Organizer); if (dropInfo.isLocalFileDrop()) { if (row == -1 && parent.isValid()) { @@ -1319,7 +1193,7 @@ bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int return true; } - DropInfo dropInfo(mimeData, *m_Organizer); + ModListDropInfo dropInfo(mimeData, *m_Organizer); if (!m_Profile || !dropInfo.isValid()) { return false; @@ -1331,7 +1205,7 @@ bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int } if (dropInfo.isLocalFileDrop()) { - return dropURLs(dropInfo, row, parent); + return dropLocalFiles(dropInfo, row, parent); } else { if (dropInfo.isModDrop()) { diff --git a/src/modlist.h b/src/modlist.h index 815024b9..870978f9 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -43,6 +43,7 @@ along with Mod Organizer. If not, see . class QSortFilterProxyModel; class PluginContainer; class OrganizerCore; +class ModListDropInfo; /** * Model presenting an overview of the installed mod @@ -353,6 +354,14 @@ private: MOBase::IModList::ModStates state(unsigned int modIndex) const; + // handle dropping of local URLs files + // + bool dropLocalFiles(const ModListDropInfo& dropInfo, int row, const QModelIndex& parent); + + // return the priority of the mod for a drop event + // + int dropPriority(int row, const QModelIndex& parent) const; + private slots: private: @@ -374,90 +383,6 @@ private: private: - // small class that extract information from mimeData - // - class DropInfo { - public: - - struct RelativeUrl { - const QUrl url; - const QString relativePath; - const QString originName; - }; - - // returns true if this drop is valid - // - bool isValid() const; - - // returns true if these data corresponds to drag&drop - // of local files (e.g. from overwrite) - // - bool isLocalFileDrop() const; - - // returns true if these data corresponds to drag&drop - // of mod in the list - // - bool isModDrop() const; - - // returns true if these data corresponds to drag&drop - // from the download list - // - bool isDownloadDrop() const; - - // returns true if these data corresponds to dropping - // an archive for installation - // - bool isExternalArchiveDrop() const; - - // returns true if these data corresponds to dropping - // a folder for copy - // - bool isExternalFolderDrop() const; - - const auto& rows() const { return m_rows; } - const auto& download() const { return m_download; } - const auto& localUrls() const { return m_localUrls; } - const auto& externalUrl() const { return m_url; } - - private: - - friend class ModList; - - // retrieve the relative path of file and its origin given a URL from Mime data - // returns an empty optional if the URL is not a valid file for dropping - // - std::optional relativeUrl(const QUrl&) const; - - private: - DropInfo(const QMimeData* mimeData, OrganizerCore& core); - - // rows for drag&drop between views - std::vector m_rows; - int m_download; // -1 if invalid - - // local URLs from the data (relative path + origin name) - std::vector m_localUrls; - - // external URL - QUrl m_url; - }; - - // create a DropInfo object from the given data - // - DropInfo dropInfo(const QMimeData* mimeData) const; - - bool dropURLs(const DropInfo& dropInfo, int row, const QModelIndex& parent); - - // return the priority of the mod for a drop event - // - int dropPriority(int row, const QModelIndex& parent) const; - -private: - - friend class ModListProxy; - friend class ModListSortProxy; - friend class ModListByPriorityProxy; - OrganizerCore *m_Organizer; Profile *m_Profile; diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index b1426422..749991d0 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -4,6 +4,7 @@ #include "profile.h" #include "organizercore.h" #include "modlist.h" +#include "modlistdropinfo.h" #include "log.h" ModListByPriorityProxy::ModListByPriorityProxy(Profile* profile, OrganizerCore& core, QObject* parent) : @@ -195,7 +196,7 @@ bool ModListByPriorityProxy::setData(const QModelIndex& index, const QVariant& v bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const { - auto dropInfo = m_core.modList()->dropInfo(data); + ModListDropInfo dropInfo(data, m_core); if (!dropInfo.isValid() || dropInfo.isLocalFileDrop()) { return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); @@ -265,7 +266,7 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { // we need to fix the source model row - auto dropInfo = m_core.modList()->dropInfo(data); + ModListDropInfo dropInfo(data, m_core); int sourceRow = -1; if (dropInfo.isLocalFileDrop()) { diff --git a/src/modlistdropinfo.cpp b/src/modlistdropinfo.cpp new file mode 100644 index 00000000..62a103a4 --- /dev/null +++ b/src/modlistdropinfo.cpp @@ -0,0 +1,124 @@ +#include "modlistdropinfo.h" + +#include "organizercore.h" + +ModListDropInfo::ModListDropInfo(const QMimeData* mimeData, OrganizerCore& core) : + m_rows{}, m_download{ -1 }, m_localUrls{}, m_url{} +{ + // this only check if the drop is valid, not if the content of the drop + // matches the target, a drop is valid if either + // 1. it contains items from another model (drag&drop in modlist or from download list) + // 2. it contains URLs from MO2 (overwrite or from another mod) + // 3. it contains a single URL to an external folder + // 4. it contains a single URL to a valid archive for MO2 + try { + if (mimeData->hasUrls()) { + for (auto& url : mimeData->urls()) { + auto p = relativeUrl(url); + if (p) { + m_localUrls.push_back(*p); + } + } + + // external drop + if (m_localUrls.empty() && mimeData->urls().size() == 1) { + auto url = mimeData->urls()[0]; + if (url.isLocalFile() && !relativeUrl(url)) { + QFileInfo info(url.toLocalFile()); + if (info.isDir()) { + m_url = url; + } + else if (core.installationManager()->getSupportedExtensions().contains(info.suffix(), Qt::CaseInsensitive)) { + m_url = url; + } + } + } + + } + else if (mimeData->hasText()) { + QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist"); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + while (!stream.atEnd()) { + int sourceRow, col; + QMap roleDataMap; + stream >> sourceRow >> col >> roleDataMap; + if (col == 0) { + m_rows.push_back(sourceRow); + } + } + + if (mimeData->text() != ModListDropInfo::MOD_TEXT) { + if (mimeData->text() == ModListDropInfo::DOWNLOAD_TEXT && m_rows.size() == 1) { + m_download = m_rows[0]; + } + m_rows = {}; + } + } + } + catch (std::exception const&) { + m_rows = {}; + m_download = -1; + m_localUrls.clear(); + m_url = {}; + } +} + +std::optional ModListDropInfo::relativeUrl(const QUrl& url) const +{ + if (!url.isLocalFile()) { + return {}; + } + + QDir allModsDir(Settings::instance().paths().mods()); + QDir overwriteDir(Settings::instance().paths().overwrite()); + + QFileInfo sourceInfo(url.toLocalFile()); + QString sourceFile = sourceInfo.canonicalFilePath(); + + QString relativePath; + QString originName; + + if (sourceFile.startsWith(allModsDir.canonicalPath())) { + QDir relativeDir(allModsDir.relativeFilePath(sourceFile)); + QStringList splitPath = relativeDir.path().split("/"); + originName = splitPath[0]; + splitPath.pop_front(); + return { { url, splitPath.join("/"), originName } }; + } + else if (sourceFile.startsWith(overwriteDir.canonicalPath())) { + return { { url, overwriteDir.relativeFilePath(sourceFile), ModInfo::getOverwrite()->name() } }; + } + + return {}; +} + +bool ModListDropInfo::isValid() const +{ + return isLocalFileDrop() || isModDrop() || isDownloadDrop() || m_url.isLocalFile(); +} + +bool ModListDropInfo::isLocalFileDrop() const +{ + return !m_localUrls.empty(); +} + +bool ModListDropInfo::isModDrop() const +{ + return !m_rows.empty(); +} + +bool ModListDropInfo::isDownloadDrop() const +{ + return m_download != -1; +} + +bool ModListDropInfo::isExternalArchiveDrop() const +{ + return m_url.isLocalFile() && QFileInfo(m_url.toLocalFile()).isFile(); +} + +bool ModListDropInfo::isExternalFolderDrop() const +{ + return m_url.isLocalFile() && QFileInfo(m_url.toLocalFile()).isDir(); +} diff --git a/src/modlistdropinfo.h b/src/modlistdropinfo.h new file mode 100644 index 00000000..14a21691 --- /dev/null +++ b/src/modlistdropinfo.h @@ -0,0 +1,92 @@ +#ifndef MODLISTDROPINFO_H +#define MODLISTDROPINFO_H + +#include +#include + +#include +#include +#include + +class OrganizerCore; + +// small class that extract information from mimeData +// +class ModListDropInfo { +public: + + // text value for the mime-data for the various possible + // origin (not for external drops) + // + static constexpr const char* MOD_TEXT = "mod"; + static constexpr const char* DOWNLOAD_TEXT = "download"; + +public: + + struct RelativeUrl { + const QUrl url; + const QString relativePath; + const QString originName; + }; + +public: + + ModListDropInfo(const QMimeData* mimeData, OrganizerCore& core); + + // returns true if this drop is valid + // + bool isValid() const; + + // returns true if these data corresponds to drag&drop + // of local files (e.g. from overwrite) + // + bool isLocalFileDrop() const; + + // returns true if these data corresponds to drag&drop + // of mod in the list + // + bool isModDrop() const; + + // returns true if these data corresponds to drag&drop + // from the download list + // + bool isDownloadDrop() const; + + // returns true if these data corresponds to dropping + // an archive for installation + // + bool isExternalArchiveDrop() const; + + // returns true if these data corresponds to dropping + // a folder for copy + // + bool isExternalFolderDrop() const; + + const auto& rows() const { return m_rows; } + const auto& download() const { return m_download; } + const auto& localUrls() const { return m_localUrls; } + const auto& externalUrl() const { return m_url; } + +private: + + friend class ModList; + + // retrieve the relative path of file and its origin given a URL from Mime data + // returns an empty optional if the URL is not a valid file for dropping + // + std::optional relativeUrl(const QUrl&) const; + +private: + + // rows for drag&drop between views + std::vector m_rows; + int m_download; // -1 if invalid + + // local URLs from the data (relative path + origin name) + std::vector m_localUrls; + + // external URL + QUrl m_url; +}; + +#endif diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index f9a32b26..0dc2be83 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -18,6 +18,7 @@ along with Mod Organizer. If not, see . */ #include "modlistsortproxy.h" +#include "modlistdropinfo.h" #include "modinfo.h" #include "profile.h" #include "messagedialog.h" @@ -607,7 +608,7 @@ bool ModListSortProxy::filterAcceptsRow(int source_row, const QModelIndex &paren bool ModListSortProxy::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const { - auto dropInfo = m_Organizer->modList()->dropInfo(data); + ModListDropInfo dropInfo(data, *m_Organizer); if (!dropInfo.isLocalFileDrop() && sortColumn() != ModList::COL_PRIORITY) { return false; @@ -630,7 +631,7 @@ bool ModListSortProxy::canDropMimeData(const QMimeData* data, Qt::DropAction act bool ModListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { - auto dropInfo = m_Organizer->modList()->dropInfo(data); + ModListDropInfo dropInfo(data, *m_Organizer); if (!dropInfo.isLocalFileDrop() && sortColumn() != ModList::COL_PRIORITY) { QWidget *wid = qApp->activeWindow()->findChild("modList"); diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 02b7384a..be173478 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -17,6 +17,7 @@ #include "log.h" #include "modflagicondelegate.h" #include "modconflicticondelegate.h" +#include "modlistdropinfo.h" #include "genericicondelegate.h" #include "shared/directoryentry.h" #include "shared/filesorigin.h" @@ -516,6 +517,91 @@ void ModListView::updateModCount() ); } +void ModListView::onExternalFolderDropped(const QUrl& url, int priority) +{ + QFileInfo fileInfo(url.toLocalFile()); + + GuessedValue name; + name.setFilter(&fixDirectoryName); + name.update(fileInfo.fileName(), GUESS_PRESET); + + do { + bool ok; + name.update(QInputDialog::getText(this, tr("Copy Folder..."), + tr("This will copy the content of %1 to a new mod.\n" + "Please enter the name:").arg(fileInfo.fileName()), QLineEdit::Normal, name, &ok), + GUESS_USER); + if (!ok) { + return; + } + } while (name->isEmpty()); + + if (m_core->modList()->getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists.")); + return; + } + + IModInterface* newMod = m_core->createMod(name); + if (!newMod) { + return; + } + + if (!copyDir(fileInfo.absoluteFilePath(), newMod->absolutePath(), true)) { + return; + } + + m_core->refresh(); + + if (priority != -1) { + m_core->modList()->changeModPriority(ModInfo::getIndex(name), priority); + } +} + +bool ModListView::moveSelection(int key) +{ + QModelIndex cindex = indexViewToModel(currentIndex()); + QModelIndexList sourceRows = indexViewToModel(selectionModel()->selectedRows()); + + int offset = key == Qt::Key_Up ? -1 : 1; + if (m_sortProxy->sortOrder() == Qt::DescendingOrder) { + offset = -offset; + } + + m_core->modList()->shiftMods(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; + } + return m_core->modList()->toggleState(indexViewToModel(selectionModel()->selectedRows())); +} + void ModListView::updateGroupByProxy(int groupIndex) { // if the index is -1, we do not refresh unless we are grouping @@ -683,6 +769,14 @@ void ModListView::dragEnterEvent(QDragEnterEvent* event) { emit dragEntered(event->mimeData()); QTreeView::dragEnterEvent(event); + + + ModListDropInfo dropInfo(event->mimeData(), *m_core); + + if (dropInfo.isValid() && !dropInfo.isLocalFileDrop() + && sortColumn() != ModList::COL_PRIORITY) { + log::warn("Drag&Drop is only supported when sorting by priority."); + } } void ModListView::dragMoveEvent(QDragMoveEvent* event) @@ -722,91 +816,6 @@ void ModListView::timerEvent(QTimerEvent* event) } } -void ModListView::onExternalFolderDropped(const QUrl& url, int priority) -{ - QFileInfo fileInfo(url.toLocalFile()); - - GuessedValue name; - name.setFilter(&fixDirectoryName); - name.update(fileInfo.fileName(), GUESS_PRESET); - - do { - bool ok; - name.update(QInputDialog::getText(this, tr("Copy Folder..."), - tr("This will copy the content of %1 to a new mod.\n" - "Please enter the name:").arg(fileInfo.fileName()), QLineEdit::Normal, name, &ok), - GUESS_USER); - if (!ok) { - return; - } - } while (name->isEmpty()); - - if (m_core->modList()->getMod(name) != nullptr) { - reportError(tr("A mod with this name already exists.")); - return; - } - - IModInterface* newMod = m_core->createMod(name); - if (!newMod) { - return; - } - - if (!copyDir(fileInfo.absoluteFilePath(), newMod->absolutePath(), true)) { - return; - } - - m_core->refresh(); - - if (priority != -1) { - m_core->modList()->changeModPriority(ModInfo::getIndex(name), priority); - } -} - -bool ModListView::moveSelection(int key) -{ - QModelIndex cindex = indexViewToModel(currentIndex()); - QModelIndexList sourceRows = indexViewToModel(selectionModel()->selectedRows()); - - int offset = key == Qt::Key_Up ? -1 : 1; - if (m_sortProxy->sortOrder() == Qt::DescendingOrder) { - offset = -offset; - } - - m_core->modList()->shiftMods(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; - } - return m_core->modList()->toggleState(indexViewToModel(selectionModel()->selectedRows())); -} - bool ModListView::event(QEvent* event) { Profile* profile = m_core->currentProfile(); -- cgit v1.3.1 From fb52a129b3a878511cf754daed433d9a789689c8 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 00:06:12 +0100 Subject: More stuff moved to mod list context. --- src/mainwindow.cpp | 121 +++------------------------------------------ src/mainwindow.h | 7 --- src/modlist.cpp | 22 ++++++++- src/modlist.h | 6 ++- src/modlistcontextmenu.cpp | 39 ++++++++++----- src/modlistcontextmenu.h | 17 ++++--- src/modlistview.cpp | 2 +- src/modlistviewactions.cpp | 68 +++++++++++++++++++++++++ src/modlistviewactions.h | 6 +++ 9 files changed, 148 insertions(+), 140 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 265cc5c2..91897d2f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3832,22 +3832,6 @@ QMenu *MainWindow::openFolderMenu() return FolderMenu; } -void MainWindow::addModSendToContextMenu(QMenu *menu) -{ - if (ui->modList->sortColumn() != ModList::COL_PRIORITY) - return; - - QMenu *sub_menu = new QMenu(menu); - sub_menu->setTitle(tr("Send to")); - sub_menu->addAction(tr("Top"), [&]() { sendSelectedModsToTop_clicked(); }); - sub_menu->addAction(tr("Bottom"), [&]() { sendSelectedModsToBottom_clicked(); }); - sub_menu->addAction(tr("Priority..."), [&]() { sendSelectedModsToPriority_clicked(); }); - sub_menu->addAction(tr("Separator..."), [&]() { sendSelectedModsToSeparator_clicked(); }); - - menu->addMenu(sub_menu); - menu->addSeparator(); -} - void MainWindow::addPluginSendToContextMenu(QMenu *menu) { if (m_PluginListSortProxy->sortColumn() != PluginList::COL_PRIORITY) @@ -3932,7 +3916,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Rename Separator..."), [=]() { renameMod_clicked(); }); menu.addAction(tr("Remove Separator..."), [=]() { removeMod_clicked(modIndex); }); menu.addSeparator(); - addModSendToContextMenu(&menu); + if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { + menu.addMenu(menu.createSendToContextMenu()); + menu.addSeparator(); + } menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); if (info->color().isValid()) { @@ -3944,7 +3931,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // foregin else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - addModSendToContextMenu(&menu); } // regular @@ -3981,7 +3967,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); - addModSendToContextMenu(&menu); + if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { + menu.addMenu(menu.createSendToContextMenu()); + menu.addSeparator(); + } menu.addAction(tr("Rename Mod..."), [=]() { renameMod_clicked(); }); menu.addAction(tr("Reinstall Mod"), [=]() { reinstallMod_clicked(modIndex); }); @@ -5477,97 +5466,3 @@ void MainWindow::on_clearFiltersButton_clicked() ui->modFilterEdit->clear(); deselectFilters(); } - -void MainWindow::sendSelectedModsToPriority(int newPriority) -{ - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection()) { - std::vector modsToMove; - for (auto idx : selection->selectedRows(ModList::COL_PRIORITY)) { - modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); - } - - if (modsToMove.size() == 1) { - m_OrganizerCore.modList()->changeModPriority(modsToMove[0], newPriority); - } - else { - m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); - } - } -} - -void MainWindow::sendSelectedModsToTop_clicked() -{ - sendSelectedModsToPriority(0); -} - -void MainWindow::sendSelectedModsToBottom_clicked() -{ - sendSelectedModsToPriority(INT_MAX); -} - -void MainWindow::sendSelectedModsToPriority_clicked() -{ - bool ok; - int newPriority = QInputDialog::getInt(this, - tr("Set Priority"), tr("Set the priority of the selected mods"), - 0, 0, INT_MAX, 1, &ok); - if (!ok) return; - - sendSelectedModsToPriority(newPriority); -} - -void MainWindow::sendSelectedModsToSeparator_clicked() -{ - QStringList separators; - auto indexesByPriority = m_OrganizerCore.currentProfile()->getAllIndexesByPriority(); - for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) { - if ((iter->second != UINT_MAX)) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second); - if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { - separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name - } - } - } - - ListDialog dialog(this); - dialog.setWindowTitle("Select a separator..."); - dialog.setChoices(separators); - - if (dialog.exec() == QDialog::Accepted) { - QString result = dialog.getChoice(); - if (!result.isEmpty()) { - result += "_separator"; - - int newPriority = INT_MAX; - bool foundSection = false; - for (auto mod : m_OrganizerCore.modsSortedByProfilePriority(m_OrganizerCore.currentProfile())) { - unsigned int modIndex = ModInfo::getIndex(mod); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - if (!foundSection && result.compare(mod) == 0) { - foundSection = true; - } else if (foundSection && modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { - newPriority = m_OrganizerCore.currentProfile()->getModPriority(modIndex); - break; - } - } - - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection()) { - std::vector modsToMove; - for (QModelIndex idx : selection->selectedRows(ModList::COL_PRIORITY)) { - modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); - } - if (modsToMove.size() == 1) { - int oldPriority = m_OrganizerCore.currentProfile()->getModPriority(modsToMove[0]); - if (oldPriority < newPriority) - --newPriority; - m_OrganizerCore.modList()->changeModPriority(modsToMove[0], newPriority); - } - else { - m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); - } - } - } - } -} diff --git a/src/mainwindow.h b/src/mainwindow.h index 57f26220..790960e1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -244,15 +244,12 @@ private: bool createBackup(const QString &filePath, const QDateTime &time); QString queryRestore(const QString &filePath); - void addModSendToContextMenu(QMenu *menu); void addPluginSendToContextMenu(QMenu *menu); QMenu *openFolderMenu(); void dropLocalFile(const QUrl &url, const QString &outputDir, bool move); - void sendSelectedModsToPriority(int newPriority); - void toggleMO2EndorseState(); void toggleUpdateAction(); @@ -374,10 +371,6 @@ private slots: void information_clicked(int modIndex); void enableSelectedMods_clicked(); void disableSelectedMods_clicked(); - void sendSelectedModsToTop_clicked(); - void sendSelectedModsToBottom_clicked(); - void sendSelectedModsToPriority_clicked(); - void sendSelectedModsToSeparator_clicked(); // data-tree context menu // pluginlist context menu diff --git a/src/modlist.cpp b/src/modlist.cpp index 7da6fe3b..22899aaa 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1417,7 +1417,7 @@ QString ModList::getColumnToolTip(int column) const } } -void ModList::shiftMods(const QModelIndexList& indices, int offset) +void ModList::shiftModsPriority(const QModelIndexList& indices, int offset) { // retrieve the mod index and sort them by priority to avoid issue // when moving them @@ -1451,6 +1451,26 @@ void ModList::shiftMods(const QModelIndexList& indices, int offset) emit modPrioritiesChanged(allIndex); } +void ModList::changeModsPriority(const QModelIndexList& indices, int priority) +{ + if (indices.isEmpty()) { + return; + } + + std::vector allIndex; + for (auto& idx : indices) { + auto index = idx.data(IndexRole).toInt(); + allIndex.push_back(index); + } + + if (allIndex.size() == 1) { + changeModPriority(allIndex[0], priority); + } + else { + changeModPriority(allIndex, priority); + } +} + bool ModList::toggleState(const QModelIndexList& indices) { emit aboutToChangeData(); diff --git a/src/modlist.h b/src/modlist.h index 870978f9..cabd1f32 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -226,7 +226,11 @@ public slots: // shift the priority of mods at the given indices by the given offset // - void shiftMods(const QModelIndexList& indices, int offset); + void shiftModsPriority(const QModelIndexList& indices, int offset); + + // change the priority of the mods specified by the given indices + // + void changeModsPriority(const QModelIndexList& indices, int priority); // toggle the active state of mods at the given indices // diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 41031eaa..c4a82e46 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -45,6 +45,7 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i QMenu(view) , m_core(core) , m_index() + , m_view(view) { if (view->selectionModel()->hasSelection()) { m_index = view->indexViewToModel(view->selectionModel()->selectedRows()); @@ -68,20 +69,23 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i // Add type-specific items ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + // TODO: + // - Don't forget to check for the sort priority for "Send To... " + if (info->isOverwrite()) { - addOverwriteActions(core, view); + addOverwriteActions(); } else if (info->isBackup()) { - addBackupActions(core, view); + addBackupActions(); } else if (info->isSeparator()) { - addSeparatorActions(core, view); + addSeparatorActions(); } else if (info->isForeign()) { - addForeignActions(core, view); + addForeignActions(); } else { - addRegularActions(core, view); + addRegularActions(); } // add information for all except foreign @@ -91,27 +95,40 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i } } -void ModListContextMenu::addOverwriteActions(OrganizerCore& core, ModListView* modListView) +QMenu* ModListContextMenu::createSendToContextMenu() { - + QMenu* menu = new QMenu(m_view); + menu->setTitle(tr("Send to... ")); + menu->addAction(tr("Top"), [=]() { m_view->actions().sendModsToTop(m_index); }); + menu->addAction(tr("Bottom"), [=]() { m_view->actions().sendModsToBottom(m_index); }); + menu->addAction(tr("Priority..."), [=]() { m_view->actions().sendModsToPriority(m_index); }); + menu->addAction(tr("Separator..."), [=]() { m_view->actions().sendModsToSeparator(m_index); }); + return menu; } -void ModListContextMenu::addSeparatorActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addOverwriteActions() { } -void ModListContextMenu::addForeignActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addSeparatorActions() { } -void ModListContextMenu::addBackupActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addForeignActions() +{ + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addMenu(createSendToContextMenu()); + } +} + +void ModListContextMenu::addBackupActions() { } -void ModListContextMenu::addRegularActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addRegularActions() { } diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 9d015afc..3bc15c57 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -29,18 +29,23 @@ public: // ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* modListView); -private: +public: // TODO: Move this to private when all is done + + // create the "Send to... " context menu + // + QMenu* createSendToContextMenu(); // add actions/menus to this menu for each type of mod // - void addOverwriteActions(OrganizerCore& core, ModListView* modListView); - void addSeparatorActions(OrganizerCore& core, ModListView* modListView); - void addForeignActions(OrganizerCore& core, ModListView* modListView); - void addBackupActions(OrganizerCore& core, ModListView* modListView); - void addRegularActions(OrganizerCore& core, ModListView* modListView); + void addOverwriteActions(); + void addSeparatorActions(); + void addForeignActions(); + void addBackupActions(); + void addRegularActions(); OrganizerCore& m_core; QModelIndexList m_index; + ModListView* m_view; }; diff --git a/src/modlistview.cpp b/src/modlistview.cpp index f8192758..8c826882 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -575,7 +575,7 @@ bool ModListView::moveSelection(int key) offset = -offset; } - m_core->modList()->shiftMods(sourceRows, offset); + m_core->modList()->shiftModsPriority(sourceRows, offset); // reset the selection and the index setCurrentIndex(indexModelToView(cindex)); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 6faebc15..0d556e64 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -11,6 +11,7 @@ #include "categories.h" #include "filedialogmemory.h" #include "filterlist.h" +#include "listdialog.h" #include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" @@ -430,3 +431,70 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in } } } + +void ModListViewActions::sendModsToTop(const QModelIndexList& index) const +{ + m_core.modList()->changeModsPriority(index, 0); +} + +void ModListViewActions::sendModsToBottom(const QModelIndexList& index) const +{ + m_core.modList()->changeModsPriority(index, std::numeric_limits::max()); +} + +void ModListViewActions::sendModsToPriority(const QModelIndexList& index) const +{ + bool ok; + int priority = QInputDialog::getInt(m_view, + tr("Set Priority"), tr("Set the priority of the selected mods"), + 0, 0, std::numeric_limits::max(), 1, &ok); + if (!ok) return; + + m_core.modList()->changeModsPriority(index, priority); +} + +void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const +{ + QStringList separators; + auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority(); + for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) { + if ((iter->second != UINT_MAX)) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second); + if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { + separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name + } + } + } + + ListDialog dialog(m_view); + dialog.setWindowTitle("Select a separator..."); + dialog.setChoices(separators); + + if (dialog.exec() == QDialog::Accepted) { + QString result = dialog.getChoice(); + if (!result.isEmpty()) { + result += "_separator"; + + int newPriority = std::numeric_limits::max(); + bool foundSection = false; + for (auto mod : m_core.modsSortedByProfilePriority(m_core.currentProfile())) { + unsigned int modIndex = ModInfo::getIndex(mod); + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + if (!foundSection && result.compare(mod) == 0) { + foundSection = true; + } + else if (foundSection && modInfo->isSeparator()) { + newPriority = m_core.currentProfile()->getModPriority(modIndex); + break; + } + } + + if (index.size() == 1 + && m_core.currentProfile()->getModPriority(index[0].data(ModList::IndexRole).toInt()) < newPriority) { + --newPriority; + } + + m_core.modList()->changeModsPriority(index, newPriority); + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index b01b8e79..d8e1a3d0 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -53,6 +53,12 @@ public: void displayModInformation(unsigned int index, ModInfoTabIDs tab = ModInfoTabIDs::None) const; void displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID = ModInfoTabIDs::None) const; + // move mods to top/bottom, start the "Send to priority" and "Send to separator" dialog + // + void sendModsToTop(const QModelIndexList& index) const; + void sendModsToBottom(const QModelIndexList& index) const; + void sendModsToPriority(const QModelIndexList& index) const; + void sendModsToSeparator(const QModelIndexList& index) const; private: -- cgit v1.3.1 From fff41be8455e588d181c7349678dff6fe29be76b Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 23:26:14 +0100 Subject: Remove selection-related stuff from plugin/mod lists. --- src/mainwindow.cpp | 26 -------------------------- src/mainwindow.h | 4 ---- src/modlist.cpp | 8 +++++--- src/modlist.h | 6 +++++- src/modlistview.cpp | 12 ++++++++++++ src/organizercore.cpp | 4 ---- src/pluginlist.cpp | 14 +++++++------- src/pluginlist.h | 6 +++++- src/pluginlistview.cpp | 16 ++++++++++++++++ 9 files changed, 50 insertions(+), 46 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 033bb0e0..55019a44 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -321,8 +321,6 @@ MainWindow::MainWindow(Settings &settings setupModList(); ui->espList->setup(m_OrganizerCore, this, ui); - connect(ui->espList->selectionModel(), &QItemSelectionModel::selectionChanged, - [=](auto&& selection) { esplistSelectionsChanged(selection); }); ui->bsaList->setLocalMoveOnly(true); ui->bsaList->setHeaderHidden(true); @@ -514,10 +512,6 @@ void MainWindow::setupModList() connect(&ui->modList->actions(), &ModListViewActions::overwriteCleared, [=]() { scheduleCheckForProblems(); }); connect(&ui->modList->actions(), &ModListViewActions::originModified, this, &MainWindow::originModified); connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); }); - - // keep here for now - connect(ui->modList->selectionModel(), &QItemSelectionModel::selectionChanged, - this, &MainWindow::modlistSelectionsChanged); } void MainWindow::resetActionIcons() @@ -2186,11 +2180,6 @@ void MainWindow::directory_refreshed() } } -void MainWindow::esplist_changed() -{ - ui->espList->updatePluginCount(); -} - void MainWindow::modInstalled(const QString &modName) { unsigned int index = ModInfo::getIndex(modName); @@ -2263,26 +2252,11 @@ void MainWindow::fileMoved(const QString &filePath, const QString &oldOriginName void MainWindow::modlistChanged(const QModelIndex&, int) { m_OrganizerCore.currentProfile()->writeModlist(); - ui->modList->updateModCount(); } void MainWindow::modlistChanged(const QModelIndexList&, int) { m_OrganizerCore.currentProfile()->writeModlist(); - ui->modList->updateModCount(); -} - -void MainWindow::modlistSelectionsChanged(const QItemSelection &selected) -{ - m_OrganizerCore.pluginList()->highlightPlugins(ui->modList->selectionModel(), *m_OrganizerCore.directoryStructure(), *m_OrganizerCore.currentProfile()); - ui->espList->verticalScrollBar()->repaint(); -} - -void MainWindow::esplistSelectionsChanged(const QItemSelection &selected) -{ - m_OrganizerCore.modList()->highlightMods(ui->espList->selectionModel(), *m_OrganizerCore.directoryStructure()); - ui->modList->verticalScrollBar()->repaint(); - ui->modList->repaint(); } void MainWindow::modRemoved(const QString &fileName) diff --git a/src/mainwindow.h b/src/mainwindow.h index c6e94aa9..5fa61c24 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -140,7 +140,6 @@ public: virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; } public slots: - void esplist_changed(); void refresherProgress(const DirectoryRefreshProgress* p); void directory_refreshed(); @@ -397,9 +396,6 @@ private slots: void about(); - void modlistSelectionsChanged(const QItemSelection ¤t); - void esplistSelectionsChanged(const QItemSelection ¤t); - void resetActionIcons(); private slots: // ui slots diff --git a/src/modlist.cpp b/src/modlist.cpp index 22899aaa..077f4af3 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -845,13 +845,15 @@ int ModList::timeElapsedSinceLastChecked() const return m_LastCheck.elapsed(); } -void ModList::highlightMods(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry) +void ModList::highlightMods( + const std::vector& pluginIndices, + const MOShared::DirectoryEntry &directoryEntry) { for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) { ModInfo::getByIndex(i)->setPluginSelected(false); } - for (QModelIndex idx : selection->selectedRows(PluginList::COL_NAME)) { - QString pluginName = idx.data().toString(); + for (auto idx : pluginIndices) { + QString pluginName = m_Organizer->pluginList()->getName(idx); const MOShared::FileEntryPtr fileEntry = directoryEntry.findFile(pluginName.toStdWString()); if (fileEntry.get() != nullptr) { diff --git a/src/modlist.h b/src/modlist.h index e4f4dfab..4b0e0157 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -129,7 +129,11 @@ public: int timeElapsedSinceLastChecked() const; - void highlightMods(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry); + // highlight mods containing the plugins at the given indices + // + void highlightMods( + const std::vector& pluginIndices, + const MOShared::DirectoryEntry &directoryEntry); public: diff --git a/src/modlistview.cpp b/src/modlistview.cpp index f23e84fe..42627e3f 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -594,6 +594,8 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo connect(m_core, &OrganizerCore::modInstalled, this, &ModListView::onModInstalled); connect(core.modList(), &ModList::modPrioritiesChanged, this, &ModListView::onModPrioritiesChanged); connect(core.modList(), &ModList::clearOverwrite, m_actions, &ModListViewActions::clearOverwrite); + connect(core.modList(), qOverload(&ModList::modlistChanged), [=]() { updateModCount(); }); + connect(core.modList(), qOverload(&ModList::modlistChanged), [=]() { updateModCount(); }); m_byPriorityProxy = new ModListByPriorityProxy(core.currentProfile(), core, this); m_byPriorityProxy->setSourceModel(core.modList()); @@ -672,6 +674,16 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo header()->setSectionResizeMode(ModList::COL_NAME, QHeaderView::Stretch); } + // highligth plugins + connect(selectionModel(), &QItemSelectionModel::selectionChanged, [=](auto&& selected) { + std::vector modIndices; + for (auto& idx : selectionModel()->selectedRows()) { + modIndices.push_back(idx.data(ModList::IndexRole).toInt()); + } + m_core->pluginList()->highlightPlugins(modIndices, *m_core->directoryStructure()); + mwui->espList->verticalScrollBar()->repaint(); + }); + // prevent the name-column from being hidden header()->setSectionHidden(ModList::COL_NAME, false); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index a8911d4f..4113607c 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -249,10 +249,6 @@ void OrganizerCore::setUserInterface(IUserInterface* ui) SLOT(modRemoved(QString))); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), w, SLOT(fileMoved(QString, QString, QString))); - connect(&m_PluginList, SIGNAL(writePluginsList()), w, - SLOT(esplist_changed())); - connect(&m_PluginList, SIGNAL(esplist_changed()), w, - SLOT(esplist_changed())); connect(&m_DownloadManager, SIGNAL(showMessage(QString)), w, SLOT(showMessage(QString))); } diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index a2e485ee..4d648a46 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -135,19 +135,19 @@ QString PluginList::getColumnToolTip(int column) } } -void PluginList::highlightPlugins(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry, const Profile &profile) +void PluginList::highlightPlugins( + const std::vector& modIndices, + const MOShared::DirectoryEntry &directoryEntry) { + auto* profile = m_Organizer.currentProfile(); + for (auto &esp : m_ESPs) { esp.modSelected = false; } - for (QModelIndex idx : selection->selectedRows(ModList::COL_PRIORITY)) { - int modIndex = idx.data(Qt::UserRole + 1).toInt(); - if (modIndex == UINT_MAX) - continue; - + for (auto& modIndex : modIndices) { ModInfo::Ptr selectedMod = ModInfo::getByIndex(modIndex); - if (!selectedMod.isNull() && profile.modEnabled(modIndex)) { + if (!selectedMod.isNull() && profile->modEnabled(modIndex)) { QDir dir(selectedMod->absolutePath()); QStringList plugins = dir.entryList(QStringList() << "*.esp" << "*.esm" << "*.esl"); const MOShared::FilesOrigin& origin = directoryEntry.getOriginByName(selectedMod->internalName().toStdWString()); diff --git a/src/pluginlist.h b/src/pluginlist.h index c93ce5cb..c16bfc98 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -217,7 +217,11 @@ public: static QString getColumnName(int column); static QString getColumnToolTip(int column); - void highlightPlugins(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry, const Profile &profile); + // highlight plugins contained in the mods at the given indices + // + void highlightPlugins( + const std::vector& modIndices, + const MOShared::DirectoryEntry &directoryEntry); void refreshLoadOrder(); diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index 4bf91c0a..39db7163 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -150,9 +150,25 @@ void PluginListView::setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder); setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(this)); + // counter + connect(core.pluginList(), &PluginList::writePluginsList, [=]() { updatePluginCount(); }); + connect(core.pluginList(), &PluginList::esplist_changed, [=]() { updatePluginCount(); }); + + // filter connect(ui.filter, &QLineEdit::textChanged, m_sortProxy, &PluginListSortProxy::updateFilter); connect(ui.filter, &QLineEdit::textChanged, this, &PluginListView::onFilterChanged); + // highligth mod list when selected + connect(selectionModel(), &QItemSelectionModel::selectionChanged, [=](auto&& selected) { + std::vector pluginIndices; + for (auto& idx : indexViewToModel(selectionModel()->selectedRows())) { + pluginIndices.push_back(idx.row()); + } + m_core->modList()->highlightMods(pluginIndices, *m_core->directoryStructure()); + mwui->modList->verticalScrollBar()->repaint(); + mwui->modList->repaint(); + }); + // using a lambda here to avoid storing the mod list actions connect(this, &QTreeView::customContextMenuRequested, [=](auto&& pos) { onCustomContextMenuRequested(pos); }); connect(this, &QTreeView::doubleClicked, [=](auto&& index) { onDoubleClicked(index); }); -- cgit v1.3.1 From e02ba3b26d30ffb5010394992c69e94b287eacd7 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 12:24:57 +0100 Subject: Some cleaning. Avoid using Qt::UserRole. --- src/modconflicticondelegate.cpp | 7 +++-- src/modflagicondelegate.cpp | 7 +++-- src/modlist.cpp | 64 ++++++++++++++++++++++++++++------------- src/modlist.h | 32 +++++++++++++++++++-- src/modlistcontextmenu.cpp | 15 ++++++---- src/modlistsortproxy.cpp | 13 +-------- src/modlistview.cpp | 24 +++++++++------- src/pluginlist.cpp | 2 +- src/pluginlistview.cpp | 4 +-- src/viewmarkingscrollbar.cpp | 14 ++++----- src/viewmarkingscrollbar.h | 14 ++++----- 11 files changed, 121 insertions(+), 75 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modconflicticondelegate.cpp b/src/modconflicticondelegate.cpp index 2ccf3363..9680aca3 100644 --- a/src/modconflicticondelegate.cpp +++ b/src/modconflicticondelegate.cpp @@ -1,4 +1,5 @@ #include "modconflicticondelegate.h" +#include "modlist.h" #include #include @@ -96,7 +97,7 @@ QList ModConflictIconDelegate::getIconsForFlags( QList ModConflictIconDelegate::getIcons(const QModelIndex &index) const { - QVariant modid = index.data(Qt::UserRole + 1); + QVariant modid = index.data(ModList::IndexRole); if (modid.isValid()) { ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt()); @@ -127,7 +128,7 @@ QString ModConflictIconDelegate::getFlagIcon(ModInfo::EConflictFlag flag) size_t ModConflictIconDelegate::getNumIcons(const QModelIndex &index) const { - unsigned int modIdx = index.data(Qt::UserRole + 1).toInt(); + unsigned int modIdx = index.data(ModList::IndexRole).toInt(); if (modIdx < ModInfo::getNumMods()) { ModInfo::Ptr info = ModInfo::getByIndex(modIdx); std::vector flags = info->getConflictFlags(); @@ -145,7 +146,7 @@ size_t ModConflictIconDelegate::getNumIcons(const QModelIndex &index) const QSize ModConflictIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const { size_t count = getNumIcons(modelIndex); - unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); + unsigned int index = modelIndex.data(ModList::IndexRole).toInt(); QSize result; if (index < ModInfo::getNumMods()) { result = QSize(static_cast(count) * 40, 20); diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp index bec3c97d..3ac1085e 100644 --- a/src/modflagicondelegate.cpp +++ b/src/modflagicondelegate.cpp @@ -1,4 +1,5 @@ #include "modflagicondelegate.h" +#include "modlist.h" #include #include @@ -39,7 +40,7 @@ QList ModFlagIconDelegate::getIconsForFlags( QList ModFlagIconDelegate::getIcons(const QModelIndex &index) const { - QVariant modid = index.data(Qt::UserRole + 1); + QVariant modid = index.data(ModList::IndexRole); if (modid.isValid()) { ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt()); @@ -71,7 +72,7 @@ QString ModFlagIconDelegate::getFlagIcon(ModInfo::EFlag flag) size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const { - unsigned int modIdx = index.data(Qt::UserRole + 1).toInt(); + unsigned int modIdx = index.data(ModList::IndexRole).toInt(); if (modIdx < ModInfo::getNumMods()) { ModInfo::Ptr info = ModInfo::getByIndex(modIdx); std::vector flags = info->getFlags(); @@ -85,7 +86,7 @@ size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const QSize ModFlagIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const { size_t count = getNumIcons(modelIndex); - unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); + unsigned int index = modelIndex.data(ModList::IndexRole).toInt(); QSize result; if (index < ModInfo::getNumMods()) { result = QSize(static_cast(count) * 40, 20); diff --git a/src/modlist.cpp b/src/modlist.cpp index 077f4af3..26581ba5 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -61,6 +61,8 @@ along with Mod Organizer. If not, see . using namespace MOBase; +const int ModList::ModUserRole = Qt::UserRole + QMetaEnum::fromType().keyCount(); + ModList::ModList(PluginContainer *pluginContainer, OrganizerCore *organizer) : QAbstractItemModel(organizer) , m_Organizer(organizer) @@ -130,7 +132,7 @@ QVariant ModList::getOverwriteData(int column, int role) const } } break; case Qt::TextAlignmentRole: return QVariant(Qt::AlignCenter | Qt::AlignVCenter); - case Qt::UserRole: return -1; + case GroupingRole: return -1; case Qt::ForegroundRole: return QBrush(Qt::red); case Qt::ToolTipRole: return tr("This entry contains files that have been created inside the virtual data tree (i.e. by the construction kit)"); default: return QVariant(); @@ -298,7 +300,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(); } - } else if (role == Qt::TextAlignmentRole) { + } + else if (role == Qt::TextAlignmentRole) { auto flags = modInfo->getFlags(); if (column == COL_NAME) { if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_CENTER) { @@ -313,7 +316,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(Qt::AlignCenter | Qt::AlignVCenter); } - } else if (role == Qt::UserRole) { + } + else if (role == GroupingRole) { if (column == COL_CATEGORY) { QVariantList categoryNames; std::set categories = modInfo->getCategories(); @@ -326,7 +330,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(); } - } else if (column == COL_PRIORITY) { + } + else if (column == COL_PRIORITY) { int priority = modInfo->getFixedPriority(); if (priority != INT_MIN) { return priority; @@ -336,9 +341,11 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return modInfo->nexusId(); } - } else if (role == IndexRole) { + } + else if (role == IndexRole) { return modIndex; - } else if (role == Qt::UserRole + 2) { + } + else if (role == AggrRole) { switch (column) { case COL_MODID: return QtGroupingProxy::AGGR_FIRST; case COL_VERSION: return QtGroupingProxy::AGGR_MAX; @@ -346,11 +353,23 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const case COL_PRIORITY: return QtGroupingProxy::AGGR_MIN; default: return QtGroupingProxy::AGGR_NONE; } - } else if (role == Qt::UserRole + 3) { + } + else if (role == ContentsRole) { return contentsToIcons(modInfo->getContents()); - } else if (role == Qt::UserRole + 4) { + } + else if (role == NameRole) { return modInfo->gameName(); - } else if (role == Qt::FontRole) { + } + else if (role == PriorityRole) { + int priority = modInfo->getFixedPriority(); + if (priority != std::numeric_limits::min()) { + return priority; + } + else { + return m_Profile->getModPriority(modIndex); + } + } + else if (role == Qt::FontRole) { QFont result; auto flags = modInfo->getFlags(); if (column == COL_NAME) { @@ -374,7 +393,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return result; - } else if (role == Qt::DecorationRole) { + } + else if (role == Qt::DecorationRole) { if (column == COL_VERSION) { if (modInfo->updateAvailable()) { return QIcon(":/MO/gui/update_available"); @@ -385,7 +405,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return QVariant(); - } else if (role == Qt::ForegroundRole) { + } + else if (role == Qt::ForegroundRole) { if ((modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) || (column == COL_NOTES)) && modInfo->color().isValid()) { return ColorSettings::idealTextColor(modInfo->color()); } else if (column == COL_NAME) { @@ -404,8 +425,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return QVariant(); - } else if ((role == Qt::BackgroundRole) - || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { + } + else if (role == Qt::BackgroundRole || role == ScrollMarkRole) { bool overwrite = m_Overwrite.find(modIndex) != m_Overwrite.end(); bool archiveOverwrite = m_ArchiveOverwrite.find(modIndex) != m_ArchiveOverwrite.end(); bool archiveLooseOverwrite = m_ArchiveLooseOverwrite.find(modIndex) != m_ArchiveLooseOverwrite.end(); @@ -424,15 +445,15 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return Settings::instance().colors().modlistOverwritingArchive(); } else if (archiveOverwrite) { return Settings::instance().colors().modlistOverwrittenArchive(); - } else if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) - && modInfo->color().isValid() - && ((role != ViewMarkingScrollBar::DEFAULT_ROLE) - || Settings::instance().colors().colorSeparatorScrollbar())) { + } else if (modInfo->isSeparator() && modInfo->color().isValid() + && (role != ScrollMarkRole + || Settings::instance().colors().colorSeparatorScrollbar())) { return modInfo->color(); } else { return QVariant(); } - } else if (role == Qt::ToolTipRole) { + } + else if (role == Qt::ToolTipRole) { if (column == COL_FLAGS) { QString result; @@ -507,7 +528,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(); } - } else { + } + else { return QVariant(); } } @@ -1366,7 +1388,9 @@ QModelIndex ModList::parent(const QModelIndex&) const QMap ModList::itemData(const QModelIndex &index) const { QMap result = QAbstractItemModel::itemData(index); - result[Qt::UserRole] = data(index, Qt::UserRole); + for (int role = Qt::UserRole; role < ModUserRole; ++role) { + result[role] = data(index, role); + } return result; } diff --git a/src/modlist.h b/src/modlist.h index 4b0e0157..e77ceb1f 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -31,6 +31,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #ifndef Q_MOC_RUN #include @@ -56,9 +57,34 @@ class ModList : public QAbstractItemModel public: - // role of the index of the mod - // - constexpr static int IndexRole = Qt::UserRole + 1; + enum ModListRole { + + // data(GroupingRole) contains the "group" role - This is used by the + // category and Nexus ID grouping proxy (but not the ByPriority proxy) + GroupingRole = Qt::UserRole, + + IndexRole = Qt::UserRole + 1, + + // data(AggrRole) contains aggregation information (for + // grouping I assume?) + AggrRole = Qt::UserRole + 2, + + // data(ContentsRole) contains mod data contents as a QVariantList + // containing icon paths + ContentsRole = Qt::UserRole + 3, + + NameRole = Qt::UserRole + 4, + + PriorityRole = Qt::UserRole + 5, + + // marking role for the scrollbar + ScrollMarkRole = Qt::UserRole + 6 + }; + + Q_ENUM(ModListRole) + + // this is the first available role + static const int ModUserRole; enum EColumn { COL_NAME, diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 303362ba..01c4b471 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -159,23 +159,26 @@ ModListContextMenu::ModListContextMenu( m_selected = { index }; } + ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + QMenu* allMods = new ModListGlobalContextMenu(core, view, view); allMods->setTitle(tr("All Mods")); addMenu(allMods); - if (view->hasCollapsibleSeparators()) { + auto viewIndex = view->indexModelToView(m_index); + if (view->model()->hasChildren(viewIndex)) { + bool expanded = view->isExpanded(viewIndex); addAction(tr("Collapse all"), view, &QTreeView::collapseAll); + addAction(tr("Collapse others"), [=]() { + m_view->collapseAll(); + m_view->setExpanded(viewIndex, expanded); + }); addAction(tr("Expand all"), view, &QTreeView::expandAll); } addSeparator(); // Add type-specific items - ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); - - // TODO: - // - Don't forget to check for the sort priority for "Send To... " - if (info->isOverwrite()) { addOverwriteActions(info); } diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 455daa76..054b980c 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -120,18 +120,7 @@ bool ModListSortProxy::lessThan(const QModelIndex &left, ModInfo::Ptr leftMod = ModInfo::getByIndex(leftIndex); ModInfo::Ptr rightMod = ModInfo::getByIndex(rightIndex); - bool lt = false; - - { - QModelIndex leftPrioIdx = left.sibling(left.row(), ModList::COL_PRIORITY); - QVariant leftPrio = leftPrioIdx.data(); - if (!leftPrio.isValid()) leftPrio = left.data(Qt::UserRole); - QModelIndex rightPrioIdx = right.sibling(right.row(), ModList::COL_PRIORITY); - QVariant rightPrio = rightPrioIdx.data(); - if (!rightPrio.isValid()) rightPrio = right.data(Qt::UserRole); - - lt = leftPrio.toInt() < rightPrio.toInt(); - } + bool lt = left.data(ModList::PriorityRole).toInt() < right.data(ModList::PriorityRole).toInt(); switch (left.column()) { case ModList::COL_FLAGS: { diff --git a/src/modlistview.cpp b/src/modlistview.cpp index f62089b5..80aa6495 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -67,7 +67,7 @@ ModListView::ModListView(QWidget* parent) , m_byPriorityProxy(nullptr) , m_byCategoryProxy(nullptr) , m_byNexusIdProxy(nullptr) - , m_scrollbar(new ViewMarkingScrollBar(this->model(), this)) + , m_scrollbar(new ViewMarkingScrollBar(this->model(), ModList::ScrollMarkRole, this)) { setVerticalScrollBar(m_scrollbar); MOBase::setCustomizableColumns(this); @@ -79,6 +79,13 @@ ModListView::ModListView(QWidget* parent) connect(this, &ModListView::customContextMenuRequested, this, &ModListView::onCustomContextMenuRequested); } +void ModListView::setModel(QAbstractItemModel* model) +{ + QTreeView::setModel(model); + setVerticalScrollBar(new ViewMarkingScrollBar(model, ModList::ScrollMarkRole, this)); +} + + void ModListView::refresh() { updateGroupByProxy(-1); @@ -593,12 +600,13 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo connect(m_byPriorityProxy, &ModListByPriorityProxy::expandItem, this, &ModListView::expandItem); m_byCategoryProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_CATEGORY, - Qt::UserRole, 0, Qt::UserRole + 2); + ModList::GroupingRole, 0, ModList::AggrRole); 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); + m_byNexusIdProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_MODID, + ModList::GroupingRole, QtGroupingProxy::FLAG_NOGROUPNAME | QtGroupingProxy::FLAG_NOSINGLE, + ModList::AggrRole); connect(this, &QTreeView::expanded, m_byNexusIdProxy, &QtGroupingProxy::expanded); connect(this, &QTreeView::collapsed, m_byNexusIdProxy, &QtGroupingProxy::collapsed); connect(m_byNexusIdProxy, &QtGroupingProxy::expandItem, this, &ModListView::expandItem); @@ -627,7 +635,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo connect(header(), &QHeaderView::sectionResized, [=](int logicalIndex, int oldSize, int newSize) { m_sortProxy->setColumnVisible(logicalIndex, newSize != 0); }); - GenericIconDelegate* contentDelegate = new GenericIconDelegate(this, Qt::UserRole + 3, ModList::COL_CONTENT, 150); + GenericIconDelegate* contentDelegate = new GenericIconDelegate(this, ModList::ContentsRole, ModList::COL_CONTENT, 150); ModFlagIconDelegate* flagDelegate = new ModFlagIconDelegate(this, ModList::COL_FLAGS, 120); ModConflictIconDelegate* conflictFlagDelegate = new ModConflictIconDelegate(this, ModList::COL_CONFLICTFLAGS, 80); @@ -722,12 +730,6 @@ void ModListView::saveState(Settings& s) const m_filters->saveState(s); } -void ModListView::setModel(QAbstractItemModel* model) -{ - QTreeView::setModel(model); - setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); -} - QRect ModListView::visualRect(const QModelIndex& index) const { // this shift the visualRect() from QTreeView to match the new actual diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 4d648a46..9e101f84 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -990,7 +990,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return checkstateData(modelIndex); } else if (role == Qt::ForegroundRole) { return foregroundData(modelIndex); - } else if (role == Qt::BackgroundRole || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { + } else if (role == Qt::BackgroundRole) { return backgroundData(modelIndex); } else if (role == Qt::FontRole) { return fontData(modelIndex); diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index f95226fc..589c5737 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -21,7 +21,7 @@ using namespace MOBase; PluginListView::PluginListView(QWidget *parent) : QTreeView(parent) , m_sortProxy(nullptr) - , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) + , m_Scrollbar(new ViewMarkingScrollBar(this->model(), Qt::BackgroundRole, this)) , m_didUpdateMasterList(false) { setVerticalScrollBar(m_Scrollbar); @@ -31,7 +31,7 @@ PluginListView::PluginListView(QWidget *parent) void PluginListView::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); - setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); + setVerticalScrollBar(new ViewMarkingScrollBar(model, Qt::BackgroundRole, this)); } int PluginListView::sortColumn() const diff --git a/src/viewmarkingscrollbar.cpp b/src/viewmarkingscrollbar.cpp index ed17120b..0991f171 100644 --- a/src/viewmarkingscrollbar.cpp +++ b/src/viewmarkingscrollbar.cpp @@ -4,18 +4,18 @@ #include #include -ViewMarkingScrollBar::ViewMarkingScrollBar(QAbstractItemModel *model, QWidget *parent, int role) +ViewMarkingScrollBar::ViewMarkingScrollBar(QAbstractItemModel* model, int role, QWidget *parent) : QScrollBar(parent) - , m_Model(model) - , m_Role(role) + , m_model(model) + , m_role(role) { // not implemented for horizontal sliders Q_ASSERT(this->orientation() == Qt::Vertical); } -void ViewMarkingScrollBar::paintEvent(QPaintEvent *event) +void ViewMarkingScrollBar::paintEvent(QPaintEvent* event) { - if (m_Model == nullptr) { + if (m_model == nullptr) { return; } QScrollBar::paintEvent(event); @@ -28,13 +28,13 @@ void ViewMarkingScrollBar::paintEvent(QPaintEvent *event) QRect handleRect = style()->subControlRect(QStyle::CC_ScrollBar, &styleOption, QStyle::SC_ScrollBarSlider, this); QRect innerRect = style()->subControlRect(QStyle::CC_ScrollBar, &styleOption, QStyle::SC_ScrollBarGroove, this); - auto indices = flatIndex(m_Model, 0, QModelIndex()); + auto indices = flatIndex(m_model, 0, QModelIndex()); painter.translate(innerRect.topLeft() + QPoint(0, 3)); qreal scale = static_cast(innerRect.height() - 3) / static_cast(indices.size()); for (int i = 0; i < indices.size(); ++i) { - QVariant data = indices[i].data(m_Role); + QVariant data = indices[i].data(m_role); if (data.isValid()) { QColor col = data.value(); painter.setPen(col); diff --git a/src/viewmarkingscrollbar.h b/src/viewmarkingscrollbar.h index 12a297d1..88d77039 100644 --- a/src/viewmarkingscrollbar.h +++ b/src/viewmarkingscrollbar.h @@ -1,21 +1,21 @@ #ifndef VIEWMARKINGSCROLLBAR_H #define VIEWMARKINGSCROLLBAR_H -#include #include +#include class ViewMarkingScrollBar : public QScrollBar { public: - static const int DEFAULT_ROLE = Qt::UserRole + 42; -public: - ViewMarkingScrollBar(QAbstractItemModel *model, QWidget *parent = 0, int role = DEFAULT_ROLE); + ViewMarkingScrollBar(QAbstractItemModel *model, int role, QWidget* parent = nullptr); + protected: - virtual void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event) override; + private: - QAbstractItemModel *m_Model; - int m_Role; + QAbstractItemModel* m_model; + int m_role; }; -- cgit v1.3.1 From 21ab4ad78b2b7007dc06ed432d707fc6edf98d6e Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 16:35:53 +0100 Subject: Minor cleaning in ModList. --- src/modlist.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index 26581ba5..d4b2cc53 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -595,16 +595,17 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) } result = true; emit dataChanged(index, index); - } else if (role == Qt::EditRole) { + } + else if (role == Qt::EditRole) { switch (index.column()) { case COL_NAME: { auto flags = info->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) - { + if (info->isSeparator()) { result = renameMod(modID, value.toString() + "_separator"); } - else + else { result = renameMod(modID, value.toString()); + } } break; case COL_PRIORITY: { bool ok = false; @@ -625,7 +626,6 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) int newID = value.toInt(&ok); if (ok) { info->setNexusID(newID); - emit modlistChanged(index, role); emit tutorialModlistUpdate(); result = true; } else { -- cgit v1.3.1 From c135920b16639136d9fa35598c0af4a7b14416b8 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 19:24:17 +0100 Subject: Fix highligth of collapsed separator. Move foreground color fix to delegate. --- src/modlist.cpp | 4 +--- src/modlistview.cpp | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index d4b2cc53..41b5c858 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -407,9 +407,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } else if (role == Qt::ForegroundRole) { - if ((modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) || (column == COL_NOTES)) && modInfo->color().isValid()) { - return ColorSettings::idealTextColor(modInfo->color()); - } else if (column == COL_NAME) { + if (column == COL_NAME) { int highlight = modInfo->getHighlight(); if (highlight & ModInfo::HIGHLIGHT_IMPORTANT) return QBrush(Qt::darkRed); diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 9f94354e..d54563a1 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -49,16 +49,21 @@ public: void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override { + // the parent version always overwrite the background brush, so + // we need to save it and restore it + auto backgroundColor = option->backgroundBrush.color(); QStyledItemDelegate::initStyleOption(option, index); - auto color = childrenColor(index, m_view, Qt::BackgroundRole); - if (color.isValid()) { - option->backgroundBrush = color; + + if (backgroundColor.isValid()) { + option->backgroundBrush = backgroundColor; } } void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { QStyleOptionViewItem opt(option); + + // remove items indentaiton when using collapsible separators if (index.column() == 0 && m_view->hasCollapsibleSeparators()) { if (!index.model()->hasChildren(index) && index.parent().isValid()) { auto parentIndex = index.parent().data(ModList::IndexRole).toInt(); @@ -67,6 +72,28 @@ public: } } } + + // compute required color from children, otherwise fallback to the + // color from the model, and draw the background here + auto color = childrenColor(index, m_view, Qt::BackgroundRole); + if (!color.isValid()) { + color = index.data(Qt::BackgroundRole).value(); + } + else { + // disable alternating row if the color is from the children + opt.features &= ~QStyleOptionViewItem::Alternate; + } + opt.backgroundBrush = color; + + // compute ideal foreground color for some rows + if (color.isValid()) { + if ((index.column() == ModList::COL_NAME + && ModInfo::getByIndex(index.data(ModList::IndexRole).toInt())->isSeparator()) + || index.column() == ModList::COL_NOTES) { + opt.palette.setBrush(QPalette::Text, ColorSettings::idealTextColor(color)); + } + } + QStyledItemDelegate::paint(painter, opt, index); } }; -- cgit v1.3.1 From 65577c71f3e51ae1f029c5fb104782e2a2fdca6d Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 12:43:17 +0100 Subject: Fix notification for removed mods. --- src/modlist.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index 41b5c858..b88c6a01 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -958,18 +958,19 @@ MOBase::IModInterface* ModList::getMod(const QString& name) const bool ModList::removeMod(MOBase::IModInterface* mod) { unsigned int index = ModInfo::getIndex(mod->name()); + bool result = false; if (index == UINT_MAX) { if (auto* p = dynamic_cast(mod)) { - return p->remove(); - } - else { - return false; + result = p->remove(); } } else { - return ModInfo::removeMod(index); + result = ModInfo::removeMod(index); + } + if (result) { + notifyModRemoved(mod->name()); } - notifyModRemoved(mod->name()); + return result; } MOBase::IModInterface* ModList::renameMod(MOBase::IModInterface* mod, const QString& name) -- cgit v1.3.1 From d86dec53822e049954d03ba18473dae08d74040f Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 13:03:51 +0100 Subject: Minor refactoring. --- src/mainwindow.cpp | 10 ---------- src/mainwindow.h | 6 ++---- src/modlist.cpp | 36 ++++++++++++++++++++---------------- src/modlist.h | 37 ++++++++++--------------------------- src/modlistview.cpp | 19 +++++++++---------- src/modlistview.h | 2 +- src/organizercore.cpp | 5 +---- 7 files changed, 43 insertions(+), 72 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 23050467..b979be86 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2226,16 +2226,6 @@ void MainWindow::fileMoved(const QString &filePath, const QString &oldOriginName } } -void MainWindow::modlistChanged(const QModelIndex&, int) -{ - m_OrganizerCore.currentProfile()->writeModlist(); -} - -void MainWindow::modlistChanged(const QModelIndexList&, int) -{ - m_OrganizerCore.currentProfile()->writeModlist(); -} - void MainWindow::modRemoved(const QString &fileName) { if (!fileName.isEmpty() && !QFileInfo(fileName).isAbsolute()) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 0db339f2..b8474bac 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -378,12 +378,10 @@ private slots: void updateStyle(const QString &style); - void modlistChanged(const QModelIndex &index, int role); - void modlistChanged(const QModelIndexList &indicies, int role); - void fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName); - void resizeLists(bool pluginListCustom); + void fileMoved(const QString& filePath, const QString& oldOriginName, const QString& newOriginName); + /** * @brief allow columns in mod list and plugin list to be resized */ diff --git a/src/modlist.cpp b/src/modlist.cpp index b88c6a01..cae40962 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -588,7 +588,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) m_Profile->setModEnabled(modID, enabled); m_Modified = true; m_LastCheck.restart(); - emit modlistChanged(index, role); + emit modStatesChanged({ index }); emit tutorialModlistUpdate(); } result = true; @@ -613,7 +613,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) } if (ok) { m_Profile->setModPriority(modID, newPriority); - emit modPrioritiesChanged({ modID }); + emit modPrioritiesChanged({ index }); result = true; } else { result = false; @@ -737,35 +737,34 @@ void ModList::changeModPriority(std::vector sourceIndices, int newPriority) if (m_Profile == nullptr) return; emit layoutAboutToBeChanged(); - Profile *profile = m_Profile; // sort the moving mods by ascending priorities std::sort(sourceIndices.begin(), sourceIndices.end(), - [profile](const int &LHS, const int &RHS) { - return profile->getModPriority(LHS) > profile->getModPriority(RHS); + [=](const int &LHS, const int &RHS) { + return m_Profile->getModPriority(LHS) > m_Profile->getModPriority(RHS); }); // move mods that are decreasing in priority for (std::vector::const_iterator iter = sourceIndices.begin(); iter != sourceIndices.end(); ++iter) { - int oldPriority = profile->getModPriority(*iter); + int oldPriority = m_Profile->getModPriority(*iter); if (oldPriority > newPriority) { - profile->setModPriority(*iter, newPriority); + m_Profile->setModPriority(*iter, newPriority); m_ModMoved(ModInfo::getByIndex(*iter)->name(), oldPriority, newPriority); } } // sort the moving mods by descending priorities std::sort(sourceIndices.begin(), sourceIndices.end(), - [profile](const int &LHS, const int &RHS) { - return profile->getModPriority(LHS) < profile->getModPriority(RHS); + [=](const int &LHS, const int &RHS) { + return m_Profile->getModPriority(LHS) < m_Profile->getModPriority(RHS); }); // if at least one mod is increasing in priority, the target index is // that of the row BELOW the dropped location, otherwise it's the one above for (std::vector::const_iterator iter = sourceIndices.begin(); iter != sourceIndices.end(); ++iter) { - int oldPriority = profile->getModPriority(*iter); + int oldPriority = m_Profile->getModPriority(*iter); if (oldPriority < newPriority) { --newPriority; break; @@ -775,16 +774,21 @@ void ModList::changeModPriority(std::vector sourceIndices, int newPriority) // move mods that are increasing in priority for (std::vector::const_iterator iter = sourceIndices.begin(); iter != sourceIndices.end(); ++iter) { - int oldPriority = profile->getModPriority(*iter); + int oldPriority = m_Profile->getModPriority(*iter); if (oldPriority < newPriority) { - profile->setModPriority(*iter, newPriority); + m_Profile->setModPriority(*iter, newPriority); m_ModMoved(ModInfo::getByIndex(*iter)->name(), oldPriority, newPriority); } } emit layoutChanged(); - emit modPrioritiesChanged(sourceIndices); + QModelIndexList indices; + for (auto& idx : sourceIndices) { + indices.append(index(idx, 0, QModelIndex())); + } + + emit modPrioritiesChanged(indices); } @@ -796,7 +800,7 @@ void ModList::changeModPriority(int sourceIndex, int newPriority) m_Profile->setModPriority(sourceIndex, newPriority); emit layoutChanged(); - emit modPrioritiesChanged({ sourceIndex }); + emit modPrioritiesChanged({ index(sourceIndex, 0) }); } void ModList::setOverwriteMarkers(const std::set &overwrite, const std::set &overwritten) @@ -1473,7 +1477,7 @@ void ModList::shiftModsPriority(const QModelIndexList& indices, int offset) notifyChange(index); } - emit modPrioritiesChanged(allIndex); + emit modPrioritiesChanged(indices); } void ModList::changeModsPriority(const QModelIndexList& indices, int priority) @@ -1513,7 +1517,7 @@ bool ModList::toggleState(const QModelIndexList& indices) m_Profile->setModsEnabled(modsToEnable, modsToDisable); - emit modlistChanged(indices, 0); + emit modStatesChanged(indices); emit tutorialModlistUpdate(); m_Modified = true; diff --git a/src/modlist.h b/src/modlist.h index e77ceb1f..6d4e0e91 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -270,13 +270,16 @@ public slots: signals: - /** - * @brief Emitted whenever the priority of mods changes - * - * The sorting of the list can only be manually changed if the list is sorted by priority - * in which case the move is intended to change the priority of a mod. - **/ - void modPrioritiesChanged(std::vector const& index); + // emitted when the priority of one or multiple mods have changed + // + // the sorting of the list can only be manually changed if the list is sorted by priority + // in which case the move is intended to change the priority of a mod. + // + void modPrioritiesChanged(const QModelIndexList& indices); + + // emitted when the state (active/inactive) of one or multiple mods have changed + // + void modStatesChanged(const QModelIndexList& indices); /** * @brief emitted when the model wants a text to be displayed by the UI @@ -312,26 +315,6 @@ signals: */ void modUninstalled(const QString &fileName); - /** - * @brief emitted whenever a row in the list has changed - * - * @param index the index of the changed field - * @param role role of the field that changed - * @note this signal must only be emitted if the row really did change. - * Slots handling this signal therefore do not have to verify that a change has happened - **/ - void modlistChanged(const QModelIndex &index, int role); - - /** - * @brief emitted whenever multiple row sin the list has changed - * - * @param indicies the list of indicies of the changed field - * @param role role of the field that changed - * @note this signal must only be emitted if the row really did change. - * Slots handling this signal therefore do not have to verify that a change has happened - **/ - void modlistChanged(const QModelIndexList &indicies, int role); - /** * @brief QML seems to handle overloaded signals poorly - create unique signal for tutorials */ diff --git a/src/modlistview.cpp b/src/modlistview.cpp index c334db93..f304a1b0 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -336,11 +336,11 @@ void ModListView::expandItem(const QModelIndex& index) } } -void ModListView::onModPrioritiesChanged(std::vector const& indices) +void ModListView::onModPrioritiesChanged(const QModelIndexList& indices) { // expand separator whose priority has changed and parents for (auto index : indices) { - auto idx = indexModelToView(m_core->modList()->index(index, 0)); + auto idx = indexModelToView(index); if (hasCollapsibleSeparators() && model()->hasChildren(idx)) { setExpanded(idx, true); } @@ -648,17 +648,16 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo mwui->currentCategoryLabel, mwui->clearFiltersButton, mwui->filtersSeparators }; - connect(m_core, &OrganizerCore::modInstalled, this, &ModListView::onModInstalled); - connect(core.modList(), &ModList::modPrioritiesChanged, this, &ModListView::onModPrioritiesChanged); - connect(core.modList(), &ModList::clearOverwrite, m_actions, &ModListViewActions::clearOverwrite); - connect(core.modList(), qOverload(&ModList::modlistChanged), [=]() { updateModCount(); }); - connect(core.modList(), qOverload(&ModList::modlistChanged), [=]() { updateModCount(); }); + connect(m_core, &OrganizerCore::modInstalled, [=](auto&& name) { onModInstalled(name); }); + connect(core.modList(), &ModList::modPrioritiesChanged, [=](auto&& indices) { onModPrioritiesChanged(indices); }); + connect(core.modList(), &ModList::clearOverwrite, [=] { m_actions->clearOverwrite(); }); + connect(core.modList(), &ModList::modStatesChanged, [=] { updateModCount(); }); m_byPriorityProxy = new ModListByPriorityProxy(core.currentProfile(), core, this); 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); + connect(this, &QTreeView::expanded, [=](auto&& name) { m_byPriorityProxy->expanded(name); }); + connect(this, &QTreeView::collapsed, [=](auto&& name) { m_byPriorityProxy->collapsed(name); }); + connect(m_byPriorityProxy, &ModListByPriorityProxy::expandItem, [=](auto&& index) { expandItem(index); }); m_byCategoryProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_CATEGORY, ModList::GroupingRole, 0, ModList::AggrRole); diff --git a/src/modlistview.h b/src/modlistview.h index a2e8505d..92b53960 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -165,7 +165,7 @@ protected slots: private: - void onModPrioritiesChanged(std::vector const& indices); + void onModPrioritiesChanged(const QModelIndexList& indices); void onModInstalled(const QString& modName); void onModFilterActive(bool filterActive); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 4113607c..2b1cbdc6 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -118,6 +118,7 @@ OrganizerCore::OrganizerCore(Settings &settings) connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString))); + connect(&m_ModList, &ModList::modStatesChanged, [=] { currentProfile()->writeModlist(); }); connect(NexusInterface::instance().getAccessManager(), SIGNAL(validateSuccessful(bool)), this, SLOT(loginSuccessful(bool))); @@ -235,10 +236,6 @@ void OrganizerCore::setUserInterface(IUserInterface* ui) } if (w) { - connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), w, - SLOT(modlistChanged(QModelIndex, int))); - connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), w, - SLOT(modlistChanged(QModelIndexList, int))); connect(&m_ModList, SIGNAL(showMessage(QString)), w, SLOT(showMessage(QString))); connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), w, -- cgit v1.3.1 From 9ac6e375754eb3ee536539c29f842417d8ddaaf6 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 13:40:26 +0100 Subject: Minor cleaning and fix in ModList. - Proper display of separator name for removal. - Replace usage of flags with isXXX. --- src/modlist.cpp | 206 ++++++++++++++++++++++++++++++++++---------------------- src/modlist.h | 6 ++ 2 files changed, 132 insertions(+), 80 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index cae40962..6d53c4f5 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -113,6 +113,22 @@ int ModList::columnCount(const QModelIndex &) const return COL_LASTCOLUMN + 1; } +QString ModList::getDisplayName(ModInfo::Ptr info) const +{ + QString name = info->name(); + if (info->isSeparator()) { + name = name.replace("_separator", ""); + } + return name; +} + +QString ModList::makeInternalName(ModInfo::Ptr info, QString name) const +{ + if (info->isSeparator()) { + name += "_separator"; + } + return name; +} QVariant ModList::getOverwriteData(int column, int role) const { @@ -218,15 +234,11 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const || (column == COL_CONTENT) || (column == COL_CONFLICTFLAGS)) { return QVariant(); - } else if (column == COL_NAME) { - auto flags = modInfo->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) - { - return modInfo->name().replace("_separator", ""); - } - else - return modInfo->name(); - } else if (column == COL_VERSION) { + } + else if (column == COL_NAME) { + return getDisplayName(modInfo); + } + else if (column == COL_VERSION) { VersionInfo verInfo = modInfo->version(); QString version = verInfo.displayString(); if (role != Qt::EditRole) { @@ -235,14 +247,17 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return version; - } else if (column == COL_PRIORITY) { + } + else if (column == COL_PRIORITY) { int priority = modInfo->getFixedPriority(); if (priority != INT_MIN) { return QVariant(); // hide priority for mods where it's fixed - } else { + } + else { return m_Profile->getModPriority(modIndex); } - } else if (column == COL_MODID) { + } + else if (column == COL_MODID) { int modID = modInfo->nexusId(); if (modID > 0) { return modID; @@ -250,7 +265,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const else { return QVariant(); } - } else if (column == COL_GAME) { + } + else if (column == COL_GAME) { if (m_PluginContainer != nullptr) { for (auto game : m_PluginContainer->plugins()) { if (game->gameShortName().compare(modInfo->gameName(), Qt::CaseInsensitive) == 0) @@ -258,10 +274,12 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return modInfo->gameName(); - } else if (column == COL_CATEGORY) { + } + else if (column == COL_CATEGORY) { if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { return tr("Non-MO"); - } else { + } + else { int category = modInfo->primaryCategory(); if (category != -1) { CategoryFactory &categoryFactory = CategoryFactory::instance(); @@ -269,51 +287,63 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const try { int categoryIdx = categoryFactory.getCategoryIndex(category); return categoryFactory.getCategoryName(categoryIdx); - } catch (const std::exception &e) { + } + catch (const std::exception &e) { log::error("failed to retrieve category name: {}", e.what()); return QString(); } - } else { + } + else { log::warn("category {} doesn't exist (may have been removed)", category); modInfo->setCategory(category, false); return QString(); } - } else { + } + else { return QVariant(); } } - } else if (column == COL_INSTALLTIME) { + } + else if (column == COL_INSTALLTIME) { // display installation time for mods that can be updated if (modInfo->creationTime().isValid()) { return modInfo->creationTime(); - } else { + } + else { return QVariant(); } - } else if (column == COL_NOTES) { + } + else if (column == COL_NOTES) { return modInfo->comments(); - } else { + } + else { return tr("invalid"); } - } else if ((role == Qt::CheckStateRole) && (column == 0)) { + } + else if ((role == Qt::CheckStateRole) && (column == 0)) { if (modInfo->canBeEnabled()) { return m_Profile->modEnabled(modIndex) ? Qt::Checked : Qt::Unchecked; - } else { + } + else { return QVariant(); } } else if (role == Qt::TextAlignmentRole) { - auto flags = modInfo->getFlags(); if (column == COL_NAME) { if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_CENTER) { return QVariant(Qt::AlignCenter | Qt::AlignVCenter); - } else { + } + else { return QVariant(Qt::AlignLeft | Qt::AlignVCenter); } - } else if (column == COL_VERSION) { + } + else if (column == COL_VERSION) { return QVariant(Qt::AlignRight | Qt::AlignVCenter); - } else if (column == COL_NOTES) { + } + else if (column == COL_NOTES) { return QVariant(Qt::AlignLeft | Qt::AlignVCenter); - } else { + } + else { return QVariant(Qt::AlignCenter | Qt::AlignVCenter); } } @@ -327,7 +357,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } if (categoryNames.count() != 0) { return categoryNames; - } else { + } + else { return QVariant(); } } @@ -335,10 +366,12 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const int priority = modInfo->getFixedPriority(); if (priority != INT_MIN) { return priority; - } else { + } + else { return m_Profile->getModPriority(modIndex); } - } else { + } + else { return modInfo->nexusId(); } } @@ -371,20 +404,19 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else if (role == Qt::FontRole) { QFont result; - auto flags = modInfo->getFlags(); if (column == COL_NAME) { - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) - { - //result.setCapitalization(QFont::AllUppercase); + if (modInfo->isSeparator()) { result.setItalic(true); - //result.setUnderline(true); result.setBold(true); - } else if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_INVALID) { + } + else if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_INVALID) { result.setItalic(true); } - } else if ((column == COL_CATEGORY) && (modInfo->hasFlag(ModInfo::FLAG_FOREIGN))) { + } + else if (column == COL_CATEGORY && modInfo->isForeign()) { result.setItalic(true); - } else if (column == COL_VERSION) { + } + else if (column == COL_VERSION) { if (modInfo->updateAvailable() || modInfo->downgradeAvailable()) { result.setWeight(QFont::Bold); } @@ -398,9 +430,11 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const if (column == COL_VERSION) { if (modInfo->updateAvailable()) { return QIcon(":/MO/gui/update_available"); - } else if (modInfo->downgradeAvailable()) { + } + else if (modInfo->downgradeAvailable()) { return QIcon(":/MO/gui/warning"); - } else if (modInfo->version().scheme() == VersionInfo::SCHEME_DATE) { + } + else if (modInfo->version().scheme() == VersionInfo::SCHEME_DATE) { return QIcon(":/MO/gui/version_date"); } } @@ -409,16 +443,21 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const else if (role == Qt::ForegroundRole) { if (column == COL_NAME) { int highlight = modInfo->getHighlight(); - if (highlight & ModInfo::HIGHLIGHT_IMPORTANT) + if (highlight & ModInfo::HIGHLIGHT_IMPORTANT) { return QBrush(Qt::darkRed); - else if (highlight & ModInfo::HIGHLIGHT_INVALID) + } + else if (highlight & ModInfo::HIGHLIGHT_INVALID) { return QBrush(Qt::darkGray); - } else if (column == COL_VERSION) { + } + } + else if (column == COL_VERSION) { if (!modInfo->newestVersion().isValid()) { return QVariant(); - } else if (modInfo->updateAvailable() || modInfo->downgradeAvailable()) { + } + else if (modInfo->updateAvailable() || modInfo->downgradeAvailable()) { return QBrush(Qt::red); - } else { + } + else { return QBrush(Qt::darkGreen); } } @@ -433,21 +472,28 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const bool archiveLooseOverwritten = m_ArchiveLooseOverwritten.find(modIndex) != m_ArchiveLooseOverwritten.end(); if (column == COL_NOTES && modInfo->color().isValid()) { return modInfo->color(); - } else if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { + } + else if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { return Settings::instance().colors().modlistContainsPlugin(); - } else if (overwritten || archiveLooseOverwritten) { + } + else if (overwritten || archiveLooseOverwritten) { return Settings::instance().colors().modlistOverwritingLoose(); - } else if (overwrite || archiveLooseOverwrite) { + } + else if (overwrite || archiveLooseOverwrite) { return Settings::instance().colors().modlistOverwrittenLoose(); - } else if (archiveOverwritten) { + } + else if (archiveOverwritten) { return Settings::instance().colors().modlistOverwritingArchive(); - } else if (archiveOverwrite) { + } + else if (archiveOverwrite) { return Settings::instance().colors().modlistOverwrittenArchive(); - } else if (modInfo->isSeparator() && modInfo->color().isValid() + } + else if (modInfo->isSeparator() && modInfo->color().isValid() && (role != ScrollMarkRole || Settings::instance().colors().colorSeparatorScrollbar())) { return modInfo->color(); - } else { + } + else { return QVariant(); } } @@ -461,7 +507,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } return result; - } else if (column == COL_CONFLICTFLAGS) { + } + else if (column == COL_CONFLICTFLAGS) { QString result; for (ModInfo::EConflictFlag flag : modInfo->getConflictFlags()) { @@ -470,16 +517,20 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } return result; - } else if (column == COL_CONTENT) { + } + else if (column == COL_CONTENT) { return contentsToToolTip(modInfo->getContents()); - } else if (column == COL_NAME) { + } + else if (column == COL_NAME) { try { return modInfo->getDescription(); - } catch (const std::exception &e) { + } + catch (const std::exception &e) { log::error("invalid mod description: {}", e.what()); return QString(); } - } else if (column == COL_VERSION) { + } + else if (column == COL_VERSION) { QString text = tr("installed version: \"%1\", newest version: \"%2\"").arg(modInfo->version().displayString(3)).arg(modInfo->newestVersion().displayString(3)); if (modInfo->downgradeAvailable()) { text += "
    " + tr("The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn " @@ -488,7 +539,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } if (modInfo->getNexusFileStatus() == 4) { text += "
    " + tr("This file has been marked as \"Old\". There is most likely an updated version of this file available."); - } else if (modInfo->getNexusFileStatus() == 6) { + } + else if (modInfo->getNexusFileStatus() == 6) { text += "
    " + tr("This file has been marked as \"Deleted\"! You may want to check for an update or remove the nexus ID from this mod!"); } if (modInfo->nexusId() > 0) { @@ -502,7 +554,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return text; - } else if (column == COL_CATEGORY) { + } + else if (column == COL_CATEGORY) { const std::set &categories = modInfo->getCategories(); std::wostringstream categoryString; categoryString << ToWString(tr("Categories:
    ")); @@ -514,16 +567,19 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } try { categoryString << "" << ToWString(categoryFactory.getCategoryName(categoryFactory.getCategoryIndex(*catIter))) << ""; - } catch (const std::exception &e) { + } + catch (const std::exception &e) { log::error("failed to generate tooltip: {}", e.what()); return QString(); } } return ToQString(categoryString.str()); - } else if (column == COL_NOTES) { + } + else if (column == COL_NOTES) { return getFlagText(ModInfo::FLAG_NOTES, modInfo); - } else { + } + else { return QVariant(); } } @@ -597,13 +653,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) else if (role == Qt::EditRole) { switch (index.column()) { case COL_NAME: { - auto flags = info->getFlags(); - if (info->isSeparator()) { - result = renameMod(modID, value.toString() + "_separator"); - } - else { - result = renameMod(modID, value.toString()); - } + result = renameMod(modID, makeInternalName(info, value.toString())); } break; case COL_PRIORITY: { bool ok = false; @@ -691,7 +741,6 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const } if (modelIndex.isValid()) { ModInfo::Ptr modInfo = ModInfo::getByIndex(modelIndex.row()); - std::vector flags = modInfo->getFlags(); if (modInfo->getFixedPriority() == INT_MIN) { result |= Qt::ItemIsDragEnabled; result |= Qt::ItemIsUserCheckable; @@ -700,9 +749,8 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const (modelIndex.column() == COL_MODID)) { result |= Qt::ItemIsEditable; } - if (((modelIndex.column() == COL_NAME) || - (modelIndex.column() == COL_NOTES)) - && (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end())) { + if ((modelIndex.column() == COL_NAME || modelIndex.column() == COL_NOTES) + && !modInfo->isForeign()) { result |= Qt::ItemIsEditable; } } @@ -1279,8 +1327,7 @@ void ModList::removeRowForce(int row, const QModelIndex &parent) if (wasEnabled) { emit removeOrigin(modInfo->name()); } - auto flags = modInfo->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) { + if (!modInfo->isBackup()) { emit modUninstalled(modInfo->installationFile()); } } @@ -1298,8 +1345,7 @@ bool ModList::removeRows(int row, int count, const QModelIndex &parent) if (count == 1) { ModInfo::Ptr modInfo = ModInfo::getByIndex(row); - std::vector flags = modInfo->getFlags(); - if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) && (QDir(modInfo->absolutePath()).count() > 2)) { + if (modInfo->isOverwrite() && QDir(modInfo->absolutePath()).count() > 2) { emit clearOverwrite(); success = true; } @@ -1314,7 +1360,7 @@ bool ModList::removeRows(int row, int count, const QModelIndex &parent) success = true; QMessageBox confirmBox(QMessageBox::Question, tr("Confirm"), - tr("Are you sure you want to remove \"%1\"?").arg(modInfo->name()), + tr("Are you sure you want to remove \"%1\"?").arg(getDisplayName(modInfo)), QMessageBox::Yes | QMessageBox::No); if (confirmBox.exec() == QMessageBox::Yes) { diff --git a/src/modlist.h b/src/modlist.h index 6d4e0e91..22703923 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -352,6 +352,12 @@ signals: private: + // retrieve the display name of a mod or convert from a user-provided + // name to internal name + // + QString getDisplayName(ModInfo::Ptr info) const; + QString makeInternalName(ModInfo::Ptr info, QString name) const; + QVariant getOverwriteData(int column, int role) const; QString getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const; -- cgit v1.3.1 From d96dcf02b131808a25045afc23667ed6a26274a6 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 19:07:50 +0100 Subject: Refactoring of ModInfo to give access to the core. Remove ModInfo::remove() completely. --- src/modinfo.cpp | 69 ++++++++++++++++++++--------------------- src/modinfo.h | 34 +++++++------------- src/modinfobackup.cpp | 4 +-- src/modinfobackup.h | 2 +- src/modinfoforeign.cpp | 13 +++----- src/modinfoforeign.h | 6 ++-- src/modinfooverwrite.cpp | 5 ++- src/modinfooverwrite.h | 3 +- src/modinforegular.cpp | 23 ++++++-------- src/modinforegular.h | 8 +---- src/modinfoseparator.cpp | 4 +-- src/modinfoseparator.h | 5 +-- src/modinfowithconflictinfo.cpp | 26 ++++++++-------- src/modinfowithconflictinfo.h | 10 +----- src/modlist.cpp | 11 +------ src/organizercore.cpp | 15 +++------ 16 files changed, 91 insertions(+), 147 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 6f3b530e..d5be538a 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -27,6 +27,8 @@ along with Mod Organizer. If not, see . #include "categories.h" #include "modinfodialog.h" +#include "organizercore.h" +#include "modlist.h" #include "overwriteinfodialog.h" #include "versioninfo.h" #include "thread_utils.h" @@ -37,6 +39,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -79,17 +82,17 @@ bool ModInfo::isRegularName(const QString& name) } -ModInfo::Ptr ModInfo::createFrom(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &dir, DirectoryEntry **directoryStructure) +ModInfo::Ptr ModInfo::createFrom(const QDir &dir, OrganizerCore& core) { QMutexLocker locker(&s_Mutex); ModInfo::Ptr result; if (isBackupName(dir.dirName())) { - result = ModInfo::Ptr(new ModInfoBackup(pluginContainer, game, dir, directoryStructure)); + result = ModInfo::Ptr(new ModInfoBackup(dir, core)); } else if (isSeparatorName(dir.dirName())) { - result = Ptr(new ModInfoSeparator(pluginContainer, game, dir, directoryStructure)); + result = Ptr(new ModInfoSeparator(dir, core)); } else { - result = ModInfo::Ptr(new ModInfoRegular(pluginContainer, game, dir, directoryStructure)); + result = ModInfo::Ptr(new ModInfoRegular(dir, core)); } result->m_Index = s_Collection.size(); s_Collection.push_back(result); @@ -100,23 +103,18 @@ ModInfo::Ptr ModInfo::createFromPlugin(const QString &modName, const QString &espName, const QStringList &bsaNames, ModInfo::EModType modType, - const MOBase::IPluginGame* game, - DirectoryEntry **directoryStructure, - PluginContainer *pluginContainer) { + OrganizerCore& core) { QMutexLocker locker(&s_Mutex); - ModInfo::Ptr result = ModInfo::Ptr( - new ModInfoForeign(modName, espName, bsaNames, modType, game, directoryStructure, pluginContainer)); + ModInfo::Ptr result = ModInfo::Ptr(new ModInfoForeign(modName, espName, bsaNames, modType, core)); result->m_Index = s_Collection.size(); s_Collection.push_back(result); return result; } -ModInfo::Ptr ModInfo::createFromOverwrite( - PluginContainer *pluginContainer, const MOBase::IPluginGame* game, - MOShared::DirectoryEntry **directoryStructure) +ModInfo::Ptr ModInfo::createFromOverwrite(OrganizerCore& core) { QMutexLocker locker(&s_Mutex); - ModInfo::Ptr overwrite = ModInfo::Ptr(new ModInfoOverwrite(pluginContainer, game, directoryStructure)); + ModInfo::Ptr overwrite = ModInfo::Ptr(new ModInfoOverwrite(core)); overwrite->m_Index = s_Collection.size(); s_Collection.push_back(overwrite); return overwrite; @@ -179,10 +177,20 @@ bool ModInfo::removeMod(unsigned int index) QMutexLocker locker(&s_Mutex); if (index >= s_Collection.size()) { - throw MyException(tr("remove: invalid mod index %1").arg(index)); + throw Exception(tr("remove: invalid mod index %1").arg(index)); } - // update the indices first + ModInfo::Ptr modInfo = s_Collection[index]; + + // remove the actual mod (this is the most likely to fail so we do this first) + if (modInfo->isRegular()) { + if (!shellDelete(QStringList(modInfo->absolutePath()), true)) { + reportError(tr("remove: failed to delete mod '%1' directory").arg(modInfo->name())); + return false; + } + } + + // update the indices s_ModsByName.erase(s_ModsByName.find(modInfo->name())); auto iter = s_ModsByModID.find(std::pair(modInfo->gameName(), modInfo->nexusId())); @@ -192,12 +200,6 @@ bool ModInfo::removeMod(unsigned int index) s_ModsByModID[std::pair(modInfo->gameName(), modInfo->nexusId())] = indices; } - // physically remove the mod directory - //TODO the return value is ignored because the indices were already removed here, so stopping - // would cause data inconsistencies. Instead we go through with the removal but the mod will show up - // again if the user refreshes - modInfo->remove(); - // finally, remove the mod from the collection s_Collection.erase(s_Collection.begin() + index); @@ -230,12 +232,9 @@ unsigned int ModInfo::findMod(const boost::function &filter } -void ModInfo::updateFromDisc(const QString &modDirectory, - DirectoryEntry **directoryStructure, - PluginContainer *pluginContainer, - bool displayForeign, - std::size_t refreshThreadCount, - MOBase::IPluginGame const *game) +void ModInfo::updateFromDisc( + const QString& modsDirectory, OrganizerCore& core, + bool displayForeign, std::size_t refreshThreadCount) { TimeThis tt("ModInfo::updateFromDisc()"); @@ -245,14 +244,15 @@ void ModInfo::updateFromDisc(const QString &modDirectory, s_Overwrite = nullptr; { // list all directories in the mod directory and make a mod out of each - QDir mods(QDir::fromNativeSeparators(modDirectory)); + QDir mods(QDir::fromNativeSeparators(modsDirectory)); mods.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QDirIterator modIter(mods); while (modIter.hasNext()) { - createFrom(pluginContainer, game, QDir(modIter.next()), directoryStructure); + createFrom(QDir(modIter.next()), core); } } + auto* game = core.managedGame(); UnmanagedMods *unmanaged = game->feature(); if (unmanaged != nullptr) { for (const QString &modName : unmanaged->mods(!displayForeign)) { @@ -262,14 +262,11 @@ void ModInfo::updateFromDisc(const QString &modDirectory, createFromPlugin(unmanaged->displayName(modName), unmanaged->referenceFile(modName).absoluteFilePath(), unmanaged->secondaryFiles(modName), - modType, - game, - directoryStructure, - pluginContainer); + modType, core); } } - s_Overwrite = createFromOverwrite(pluginContainer, game, directoryStructure); + s_Overwrite = createFromOverwrite(core); std::sort(s_Collection.begin(), s_Collection.end(), ModInfo::ByName); @@ -296,8 +293,8 @@ void ModInfo::updateIndices() } -ModInfo::ModInfo(PluginContainer *pluginContainer) - : m_PrimaryCategory(-1) +ModInfo::ModInfo(OrganizerCore& core) + : m_PrimaryCategory(-1), m_Core(core) { } diff --git a/src/modinfo.h b/src/modinfo.h index f0c7bcb5..08ed94f8 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "imodinterface.h" #include "versioninfo.h" +class OrganizerCore; class PluginContainer; class QDir; class QDateTime; @@ -107,12 +108,9 @@ public: // Static functions: /** * @brief Read the mod directory and Mod ModInfo objects for all subdirectories. */ - static void updateFromDisc(const QString &modDirectory, - MOShared::DirectoryEntry **directoryStructure, - PluginContainer *pluginContainer, - bool displayForeign, - std::size_t refreshThreadCount, - MOBase::IPluginGame const *game); + static void updateFromDisc( + const QString &modDirectory, OrganizerCore& core, + bool displayForeign, std::size_t refreshThreadCount); static void clear() { s_Collection.clear(); s_ModsByName.clear(); s_ModsByModID.clear(); } @@ -467,14 +465,6 @@ public: // Mutable operations: */ virtual bool setName(const QString& name) = 0; - /** - * @brief Deletes the mod from the disc. This does not update the global ModInfo structure or - * indices. - * - * @return true on success, false otherwise. - */ - virtual bool remove() = 0; - public: // Methods after this do not come from IModInterface: /** @@ -945,7 +935,7 @@ protected: /** * */ - ModInfo(PluginContainer *pluginContainer); + ModInfo(OrganizerCore& core); /** * @brief Prefetch content for this mod. @@ -959,6 +949,9 @@ protected: protected: + // the mod list + OrganizerCore& m_Core; + // the index of the mod in s_Collection, only valid after updateIndices() int m_Index; @@ -978,9 +971,7 @@ protected: * * @return pointer to the info-structure of the newly created/added mod. */ - static ModInfo::Ptr createFrom( - PluginContainer* pluginContainer, const MOBase::IPluginGame* game, - const QDir& dir, MOShared::DirectoryEntry** directoryStructure); + static ModInfo::Ptr createFrom(const QDir& dir, OrganizerCore& core); /** * @brief Create a new "foreign-managed" mod from a tuple of plugin and archives. @@ -992,12 +983,9 @@ protected: */ static ModInfo::Ptr createFromPlugin( const QString& modName, const QString& espName, const QStringList& bsaNames, - ModInfo::EModType modType, const MOBase::IPluginGame* game, - MOShared::DirectoryEntry** directoryStructure, PluginContainer* pluginContainer); + ModInfo::EModType modType, OrganizerCore& core); - static ModInfo::Ptr createFromOverwrite(PluginContainer* pluginContainer, - const MOBase::IPluginGame* game, - MOShared::DirectoryEntry** directoryStructure); + static ModInfo::Ptr createFromOverwrite(OrganizerCore& core); // update the m_Index attribute of all mods and the various mapping // diff --git a/src/modinfobackup.cpp b/src/modinfobackup.cpp index 6a34b86a..603e74f6 100644 --- a/src/modinfobackup.cpp +++ b/src/modinfobackup.cpp @@ -15,7 +15,7 @@ QString ModInfoBackup::getDescription() const } -ModInfoBackup::ModInfoBackup(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure) - : ModInfoRegular(pluginContainer, game, path, directoryStructure) +ModInfoBackup::ModInfoBackup(const QDir& path, OrganizerCore& core) + : ModInfoRegular(path, core) { } diff --git a/src/modinfobackup.h b/src/modinfobackup.h index bf5dc5e6..f25ee9cf 100644 --- a/src/modinfobackup.h +++ b/src/modinfobackup.h @@ -42,7 +42,7 @@ public: private: - ModInfoBackup(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure); + ModInfoBackup(const QDir& path, OrganizerCore& core); }; diff --git a/src/modinfoforeign.cpp b/src/modinfoforeign.cpp index 19413891..97d3f4e1 100644 --- a/src/modinfoforeign.cpp +++ b/src/modinfoforeign.cpp @@ -42,14 +42,11 @@ QString ModInfoForeign::getDescription() const return tr("This pseudo mod represents content managed outside MO. It isn't modified by MO."); } -ModInfoForeign::ModInfoForeign(const QString &modName, - const QString &referenceFile, - const QStringList &archives, - ModInfo::EModType modType, - const MOBase::IPluginGame* gamePlugin, - DirectoryEntry **directoryStructure, - PluginContainer *pluginContainer) - : ModInfoWithConflictInfo(pluginContainer, gamePlugin, directoryStructure), +ModInfoForeign::ModInfoForeign( + const QString &modName, const QString &referenceFile, + const QStringList &archives, ModInfo::EModType modType, + OrganizerCore& core) + : ModInfoWithConflictInfo(core), m_ReferenceFile(referenceFile), m_Archives(archives), m_ModType(modType) { m_CreationTime = QFileInfo(referenceFile).birthTime(); diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h index 0a4ec42a..31fd13aa 100644 --- a/src/modinfoforeign.h +++ b/src/modinfoforeign.h @@ -32,7 +32,6 @@ public: virtual void setIsEndorsed(bool) override {} virtual void setNeverEndorse() override {} virtual void setIsTracked(bool) override {} - virtual bool remove() override { return false; } virtual void endorse(bool) override {} virtual void track(bool) override {} virtual bool isEmpty() const override { return false; } @@ -81,9 +80,8 @@ public: protected: ModInfoForeign(const QString &modName, const QString &referenceFile, const QStringList &archives, ModInfo::EModType modType, - const MOBase::IPluginGame *gamePlugin, - MOShared::DirectoryEntry **directoryStructure, PluginContainer *pluginContainer); - + OrganizerCore &core); + private: QString m_Name; diff --git a/src/modinfooverwrite.cpp b/src/modinfooverwrite.cpp index 8aad6209..5ee7a0d9 100644 --- a/src/modinfooverwrite.cpp +++ b/src/modinfooverwrite.cpp @@ -6,8 +6,7 @@ #include #include -ModInfoOverwrite::ModInfoOverwrite(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, MOShared::DirectoryEntry **directoryStructure) - : ModInfoWithConflictInfo(pluginContainer, game, directoryStructure) +ModInfoOverwrite::ModInfoOverwrite(OrganizerCore& core) : ModInfoWithConflictInfo(core) { } @@ -60,7 +59,7 @@ QString ModInfoOverwrite::getDescription() const "modified (i.e. by the construction kit)"); } -QStringList ModInfoOverwrite::archives(bool checkOnDisk) +QStringList ModInfoOverwrite::archives(bool checkOnDisk) { QStringList result; QDir dir(this->absolutePath()); diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h index 7236d3bf..065a3ba2 100644 --- a/src/modinfooverwrite.h +++ b/src/modinfooverwrite.h @@ -34,7 +34,6 @@ public: virtual void setIsEndorsed(bool) override {} virtual void setNeverEndorse() override {} virtual void setIsTracked(bool) override {} - virtual bool remove() override { return false; } virtual void endorse(bool) override {} virtual void track(bool) override {} virtual bool alwaysEnabled() const override { return true; } @@ -78,7 +77,7 @@ public: virtual std::map clearPluginSettings(const QString& pluginName) override { return {}; } private: - ModInfoOverwrite(PluginContainer *pluginContainer, const MOBase::IPluginGame* game, MOShared::DirectoryEntry **directoryStructure); + ModInfoOverwrite(OrganizerCore& core); }; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 6363dd6e..94f2fc31 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -5,6 +5,9 @@ #include "report.h" #include "moddatacontent.h" #include "settings.h" +#include "organizercore.h" +#include "plugincontainer.h" +#include #include #include @@ -24,25 +27,25 @@ namespace { } } -ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, const IPluginGame *game, const QDir &path, DirectoryEntry **directoryStructure) - : ModInfoWithConflictInfo(pluginContainer, game, directoryStructure) +ModInfoRegular::ModInfoRegular(const QDir &path, OrganizerCore& core) + : ModInfoWithConflictInfo(core) , m_Name(path.dirName()) , m_Path(path.absolutePath()) , m_Repository() - , m_GameName(game->gameShortName()) + , m_GameName(core.managedGame()->gameShortName()) , m_IsAlternate(false) , m_Converted(false) , m_Validated(false) , m_MetaInfoChanged(false) , m_EndorsedState(EndorsedState::ENDORSED_UNKNOWN) , m_TrackedState(TrackedState::TRACKED_UNKNOWN) - , m_NexusBridge(pluginContainer) + , m_NexusBridge(&core.pluginContainer()) { m_CreationTime = QFileInfo(path.absolutePath()).birthTime(); // read out the meta-file for information readMeta(); - if (m_GameName.compare(game->gameShortName(), Qt::CaseInsensitive) != 0) - if (!game->primarySources().contains(m_GameName, Qt::CaseInsensitive)) + if (m_GameName.compare(core.managedGame()->gameShortName(), Qt::CaseInsensitive) != 0) + if (!core.managedGame()->primarySources().contains(m_GameName, Qt::CaseInsensitive)) m_IsAlternate = true; //populate m_Archives @@ -588,12 +591,6 @@ QColor ModInfoRegular::color() const return m_Color; } -bool ModInfoRegular::remove() -{ - m_MetaInfoChanged = false; - return shellDelete(QStringList(absolutePath()), true); -} - void ModInfoRegular::endorse(bool doEndorse) { if (doEndorse != (m_EndorsedState == EndorsedState::ENDORSED_TRUE)) { @@ -684,7 +681,7 @@ std::vector ModInfoRegular::getFlags() const std::set ModInfoRegular::doGetContents() const { - ModDataContent* contentFeature = m_GamePlugin->feature(); + ModDataContent* contentFeature = m_Core.managedGame()->feature(); if (contentFeature) { auto result = contentFeature->getContentsFor(fileTree()); diff --git a/src/modinforegular.h b/src/modinforegular.h index f2e0b82d..08660993 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -186,12 +186,6 @@ public: */ virtual void setIsTracked(bool tracked) override; - /** - * @brief delete the mod from the disc. This does not update the global ModInfo structure or indices - * @return true if the mod was successfully removed - **/ - bool remove() override; - /** * @brief endorse or un-endorse the mod * @param doEndorse if true, the mod is endorsed, if false, it's un-endorsed. @@ -424,7 +418,7 @@ protected: virtual std::set doGetContents() const override; - ModInfoRegular(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure); + ModInfoRegular(const QDir& path, OrganizerCore& core); private: diff --git a/src/modinfoseparator.cpp b/src/modinfoseparator.cpp index 58c8310a..02a67ecd 100644 --- a/src/modinfoseparator.cpp +++ b/src/modinfoseparator.cpp @@ -30,7 +30,7 @@ QString ModInfoSeparator::name() const } -ModInfoSeparator::ModInfoSeparator(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure) - : ModInfoRegular(pluginContainer, game, path, directoryStructure) +ModInfoSeparator::ModInfoSeparator(const QDir& path, OrganizerCore& core) + : ModInfoRegular(path, core) { } diff --git a/src/modinfoseparator.h b/src/modinfoseparator.h index c7df7184..eaff7c42 100644 --- a/src/modinfoseparator.h +++ b/src/modinfoseparator.h @@ -54,10 +54,7 @@ protected: private: - ModInfoSeparator( - PluginContainer* pluginContainer, - const MOBase::IPluginGame* game, const QDir& path, - MOShared::DirectoryEntry** directoryStructure); + ModInfoSeparator(const QDir& path, OrganizerCore& core); }; #endif diff --git a/src/modinfowithconflictinfo.cpp b/src/modinfowithconflictinfo.cpp index ad7eda3f..7a51a727 100644 --- a/src/modinfowithconflictinfo.cpp +++ b/src/modinfowithconflictinfo.cpp @@ -5,6 +5,7 @@ #include "shared/fileentry.h" #include +#include "organizercore.h" #include "iplugingame.h" #include "moddatachecker.h" #include "qdirfiletree.h" @@ -13,13 +14,12 @@ using namespace MOBase; using namespace MOShared; namespace fs = std::filesystem; -ModInfoWithConflictInfo::ModInfoWithConflictInfo( - PluginContainer *pluginContainer, const MOBase::IPluginGame* gamePlugin, DirectoryEntry **directoryStructure) - : ModInfo(pluginContainer), m_GamePlugin(gamePlugin), +ModInfoWithConflictInfo::ModInfoWithConflictInfo(OrganizerCore& core) : + ModInfo(core), m_FileTree([this]() { return QDirFileTree::makeTree(absolutePath()); }), m_Valid([this]() { return doIsValid(); }), m_Contents([this]() { return doGetContents(); }), - m_DirectoryStructure(directoryStructure), m_HasLooseOverwrite(false), m_HasHiddenFiles(false) {} + m_HasLooseOverwrite(false), m_HasHiddenFiles(false) {} void ModInfoWithConflictInfo::clearCaches() { @@ -95,8 +95,8 @@ void ModInfoWithConflictInfo::doConflictCheck() const bool hasHiddenFiles = false; int dataID = 0; - if ((*m_DirectoryStructure)->originExists(L"data")) { - dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID(); + if (m_Core.directoryStructure()->originExists(L"data")) { + dataID = m_Core.directoryStructure()->getOriginByName(L"data").getID(); } std::wstring name = ToWString(this->name()); @@ -106,8 +106,8 @@ void ModInfoWithConflictInfo::doConflictCheck() const m_ArchiveConflictState = CONFLICT_NONE; m_ArchiveConflictLooseState = CONFLICT_NONE; - if ((*m_DirectoryStructure)->originExists(name)) { - FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); + if (m_Core.directoryStructure()->originExists(name)) { + FilesOrigin &origin = m_Core.directoryStructure()->getOriginByName(name); std::vector files = origin.getFiles(); std::set checkedDirs; @@ -164,7 +164,7 @@ void ModInfoWithConflictInfo::doConflictCheck() const // If this is not the origin then determine the correct overwrite if (file->getOrigin() != origin.getID()) { - FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(file->getOrigin()); + FilesOrigin &altOrigin = m_Core.directoryStructure()->getOriginByID(file->getOrigin()); unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName())); if (!file->isFromArchive()) { if (!archiveData.isValid()) @@ -182,7 +182,7 @@ void ModInfoWithConflictInfo::doConflictCheck() const // Sort out the alternatives for (const auto& altInfo : alternatives) { if ((altInfo.originID() != dataID) && (altInfo.originID() != origin.getID())) { - FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(altInfo.originID()); + FilesOrigin &altOrigin = m_Core.directoryStructure()->getOriginByID(altInfo.originID()); QString altOriginName = ToQString(altOrigin.getName()); unsigned int altIndex = ModInfo::getIndex(altOriginName); if (!altInfo.isFromArchive()) { @@ -276,8 +276,8 @@ ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isLooseArchiveCo bool ModInfoWithConflictInfo::isRedundant() const { std::wstring name = ToWString(this->name()); - if ((*m_DirectoryStructure)->originExists(name)) { - FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); + if (m_Core.directoryStructure()->originExists(name)) { + FilesOrigin &origin = m_Core.directoryStructure()->getOriginByName(name); std::vector files = origin.getFiles(); bool ignore = false; for (auto iter = files.begin(); iter != files.end(); ++iter) { @@ -315,7 +315,7 @@ void ModInfoWithConflictInfo::prefetch() { } bool ModInfoWithConflictInfo::doIsValid() const { - auto mdc = m_GamePlugin->feature(); + auto mdc = m_Core.managedGame()->feature(); if (mdc) { auto qdirfiletree = fileTree(); diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h index 0d11fcb1..889f1246 100644 --- a/src/modinfowithconflictinfo.h +++ b/src/modinfowithconflictinfo.h @@ -91,10 +91,7 @@ protected: **/ virtual std::set doGetContents() const { return {}; } - ModInfoWithConflictInfo( - PluginContainer* pluginContainer, - const MOBase::IPluginGame* gamePlugin, - MOShared::DirectoryEntry** directoryStructure); + ModInfoWithConflictInfo(OrganizerCore& core); private: @@ -142,17 +139,12 @@ protected: */ virtual void prefetch() override; - // Current game plugin running in MO2: - MOBase::IPluginGame const * const m_GamePlugin; - private: MOBase::MemoizedLocked> m_FileTree; MOBase::MemoizedLocked m_Valid; MOBase::MemoizedLocked> m_Contents; - MOShared::DirectoryEntry **m_DirectoryStructure; - mutable EConflictType m_CurrentConflictState; mutable EConflictType m_ArchiveConflictState; mutable EConflictType m_ArchiveConflictLooseState; diff --git a/src/modlist.cpp b/src/modlist.cpp index 6d53c4f5..a2e59b62 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1009,16 +1009,7 @@ MOBase::IModInterface* ModList::getMod(const QString& name) const bool ModList::removeMod(MOBase::IModInterface* mod) { - unsigned int index = ModInfo::getIndex(mod->name()); - bool result = false; - if (index == UINT_MAX) { - if (auto* p = dynamic_cast(mod)) { - result = p->remove(); - } - } - else { - result = ModInfo::removeMod(index); - } + bool result = ModInfo::removeMod(ModInfo::getIndex(mod->name())); if (result) { notifyModRemoved(mod->name()); } diff --git a/src/organizercore.cpp b/src/organizercore.cpp index bf9308b8..d952bc4c 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -219,9 +219,9 @@ void OrganizerCore::updateExecutablesList() void OrganizerCore::updateModInfoFromDisc() { ModInfo::updateFromDisc( - m_Settings.paths().mods(), &m_DirectoryStructure, - m_PluginContainer, m_Settings.interface().displayForeign(), - m_Settings.refreshThreadCount(), managedGame()); + m_Settings.paths().mods(), *this, + m_Settings.interface().displayForeign(), + m_Settings.refreshThreadCount()); } void OrganizerCore::setUserInterface(IUserInterface* ui) @@ -692,8 +692,7 @@ MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) // shouldn't this use the existing mod in case of a merge? also, this does not refresh the indices // in the ModInfo structure - return ModInfo::createFrom(m_PluginContainer, m_GamePlugin, QDir(targetDirectory), &m_DirectoryStructure) - .data(); + return ModInfo::createFrom(QDir(targetDirectory), *this).data(); } void OrganizerCore::modDataChanged(MOBase::IModInterface *) @@ -1219,11 +1218,7 @@ void OrganizerCore::refresh(bool saveChanges) m_CurrentProfile->writeModlistNow(true); } - ModInfo::updateFromDisc( - m_Settings.paths().mods(), &m_DirectoryStructure, - m_PluginContainer, m_Settings.interface().displayForeign(), - m_Settings.refreshThreadCount(), managedGame()); - + updateModInfoFromDisc(); m_CurrentProfile->refreshModStatus(); m_ModList.notifyChange(-1); -- cgit v1.3.1 From 8de7b79a2b211805a4ba348e490a7ab8eac15eef Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 20:26:39 +0100 Subject: Move markers management for overwrite/overwritten to ModListView. --- src/modelutils.cpp | 34 ---------- src/modelutils.h | 5 -- src/modlist.cpp | 51 +-------------- src/modlist.h | 11 ---- src/modlistbypriorityproxy.cpp | 46 -------------- src/modlistbypriorityproxy.h | 1 - src/modlistview.cpp | 140 +++++++++++++++++++++++++++++++++++++---- src/modlistview.h | 27 +++++++- src/viewmarkingscrollbar.cpp | 22 +++---- src/viewmarkingscrollbar.h | 4 ++ 10 files changed, 168 insertions(+), 173 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modelutils.cpp b/src/modelutils.cpp index 7b54d258..83054806 100644 --- a/src/modelutils.cpp +++ b/src/modelutils.cpp @@ -90,38 +90,4 @@ QModelIndexList indexViewToModel(const QModelIndexList& index, const QAbstractIt return result; } -QColor childrenColor(const QModelIndex& index, QTreeView* view, int role) -{ - auto* model = view->model(); - auto rowIndex = index.sibling(index.row(), 0); - - if (model->hasChildren(rowIndex) && !view->isExpanded(rowIndex)) { - - // this is a non-expanded item - std::vector colors; - for (int i = 0; i < model->rowCount(rowIndex); ++i) { - auto childData = model->data(model->index(i, index.column(), rowIndex), role); - if (childData.isValid() && childData.canConvert()) { - colors.push_back(childData.value()); - } - } - - if (colors.empty()) { - return QColor(); - } - - int r = 0, g = 0, b = 0, a = 0; - for (auto& color : colors) { - r += color.red(); - g += color.green(); - b += color.blue(); - a += color.alpha(); - } - - return QColor(r / colors.size(), g / colors.size(), b / colors.size(), a / colors.size()); - } - - return QColor(); -} - } diff --git a/src/modelutils.h b/src/modelutils.h index cb7c9394..e6510941 100644 --- a/src/modelutils.h +++ b/src/modelutils.h @@ -20,11 +20,6 @@ QModelIndexList indexModelToView(const QModelIndexList& index, const QAbstractIt QModelIndex indexViewToModel(const QModelIndex& index, const QAbstractItemModel* model); QModelIndexList indexViewToModel(const QModelIndexList& index, const QAbstractItemModel* model); -// retrieve the color of the children of the given index for the given, or an invalid -// color if the item is expanded or the children do not have colors for the given role -// -QColor childrenColor(const QModelIndex& index, QTreeView* view, int role); - } #endif diff --git a/src/modlist.cpp b/src/modlist.cpp index a2e59b62..155a094f 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -464,33 +464,11 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } else if (role == Qt::BackgroundRole || role == ScrollMarkRole) { - bool overwrite = m_Overwrite.find(modIndex) != m_Overwrite.end(); - bool archiveOverwrite = m_ArchiveOverwrite.find(modIndex) != m_ArchiveOverwrite.end(); - bool archiveLooseOverwrite = m_ArchiveLooseOverwrite.find(modIndex) != m_ArchiveLooseOverwrite.end(); - bool overwritten = m_Overwritten.find(modIndex) != m_Overwritten.end(); - bool archiveOverwritten = m_ArchiveOverwritten.find(modIndex) != m_ArchiveOverwritten.end(); - bool archiveLooseOverwritten = m_ArchiveLooseOverwritten.find(modIndex) != m_ArchiveLooseOverwritten.end(); if (column == COL_NOTES && modInfo->color().isValid()) { return modInfo->color(); } - else if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { - return Settings::instance().colors().modlistContainsPlugin(); - } - else if (overwritten || archiveLooseOverwritten) { - return Settings::instance().colors().modlistOverwritingLoose(); - } - else if (overwrite || archiveLooseOverwrite) { - return Settings::instance().colors().modlistOverwrittenLoose(); - } - else if (archiveOverwritten) { - return Settings::instance().colors().modlistOverwritingArchive(); - } - else if (archiveOverwrite) { - return Settings::instance().colors().modlistOverwrittenArchive(); - } else if (modInfo->isSeparator() && modInfo->color().isValid() - && (role != ScrollMarkRole - || Settings::instance().colors().colorSeparatorScrollbar())) { + && (role != ScrollMarkRole || Settings::instance().colors().colorSeparatorScrollbar())) { return modInfo->color(); } else { @@ -851,27 +829,6 @@ void ModList::changeModPriority(int sourceIndex, int newPriority) emit modPrioritiesChanged({ index(sourceIndex, 0) }); } -void ModList::setOverwriteMarkers(const std::set &overwrite, const std::set &overwritten) -{ - m_Overwrite = overwrite; - m_Overwritten = overwritten; - notifyChange(0, rowCount() - 1); -} - -void ModList::setArchiveOverwriteMarkers(const std::set &overwrite, const std::set &overwritten) -{ - m_ArchiveOverwrite = overwrite; - m_ArchiveOverwritten = overwritten; - notifyChange(0, rowCount() - 1); -} - -void ModList::setArchiveLooseOverwriteMarkers(const std::set &overwrite, const std::set &overwritten) -{ - m_ArchiveLooseOverwrite = overwrite; - m_ArchiveLooseOverwritten = overwritten; - notifyChange(0, rowCount() - 1); -} - void ModList::setPluginContainer(PluginContainer *pluginContianer) { m_PluginContainer = pluginContianer; @@ -1393,12 +1350,6 @@ void ModList::notifyChange(int rowStart, int rowEnd) Guard g([&]{ m_InNotifyChange = false; }); if (rowStart < 0) { - m_Overwrite.clear(); - m_Overwritten.clear(); - m_ArchiveOverwrite.clear(); - m_ArchiveOverwritten.clear(); - m_ArchiveLooseOverwrite.clear(); - m_ArchiveLooseOverwritten.clear(); beginResetModel(); endResetModel(); } else { diff --git a/src/modlist.h b/src/modlist.h index 22703923..afbef7af 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -142,12 +142,8 @@ public: void changeModPriority(int sourceIndex, int newPriority); void changeModPriority(std::vector sourceIndices, int newPriority); - void setOverwriteMarkers(const std::set &overwrite, const std::set &overwritten); void setPluginContainer(PluginContainer *pluginContainer); - void setArchiveOverwriteMarkers(const std::set &overwrite, const std::set &overwritten); - void setArchiveLooseOverwriteMarkers(const std::set &overwrite, const std::set &overwritten); - bool modInfoAboutToChange(ModInfo::Ptr info); void modInfoChanged(ModInfo::Ptr info); @@ -420,13 +416,6 @@ private: QFontMetrics m_FontMetrics; - std::set m_Overwrite; - std::set m_Overwritten; - std::set m_ArchiveOverwrite; - std::set m_ArchiveOverwritten; - std::set m_ArchiveLooseOverwrite; - std::set m_ArchiveLooseOverwritten; - TModInfoChange m_ChangeInfo; SignalModInstalled m_ModInstalled; diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index 7ef540b0..749991d0 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -181,52 +181,6 @@ bool ModListByPriorityProxy::hasChildren(const QModelIndex& parent) const return item->children.size() > 0; } -QVariant ModListByPriorityProxy::data(const QModelIndex& index, int role) const -{ - auto sourceIndex = mapToSource(index); - if (!sourceIndex.isValid()) { - return QVariant(); - } - - auto sourceData = sourceModel()->data(sourceIndex, role); - if (role != Qt::BackgroundRole && role != ModList::ScrollMarkRole) { - return sourceData; - } - - if (!hasChildren(index)) { - return sourceData; - } - - bool expanded = !m_CollapsedItems.contains(index.sibling(index.row(), 0).data(Qt::DisplayRole).toString()); - - if (expanded) { - return sourceData; - } - - // this is a non-expanded item - std::vector colors; - for (int i = 0; i < rowCount(index); ++i) { - auto childData = sourceModel()->data(mapToSource(this->index(i, index.column(), index)), role); - if (childData.isValid() && childData.canConvert()) { - colors.push_back(childData.value()); - } - } - - if (true || colors.empty()) { - return sourceData; - } - - int r = 0, g = 0, b = 0, a = 0; - for (auto& color : colors) { - r += color.red(); - g += color.green(); - b += color.blue(); - a += color.alpha(); - } - - return QColor(r / colors.size(), g / colors.size(), b / colors.size(), a / colors.size()); -} - bool ModListByPriorityProxy::setData(const QModelIndex& index, const QVariant& value, int role) { // only care about the "name" column diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h index 5149b7a2..e5a8adff 100644 --- a/src/modlistbypriorityproxy.h +++ b/src/modlistbypriorityproxy.h @@ -37,7 +37,6 @@ public: int columnCount(const QModelIndex& index) const override; bool hasChildren(const QModelIndex& parent) const override; - QVariant data(const QModelIndex& index, int role) const override; bool setData(const QModelIndex& index, const QVariant& value, int role) 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 a460e4a4..f7db64e6 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -75,7 +75,7 @@ public: // compute required color from children, otherwise fallback to the // color from the model, and draw the background here - auto color = childrenColor(index, m_view, Qt::BackgroundRole); + auto color = m_view->markerColor(index); if (!color.isValid()) { color = index.data(Qt::BackgroundRole).value(); } @@ -98,6 +98,24 @@ public: } }; +class ModListViewMarkingScrollBar : public ViewMarkingScrollBar { + ModListView* m_view; +public: + ModListViewMarkingScrollBar(ModListView* view) : + ViewMarkingScrollBar(view, ModList::ScrollMarkRole), m_view(view) { } + + + QColor color(const QModelIndex& index) const override + { + auto color = m_view->markerColor(index); + if (!color.isValid()) { + color = ViewMarkingScrollBar::color(index); + } + return color; + } + +}; + ModListView::ModListView(QWidget* parent) : QTreeView(parent) , m_core(nullptr) @@ -105,7 +123,8 @@ ModListView::ModListView(QWidget* parent) , m_byPriorityProxy(nullptr) , m_byCategoryProxy(nullptr) , m_byNexusIdProxy(nullptr) - , m_scrollbar(new ViewMarkingScrollBar(this, ModList::ScrollMarkRole)) + , m_overwrite{ {}, {}, {}, {}, {}, {} } + , m_scrollbar(new ModListViewMarkingScrollBar(this)) { setVerticalScrollBar(m_scrollbar); MOBase::setCustomizableColumns(this); @@ -386,10 +405,7 @@ void ModListView::onModPrioritiesChanged(const QModelIndexList& indices) } // 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(); + setOverwriteMarkers(modInfo); } } } @@ -681,6 +697,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo connect(core.modList(), &ModList::modPrioritiesChanged, [=](auto&& indices) { onModPrioritiesChanged(indices); }); connect(core.modList(), &ModList::clearOverwrite, [=] { m_actions->clearOverwrite(); }); connect(core.modList(), &ModList::modStatesChanged, [=] { updateModCount(); }); + connect(core.modList(), &ModList::modelReset, [=] { clearOverwriteMarkers(); }); m_byPriorityProxy = new ModListByPriorityProxy(core.currentProfile(), core, this); m_byPriorityProxy->setSourceModel(core.modList()); @@ -935,6 +952,108 @@ void ModListView::onDoubleClicked(const QModelIndex& index) closePersistentEditor(index); } +void ModListView::clearOverwriteMarkers() +{ + m_overwrite.overwrite.clear(); + m_overwrite.overwritten.clear(); + m_overwrite.archiveOverwrite.clear(); + m_overwrite.archiveOverwritten.clear(); + m_overwrite.archiveLooseOverwrite.clear(); + m_overwrite.archiveLooseOverwritten.clear(); +} + +void ModListView::setOverwriteMarkers(const std::set& overwrite, const std::set& overwritten) +{ + m_overwrite.overwrite = overwrite; + m_overwrite.overwritten = overwritten; +} + +void ModListView::setArchiveOverwriteMarkers(const std::set& overwrite, const std::set& overwritten) +{ + m_overwrite.archiveOverwrite = overwrite; + m_overwrite.archiveOverwritten = overwritten; +} + +void ModListView::setArchiveLooseOverwriteMarkers(const std::set& overwrite, const std::set& overwritten) +{ + m_overwrite.archiveLooseOverwrite = overwrite; + m_overwrite.archiveLooseOverwritten = overwritten; +} + +void ModListView::setOverwriteMarkers(ModInfo::Ptr mod) +{ + if (mod) { + setOverwriteMarkers(mod->getModOverwrite(), mod->getModOverwritten()); + setArchiveOverwriteMarkers(mod->getModArchiveOverwrite(), mod->getModArchiveOverwritten()); + setArchiveLooseOverwriteMarkers(mod->getModArchiveLooseOverwrite(), mod->getModArchiveLooseOverwritten()); + } + else { + setOverwriteMarkers({}, {}); + setArchiveOverwriteMarkers({}, {}); + setArchiveLooseOverwriteMarkers({}, {}); + } + dataChanged(model()->index(0, 0), model()->index(model()->rowCount(), model()->columnCount())); + verticalScrollBar()->repaint(); +} + +QColor ModListView::markerColor(const QModelIndex& index) const +{ + unsigned int modIndex = index.data(ModList::IndexRole).toInt(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + bool overwrite = m_overwrite.overwrite.find(modIndex) != m_overwrite.overwrite.end(); + bool archiveOverwrite = m_overwrite.archiveOverwrite.find(modIndex) != m_overwrite.archiveOverwrite.end(); + bool archiveLooseOverwrite = m_overwrite.archiveOverwritten.find(modIndex) != m_overwrite.archiveOverwritten.end(); + bool overwritten = m_overwrite.overwritten.find(modIndex) != m_overwrite.overwritten.end(); + bool archiveOverwritten = m_overwrite.archiveOverwritten.find(modIndex) != m_overwrite.archiveOverwritten.end(); + bool archiveLooseOverwritten = m_overwrite.archiveLooseOverwritten.find(modIndex) != m_overwrite.archiveLooseOverwritten.end(); + + // TODO: Move this here + if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { + return Settings::instance().colors().modlistContainsPlugin(); + } + else if (overwritten || archiveLooseOverwritten) { + return Settings::instance().colors().modlistOverwritingLoose(); + } + else if (overwrite || archiveLooseOverwrite) { + return Settings::instance().colors().modlistOverwrittenLoose(); + } + else if (archiveOverwritten) { + return Settings::instance().colors().modlistOverwritingArchive(); + } + else if (archiveOverwrite) { + return Settings::instance().colors().modlistOverwrittenArchive(); + } + + // collapsed separator + auto rowIndex = index.sibling(index.row(), 0); + if (hasCollapsibleSeparators() && model()->hasChildren(rowIndex) && !isExpanded(rowIndex)) { + + std::vector colors; + for (int i = 0; i < model()->rowCount(rowIndex); ++i) { + auto childColor = markerColor(model()->index(i, index.column(), rowIndex)); + if (childColor.isValid()) { + colors.push_back(childColor); + } + } + + if (colors.empty()) { + return QColor(); + } + + int r = 0, g = 0, b = 0, a = 0; + for (auto& color : colors) { + r += color.red(); + g += color.green(); + b += color.blue(); + a += color.alpha(); + } + + return QColor(r / colors.size(), g / colors.size(), b / colors.size(), a / colors.size()); + } + + return QColor(); +} + void ModListView::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { if (hasCollapsibleSeparators()) { @@ -948,16 +1067,11 @@ void ModListView::onSelectionChanged(const QItemSelection& selected, const QItem if (selected.count()) { auto index = selected.indexes().last(); ModInfo::Ptr selectedMod = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); - m_core->modList()->setOverwriteMarkers(selectedMod->getModOverwrite(), selectedMod->getModOverwritten()); - m_core->modList()->setArchiveOverwriteMarkers(selectedMod->getModArchiveOverwrite(), selectedMod->getModArchiveOverwritten()); - m_core->modList()->setArchiveLooseOverwriteMarkers(selectedMod->getModArchiveLooseOverwrite(), selectedMod->getModArchiveLooseOverwritten()); + setOverwriteMarkers(selectedMod); } else { - m_core->modList()->setOverwriteMarkers({}, {}); - m_core->modList()->setArchiveOverwriteMarkers({}, {}); - m_core->modList()->setArchiveLooseOverwriteMarkers({}, {}); + setOverwriteMarkers(nullptr); } - verticalScrollBar()->repaint(); } diff --git a/src/modlistview.h b/src/modlistview.h index d67f2940..bf705573 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -166,10 +166,27 @@ protected slots: private: + friend class ModListStyledItemDelegated; + friend class ModListViewMarkingScrollBar; + void onModPrioritiesChanged(const QModelIndexList& indices); void onModInstalled(const QString& modName); void onModFilterActive(bool filterActive); + // overwrite markers + void clearOverwriteMarkers(); + void setOverwriteMarkers(const std::set& overwrite, const std::set& overwritten); + void setArchiveOverwriteMarkers(const std::set& overwrite, const std::set& overwritten); + void setArchiveLooseOverwriteMarkers(const std::set& overwrite, const std::set& overwritten); + + // set overwrite markers from the mod and repaint (if mod is nullptr, clear overwrite and repaint) + // + void setOverwriteMarkers(ModInfo::Ptr mod); + + // retrieve the marker color for the given index + // + QColor markerColor(const QModelIndex& index) const; + // get/set the selected items on the view, this method return/take indices // from the mod list model, not the view, so it's safe to restore // @@ -219,10 +236,18 @@ private: ModListSortProxy* m_sortProxy; ModListByPriorityProxy* m_byPriorityProxy; - QtGroupingProxy* m_byCategoryProxy; QtGroupingProxy* m_byNexusIdProxy; + struct OverwriteInfo { + std::set overwrite; + std::set overwritten; + std::set archiveOverwrite; + std::set archiveOverwritten; + std::set archiveLooseOverwrite; + std::set archiveLooseOverwritten; + } m_overwrite; + ViewMarkingScrollBar* m_scrollbar; bool m_inDragMoveEvent = false; diff --git a/src/viewmarkingscrollbar.cpp b/src/viewmarkingscrollbar.cpp index fb165922..56754237 100644 --- a/src/viewmarkingscrollbar.cpp +++ b/src/viewmarkingscrollbar.cpp @@ -15,6 +15,15 @@ ViewMarkingScrollBar::ViewMarkingScrollBar(QTreeView* view, int role) Q_ASSERT(this->orientation() == Qt::Vertical); } +QColor ViewMarkingScrollBar::color(const QModelIndex& index) const +{ + auto data = index.data(m_role); + if (data.canConvert()) { + return data.value(); + } + return QColor(); +} + void ViewMarkingScrollBar::paintEvent(QPaintEvent* event) { if (m_view->model() == nullptr) { @@ -36,18 +45,7 @@ void ViewMarkingScrollBar::paintEvent(QPaintEvent* event) qreal scale = static_cast(innerRect.height() - 3) / static_cast(indices.size()); for (int i = 0; i < indices.size(); ++i) { - QVariant data = indices[i].data(m_role); - QColor color; - - if (data.canConvert()) { - color = data.value(); - } - - auto childrenColor = MOShared::childrenColor(indices[i], m_view, m_role); - if (childrenColor.isValid()) { - color = childrenColor; - } - + QColor color = this->color(indices[i]); if (color.isValid()) { painter.setPen(color); painter.setBrush(color); diff --git a/src/viewmarkingscrollbar.h b/src/viewmarkingscrollbar.h index 6947a018..01b5a8c0 100644 --- a/src/viewmarkingscrollbar.h +++ b/src/viewmarkingscrollbar.h @@ -13,6 +13,10 @@ public: protected: void paintEvent(QPaintEvent *event) override; + // retrieve the color of the marker for the given index + // + virtual QColor color(const QModelIndex& index) const; + private: QTreeView* m_view; int m_role; -- cgit v1.3.1 From 94b3674d086f81479e71e265102b48c7607c3ef2 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 20:35:39 +0100 Subject: Move highlighting of mods containing selected plugins to mod view. --- src/modlist.cpp | 24 ------------------ src/modlist.h | 6 ----- src/modlistview.cpp | 66 ++++++++++++++++++++++++++++++++------------------ src/modlistview.h | 12 +++++++-- src/pluginlistview.cpp | 4 +-- 5 files changed, 54 insertions(+), 58 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index 155a094f..c89e53d5 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -874,30 +874,6 @@ int ModList::timeElapsedSinceLastChecked() const return m_LastCheck.elapsed(); } -void ModList::highlightMods( - const std::vector& pluginIndices, - const MOShared::DirectoryEntry &directoryEntry) -{ - for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) { - ModInfo::getByIndex(i)->setPluginSelected(false); - } - for (auto idx : pluginIndices) { - QString pluginName = m_Organizer->pluginList()->getName(idx); - - const MOShared::FileEntryPtr fileEntry = directoryEntry.findFile(pluginName.toStdWString()); - if (fileEntry.get() != nullptr) { - - QString originName = QString::fromStdWString(directoryEntry.getOriginByID(fileEntry->getOrigin()).getName()); - const auto index = ModInfo::getIndex(originName); - if (index != UINT_MAX) { - auto modInfo = ModInfo::getByIndex(index); - modInfo->setPluginSelected(true); - } - } - } - notifyChange(0, rowCount() - 1); -} - IModList::ModStates ModList::state(unsigned int modIndex) const { IModList::ModStates result; diff --git a/src/modlist.h b/src/modlist.h index afbef7af..9c963119 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -151,12 +151,6 @@ public: int timeElapsedSinceLastChecked() const; - // highlight mods containing the plugins at the given indices - // - void highlightMods( - const std::vector& pluginIndices, - const MOShared::DirectoryEntry &directoryEntry); - public: /** diff --git a/src/modlistview.cpp b/src/modlistview.cpp index f7db64e6..8351e429 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -22,6 +22,7 @@ #include "modlistdropinfo.h" #include "modlistcontextmenu.h" #include "genericicondelegate.h" +#include "shared/fileentry.h" #include "shared/directoryentry.h" #include "shared/filesorigin.h" #include "mainwindow.h" @@ -123,7 +124,7 @@ ModListView::ModListView(QWidget* parent) , m_byPriorityProxy(nullptr) , m_byCategoryProxy(nullptr) , m_byNexusIdProxy(nullptr) - , m_overwrite{ {}, {}, {}, {}, {}, {} } + , m_markers{ {}, {}, {}, {}, {}, {} } , m_scrollbar(new ModListViewMarkingScrollBar(this)) { setVerticalScrollBar(m_scrollbar); @@ -954,30 +955,30 @@ void ModListView::onDoubleClicked(const QModelIndex& index) void ModListView::clearOverwriteMarkers() { - m_overwrite.overwrite.clear(); - m_overwrite.overwritten.clear(); - m_overwrite.archiveOverwrite.clear(); - m_overwrite.archiveOverwritten.clear(); - m_overwrite.archiveLooseOverwrite.clear(); - m_overwrite.archiveLooseOverwritten.clear(); + m_markers.overwrite.clear(); + m_markers.overwritten.clear(); + m_markers.archiveOverwrite.clear(); + m_markers.archiveOverwritten.clear(); + m_markers.archiveLooseOverwrite.clear(); + m_markers.archiveLooseOverwritten.clear(); } void ModListView::setOverwriteMarkers(const std::set& overwrite, const std::set& overwritten) { - m_overwrite.overwrite = overwrite; - m_overwrite.overwritten = overwritten; + m_markers.overwrite = overwrite; + m_markers.overwritten = overwritten; } void ModListView::setArchiveOverwriteMarkers(const std::set& overwrite, const std::set& overwritten) { - m_overwrite.archiveOverwrite = overwrite; - m_overwrite.archiveOverwritten = overwritten; + m_markers.archiveOverwrite = overwrite; + m_markers.archiveOverwritten = overwritten; } void ModListView::setArchiveLooseOverwriteMarkers(const std::set& overwrite, const std::set& overwritten) { - m_overwrite.archiveLooseOverwrite = overwrite; - m_overwrite.archiveLooseOverwritten = overwritten; + m_markers.archiveLooseOverwrite = overwrite; + m_markers.archiveLooseOverwritten = overwritten; } void ModListView::setOverwriteMarkers(ModInfo::Ptr mod) @@ -996,19 +997,38 @@ void ModListView::setOverwriteMarkers(ModInfo::Ptr mod) verticalScrollBar()->repaint(); } +void ModListView::setHighlightedMods(const std::vector& pluginIndices) +{ + m_markers.highlight.clear(); + auto& directoryEntry = *m_core->directoryStructure(); + for (auto idx : pluginIndices) { + QString pluginName = m_core->pluginList()->getName(idx); + + const MOShared::FileEntryPtr fileEntry = directoryEntry.findFile(pluginName.toStdWString()); + if (fileEntry.get() != nullptr) { + QString originName = QString::fromStdWString(directoryEntry.getOriginByID(fileEntry->getOrigin()).getName()); + const auto index = ModInfo::getIndex(originName); + if (index != UINT_MAX) { + m_markers.highlight.insert(index); + } + } + } + dataChanged(model()->index(0, 0), model()->index(model()->rowCount(), model()->columnCount())); + verticalScrollBar()->repaint(); +} + QColor ModListView::markerColor(const QModelIndex& index) const { unsigned int modIndex = index.data(ModList::IndexRole).toInt(); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - bool overwrite = m_overwrite.overwrite.find(modIndex) != m_overwrite.overwrite.end(); - bool archiveOverwrite = m_overwrite.archiveOverwrite.find(modIndex) != m_overwrite.archiveOverwrite.end(); - bool archiveLooseOverwrite = m_overwrite.archiveOverwritten.find(modIndex) != m_overwrite.archiveOverwritten.end(); - bool overwritten = m_overwrite.overwritten.find(modIndex) != m_overwrite.overwritten.end(); - bool archiveOverwritten = m_overwrite.archiveOverwritten.find(modIndex) != m_overwrite.archiveOverwritten.end(); - bool archiveLooseOverwritten = m_overwrite.archiveLooseOverwritten.find(modIndex) != m_overwrite.archiveLooseOverwritten.end(); - - // TODO: Move this here - if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { + bool highligth = m_markers.highlight.find(modIndex) != m_markers.highlight.end(); + bool overwrite = m_markers.overwrite.find(modIndex) != m_markers.overwrite.end(); + bool archiveOverwrite = m_markers.archiveOverwrite.find(modIndex) != m_markers.archiveOverwrite.end(); + bool archiveLooseOverwrite = m_markers.archiveOverwritten.find(modIndex) != m_markers.archiveOverwritten.end(); + bool overwritten = m_markers.overwritten.find(modIndex) != m_markers.overwritten.end(); + bool archiveOverwritten = m_markers.archiveOverwritten.find(modIndex) != m_markers.archiveOverwritten.end(); + bool archiveLooseOverwritten = m_markers.archiveLooseOverwritten.find(modIndex) != m_markers.archiveLooseOverwritten.end(); + + if (highligth) { return Settings::instance().colors().modlistContainsPlugin(); } else if (overwritten || archiveLooseOverwritten) { diff --git a/src/modlistview.h b/src/modlistview.h index bf705573..c11c4fbf 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -118,6 +118,10 @@ public slots: // void refreshFilters(); + // set highligth markers + // + void setHighlightedMods(const std::vector& pluginIndices); + protected: friend class ModListContextMenu; @@ -239,14 +243,18 @@ private: QtGroupingProxy* m_byCategoryProxy; QtGroupingProxy* m_byNexusIdProxy; - struct OverwriteInfo { + struct MarkerInfos { + // conflicts std::set overwrite; std::set overwritten; std::set archiveOverwrite; std::set archiveOverwritten; std::set archiveLooseOverwrite; std::set archiveLooseOverwritten; - } m_overwrite; + + // selected plugins + std::set highlight; + } m_markers; ViewMarkingScrollBar* m_scrollbar; diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index d17b4a2f..7388ae40 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -206,9 +206,7 @@ void PluginListView::setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* for (auto& idx : indexViewToModel(selectionModel()->selectedRows())) { pluginIndices.push_back(idx.row()); } - m_core->modList()->highlightMods(pluginIndices, *m_core->directoryStructure()); - mwui->modList->verticalScrollBar()->repaint(); - mwui->modList->repaint(); + mwui->modList->setHighlightedMods(pluginIndices); }); // using a lambda here to avoid storing the mod list actions -- cgit v1.3.1 From e96a1f3ced974b5d1b02907bf9feabca97d2baa9 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sun, 3 Jan 2021 01:44:39 +0100 Subject: Fix indentation of indicators branches when using collapsible separators. --- src/moapplication.cpp | 17 ++++++++++------- src/modlist.cpp | 2 -- src/modlist.h | 10 ++++------ src/modlistview.cpp | 9 +++++++++ src/modlistview.h | 2 ++ 5 files changed, 25 insertions(+), 15 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/moapplication.cpp b/src/moapplication.cpp index 15fbdb3c..709bcbda 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -60,13 +60,13 @@ using namespace MOShared; // class ProxyStyle : public QProxyStyle { public: - ProxyStyle(QStyle *baseStyle = 0) + ProxyStyle(QStyle* baseStyle = 0) : QProxyStyle(baseStyle) { } - void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { - if(element == QStyle::PE_IndicatorItemViewItemDrop) { + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const { + if (element == QStyle::PE_IndicatorItemViewItemDrop) { // 1. full-width drop indicator QRect rect(option->rect); @@ -85,7 +85,7 @@ public: painter->setPen(pen); painter->setBrush(QBrush(col)); - if(rect.height() == 0) { + if (rect.height() == 0) { QPoint tri[3] = { rect.topLeft(), rect.topLeft() + QPoint(-5, 5), @@ -93,13 +93,16 @@ public: }; painter->drawPolygon(tri, 3); painter->drawLine(QPoint(rect.topLeft().x(), rect.topLeft().y()), rect.topRight()); - } else { + } + else { painter->drawRoundedRect(rect, 5, 5); } - } else { + } + else { QProxyStyle::drawPrimitive(element, option, painter, widget); } } + }; @@ -535,8 +538,8 @@ bool MOApplication::notify(QObject* receiver, QEvent* event) void MOApplication::updateStyle(const QString& fileName) { if (QStyleFactory::keys().contains(fileName)) { - setStyle(QStyleFactory::create(fileName)); setStyleSheet(""); + setStyle(new ProxyStyle(QStyleFactory::create(fileName))); } else { setStyle(new ProxyStyle(QStyleFactory::create(m_DefaultStyle))); if (QFile::exists(fileName)) { diff --git a/src/modlist.cpp b/src/modlist.cpp index c89e53d5..e541ff71 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -61,8 +61,6 @@ along with Mod Organizer. If not, see . using namespace MOBase; -const int ModList::ModUserRole = Qt::UserRole + QMetaEnum::fromType().keyCount(); - ModList::ModList(PluginContainer *pluginContainer, OrganizerCore *organizer) : QAbstractItemModel(organizer) , m_Organizer(organizer) diff --git a/src/modlist.h b/src/modlist.h index 9c963119..279ef71a 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -78,13 +78,11 @@ public: PriorityRole = Qt::UserRole + 5, // marking role for the scrollbar - ScrollMarkRole = Qt::UserRole + 6 - }; - - Q_ENUM(ModListRole) + ScrollMarkRole = Qt::UserRole + 6, - // this is the first available role - static const int ModUserRole; + // this is the first available role + ModUserRole = Qt::UserRole + 7 + }; enum EColumn { COL_NAME, diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 778b228d..c2942717 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -853,6 +853,15 @@ QRect ModListView::visualRect(const QModelIndex& index) const return rect; } +void ModListView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const +{ + QRect r(rect); + if (hasCollapsibleSeparators() && index.parent().isValid()) { + r.adjust(-indentation(), 0, 0 -indentation(), 0); + } + QTreeView::drawBranches(painter, r, index); +} + QModelIndexList ModListView::selectedIndexes() const { // during drag&drop events, we fake the return value of selectedIndexes() diff --git a/src/modlistview.h b/src/modlistview.h index c11c4fbf..b7d641ea 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -155,6 +155,8 @@ protected: bool toggleSelectionState(); bool copySelection(); + void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override; + void timerEvent(QTimerEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; -- cgit v1.3.1 From 1ed3fcaa9bb6caa16bec3f1505d003a1b54886cb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sun, 10 Jan 2021 10:19:40 +0100 Subject: NameRole -> GameNameRole. --- src/modlist.cpp | 2 +- src/modlist.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modlist.cpp') diff --git a/src/modlist.cpp b/src/modlist.cpp index e541ff71..ea1d2754 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -388,7 +388,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const else if (role == ContentsRole) { return contentsToIcons(modInfo->getContents()); } - else if (role == NameRole) { + else if (role == GameNameRole) { return modInfo->gameName(); } else if (role == PriorityRole) { diff --git a/src/modlist.h b/src/modlist.h index 279ef71a..1d94749c 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -73,7 +73,7 @@ public: // containing icon paths ContentsRole = Qt::UserRole + 3, - NameRole = Qt::UserRole + 4, + GameNameRole = Qt::UserRole + 4, PriorityRole = Qt::UserRole + 5, -- cgit v1.3.1