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/organizercore.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/organizercore.cpp') 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 -- 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/organizercore.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 6e802d7327495bbd64153e503b1aff44a80f0e0f Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 29 Dec 2020 01:07:36 +0100 Subject: Do not move installed mod after merge/replace. --- src/installationmanager.cpp | 96 +++++++++++++++++++++++++-------------------- src/installationmanager.h | 77 ++++++++++++++++++++++++++---------- src/organizercore.cpp | 14 +++---- 3 files changed, 118 insertions(+), 69 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 413f567a..ad7cdcd5 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -64,6 +64,12 @@ using namespace MOBase; using namespace MOShared; +InstallationResult::InstallationResult(IPluginInstaller::EInstallResult result) : + m_result(result), m_name(), m_iniTweaks(false), m_backup(false), m_merged(false), m_replaced(false) +{ +} + + template static T resolveFunction(QLibrary &lib, const char *name) { @@ -353,8 +359,7 @@ IPluginInstaller::EInstallResult InstallationManager::installArchive(GuessedValu // in earlier versions the modName was copied here and the copy passed to install. I don't know why I did this and it causes // a problem if this is called by the bundle installer and the bundled installer adds additional names that then end up being used, // because the caller will then not have the right name. - bool iniTweaks; - return install(archiveName, modName, iniTweaks, modId); + return install(archiveName, modName, modId).result(); } @@ -374,10 +379,13 @@ QString InstallationManager::generateBackupName(const QString &directoryName) co } -IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue &modName, bool *merge) +InstallationResult InstallationManager::testOverwrite(GuessedValue &modName) { QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory + "\\" + modName); + // this is only returned on success + InstallationResult result{ IPluginInstaller::RESULT_SUCCESS }; + while (QDir(targetDirectory).exists()) { Settings &settings(Settings::instance()); @@ -393,12 +401,14 @@ IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue QString backupDirectory = generateBackupName(targetDirectory); if (!copyDir(targetDirectory, backupDirectory, false)) { reportError(tr("Failed to create backup")); - return IPluginInstaller::RESULT_FAILED; + return { IPluginInstaller::RESULT_FAILED }; } } - if (merge != nullptr) { - *merge = (overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE); - } + + result.m_merged = overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE; + result.m_replaced = overwriteDialog.action() == QueryOverwriteDialog::ACT_REPLACE; + result.m_backup = overwriteDialog.backup(); + if (overwriteDialog.action() == QueryOverwriteDialog::ACT_RENAME) { bool ok = false; QString name = QInputDialog::getText(m_ParentWidget, tr("Mod Name"), tr("Name"), @@ -406,7 +416,7 @@ IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue if (ok && !name.isEmpty()) { modName.update(name, GUESS_USER); if (!ensureValidModName(modName)) { - return IPluginInstaller::RESULT_FAILED; + return { IPluginInstaller::RESULT_FAILED }; } targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory) + "/" + modName; } @@ -441,20 +451,20 @@ IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue } else { log::error("failed to restore original settings: {}", metaFilename); } - return IPluginInstaller::RESULT_SUCCESS; + return result; } else if (overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE) { - return IPluginInstaller::RESULT_SUCCESS; + return result; } else /* if (overwriteDialog.action() == QueryOverwriteDialog::ACT_NONE) */ { - return IPluginInstaller::RESULT_CANCELED; + return { IPluginInstaller::RESULT_CANCELED }; } } else { - return IPluginInstaller::RESULT_CANCELED; + return { IPluginInstaller::RESULT_CANCELED }; } } QDir().mkdir(targetDirectory); - return IPluginInstaller::RESULT_SUCCESS;; + return result; } @@ -473,27 +483,30 @@ bool InstallationManager::ensureValidModName(GuessedValue &name) const return true; } -IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue &modName, QString gameName, int modID, - const QString &version, const QString &newestVersion, - int categoryID, int fileCategoryID, const QString &repository) +InstallationResult InstallationManager::doInstall( + GuessedValue &modName, QString gameName, int modID, + const QString &version, const QString &newestVersion, + int categoryID, int fileCategoryID, const QString &repository) { if (!ensureValidModName(modName)) { - return IPluginInstaller::RESULT_FAILED; + return { IPluginInstaller::RESULT_FAILED }; } bool merge = false; // determine target directory - IPluginInstaller::EInstallResult result = testOverwrite(modName, &merge); - if (result != IPluginInstaller::RESULT_SUCCESS) { + InstallationResult result = testOverwrite(modName); + if (!result) { return result; } + result.m_name = modName; + QString targetDirectory = QDir(m_ModsDirectory + "/" + modName).canonicalPath(); QString targetDirectoryNative = QDir::toNativeSeparators(targetDirectory); log::debug("installing to \"{}\"", targetDirectoryNative); if (!extractFiles(targetDirectory, "", true, false)) { - return IPluginInstaller::RESULT_CANCELED; + return { IPluginInstaller::RESULT_CANCELED }; } // Copy the created files: @@ -547,7 +560,7 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue &modName, - bool &hasIniTweaks, - int modID) +InstallationResult InstallationManager::install( + const QString &fileName, GuessedValue &modName, int modID) { m_IsRunning = true; ON_BLOCK_EXIT([this]() { m_IsRunning = false; }); @@ -612,7 +623,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil QFileInfo fileInfo(fileName); if (!getSupportedExtensions().contains(fileInfo.suffix(), Qt::CaseInsensitive)) { reportError(tr("File format \"%1\" not supported").arg(fileInfo.suffix())); - return IPluginInstaller::RESULT_FAILED; + return InstallationResult(IPluginInstaller::RESULT_FAILED); } modName.setFilter(&fixDirectoryName); @@ -703,7 +714,6 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil std::shared_ptr filesTree = archiveOpen ? ArchiveFileTree::makeTree(*m_ArchiveHandler) : nullptr; - IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED; auto installers = m_PluginContainer->plugins(); @@ -711,6 +721,8 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil return lhs->priority() > rhs->priority(); }); + InstallationResult installResult(IPluginInstaller::RESULT_NOTATTEMPTED); + for (IPluginInstaller *installer : installers) { // don't use inactive installers (installer can't be null here but vc static code analysis thinks it could) if ((installer == nullptr) || !m_PluginContainer->isEnabled(installer)) { @@ -718,11 +730,11 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil } // try only manual installers if that was requested - if (installResult == IPluginInstaller::RESULT_MANUALREQUESTED) { + if (installResult.result() == IPluginInstaller::RESULT_MANUALREQUESTED) { if (!installer->isManualInstaller()) { continue; } - } else if (installResult != IPluginInstaller::RESULT_NOTATTEMPTED) { + } else if (installResult.result() != IPluginInstaller::RESULT_NOTATTEMPTED) { break; } @@ -732,9 +744,9 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil = dynamic_cast(installer); if ((installerSimple != nullptr) && (filesTree != nullptr) && (installer->isArchiveSupported(filesTree))) { - installResult + installResult.m_result = installerSimple->install(modName, filesTree, version, modID); - if (installResult == IPluginInstaller::RESULT_SUCCESS) { + if (installResult) { // Downcast to an actual ArchiveFileTree and map to the archive. Test if // the tree is still an ArchiveFileTree, otherwize it means the installer @@ -755,12 +767,13 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil // the simple installer only prepares the installation, the rest // works the same for all installers - installResult = doInstall(modName, gameName, modID, version, newestVersion, categoryID, fileCategoryID, repository); + installResult = doInstall(modName, gameName, modID, version, + newestVersion, categoryID, fileCategoryID, repository); } } } - if (installResult != IPluginInstaller::RESULT_CANCELED) { // custom case + if (installResult.result() != IPluginInstaller::RESULT_CANCELED) { // custom case IPluginInstallerCustom *installerCustom = dynamic_cast(installer); if ((installerCustom != nullptr) @@ -771,7 +784,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil std::set installerExt = installerCustom->supportedExtensions(); if (installerExt.find(fileInfo.suffix()) != installerExt.end()) { - installResult + installResult.m_result = installerCustom->install(modName, gameName, fileName, version, modID); unsigned int idx = ModInfo::getIndex(modName); if (idx != UINT_MAX) { @@ -787,7 +800,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil // act upon the installation result. at this point the files have already been // extracted to the correct location - switch (installResult) { + switch (installResult.result()) { case IPluginInstaller::RESULT_FAILED: { QMessageBox::information(qApp->activeWindow(), tr("Installation failed"), tr("Something went wrong while installing this mod."), @@ -798,10 +811,11 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil case IPluginInstaller::RESULT_SUCCESSCANCEL: { if (filesTree != nullptr) { auto iniTweakEntry = filesTree->find("INI Tweaks", FileTreeEntry::DIRECTORY); - hasIniTweaks = iniTweakEntry != nullptr + installResult.m_iniTweaks = iniTweakEntry != nullptr && !iniTweakEntry->astree()->empty(); } - return IPluginInstaller::RESULT_SUCCESS; + installResult.m_result = IPluginInstaller::RESULT_SUCCESS; + return installResult; } break; case IPluginInstaller::RESULT_NOTATTEMPTED: case IPluginInstaller::RESULT_MANUALREQUESTED: { @@ -811,7 +825,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil return installResult; } } - if (installResult == IPluginInstaller::RESULT_NOTATTEMPTED) { + if (installResult.result() == IPluginInstaller::RESULT_NOTATTEMPTED) { reportError(tr("None of the available installer plugins were able to handle that archive.\n" "This is likely due to a corrupted or incompatible download or unrecognized archive format.")); } @@ -877,14 +891,12 @@ void InstallationManager::notifyInstallationStart(QString const& archive, bool r } } -void InstallationManager::notifyInstallationEnd( - MOBase::IPluginInstaller::EInstallResult result, - ModInfo::Ptr newMod) +void InstallationManager::notifyInstallationEnd(const InstallationResult& result, ModInfo::Ptr newMod) { auto& installers = m_PluginContainer->plugins(); for (auto* installer : installers) { if (m_PluginContainer->isEnabled(installer)) { - installer->onInstallationEnd(result, newMod.get()); + installer->onInstallationEnd(result.result(), newMod.get()); } } } diff --git a/src/installationmanager.h b/src/installationmanager.h index 277c276a..a5ec11d9 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -37,15 +37,48 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include "plugincontainer.h" +// contains installation result from the manager, internal class +// for MO2 that is not forwarded to plugin +class InstallationResult { +public: + + // result status of the installation + // + auto result() const { return m_result; } + + // information about the installation, only valid for successful + // installation + // + QString name() const { return m_name; } + bool backupCreated() const { return m_backup; } + bool merged() const { return m_merged; } + bool replaced() const { return m_replaced; } + bool hasIniTweaks() const { return m_iniTweaks; } + bool mergedOrReplaced() const { return merged() || replaced(); } + + // check if the installation was a success + // + explicit operator bool() const { return result() == MOBase::IPluginInstaller::EInstallResult::RESULT_SUCCESS; } + +private: + + friend class InstallationManager; + + // create a failed result + InstallationResult(MOBase::IPluginInstaller::EInstallResult result = MOBase::IPluginInstaller::EInstallResult::RESULT_FAILED); + + MOBase::IPluginInstaller::EInstallResult m_result; + + QString m_name; + + bool m_iniTweaks; + bool m_backup; + bool m_merged; + bool m_replaced; + +}; + -/** - * @brief manages the installation of mod archives - * This currently supports two special kind of archives: - * - "simple" archives: properly packaged archives without options, so they can be extracted to the (virtual) data directory directly - * - "complex" bain archives: archives with options for the bain system. - * All other archives are managed through the manual "InstallDialog" - * @todo this may be a good place to support plugins - **/ class InstallationManager : public QObject, public MOBase::IInstallationManager { Q_OBJECT @@ -79,7 +112,7 @@ public: * @param result The result of the installation process. * @param currentMod The newly install mod, if result is SUCCESS, a null pointer otherwise. */ - void notifyInstallationEnd(MOBase::IPluginInstaller::EInstallResult result, ModInfo::Ptr newMod); + void notifyInstallationEnd(const InstallationResult& result, ModInfo::Ptr newMod); /** * @brief update the directory where mods are to be installed @@ -107,7 +140,7 @@ public: * @return true if the archive was installed, false if installation failed or was refused * @exception std::exception an exception may be thrown if the archive can't be opened (maybe the format is invalid or the file is damaged) **/ - MOBase::IPluginInstaller::EInstallResult install(const QString &fileName, MOBase::GuessedValue &modName, bool &hasIniTweaks, int modID = 0); + InstallationResult install(const QString &fileName, MOBase::GuessedValue &modName, int modID = 0); /** * @return true if the installation was canceled @@ -148,7 +181,7 @@ public: * @note The temporary file is automatically cleaned up after the installation. * @note This call can be very slow if the archive is large and "solid". */ - virtual QString extractFile(std::shared_ptr entry, bool silent = false) override; + QString extractFile(std::shared_ptr entry, bool silent = false) override; /** * @brief Extract the specified files from the currently opened archive to a temporary location. @@ -171,7 +204,7 @@ public: * IFileTree and thus to given a list of entries flattened (this was not possible with the * QStringList version since these were based on the name of the file inside the archive). */ - virtual QStringList extractFiles(std::vector> const& entries, bool silent = false) override; + QStringList extractFiles(std::vector> const& entries, bool silent = false) override; /** * @brief Create a new file on the disk corresponding to the given entry. @@ -184,7 +217,7 @@ public: * * @return the path to the created file. */ - virtual QString createFile(std::shared_ptr entry) override; + QString createFile(std::shared_ptr entry) override; /** * @brief Installs the given archive. @@ -195,22 +228,26 @@ public: * * @return the installation result. */ - virtual MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue &modName, const QString &archiveName, int modId = 0) override; + MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue &modName, const QString &archiveName, int modId = 0) override; /** - * @brief test if the specified mod name is free. If not, query the user how to proceed * @param modName current possible names for the mod - * @param merge if this value is not null, the value will be set to whether the use chose to merge or replace - * @return true if we can proceed with the installation, false if the user canceled or in case of an unrecoverable error + * + * @return an installation result containing information from the user. */ - virtual MOBase::IPluginInstaller::EInstallResult testOverwrite(MOBase::GuessedValue &modName, bool *merge = nullptr); + InstallationResult testOverwrite(MOBase::GuessedValue& modName); QString generateBackupName(const QString &directoryName) const; private: - MOBase::IPluginInstaller::EInstallResult doInstall(MOBase::GuessedValue &modName, QString gameName, - int modID, const QString &version, const QString &newestVersion, int categoryID, int fileCategoryID, const QString &repository); + // actually perform the installation (write files to the disk, etc.), returns the + // installation result + // + InstallationResult doInstall( + MOBase::GuessedValue &modName, QString gameName, + int modID, const QString &version, const QString &newestVersion, + int categoryID, int fileCategoryID, const QString &repository); /** * @brief Clean the list of created files by removing all entries that are not diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 159296b7..ecd4b34e 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -692,8 +692,8 @@ MOBase::IPluginGame *OrganizerCore::getGame(const QString &name) const MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) { - bool merge = false; - if (m_InstallationManager.testOverwrite(name, &merge) != IPluginInstaller::EInstallResult::RESULT_SUCCESS) { + auto result = m_InstallationManager.testOverwrite(name); + if (!result) { return nullptr; } @@ -706,7 +706,7 @@ MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat); - if (!merge) { + if (!result.merged()) { settingsFile.setValue("modid", 0); settingsFile.setValue("version", ""); settingsFile.setValue("newestVersion", ""); @@ -783,7 +783,7 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); m_InstallationManager.notifyInstallationStart(fileName, reinstallation, currentMod); auto result = m_InstallationManager.install(fileName, modName, hasIniTweaks); - if (result == IPluginInstaller::RESULT_SUCCESS) { + if (result) { MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); refresh(); @@ -865,7 +865,7 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); m_InstallationManager.notifyInstallationStart(fileName, false, currentMod); auto result = m_InstallationManager.install(fileName, modName, hasIniTweaks); - if (result == IPluginInstaller::RESULT_SUCCESS) { + if (result) { MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); refresh(); @@ -876,7 +876,7 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) modInfo = ModInfo::getByIndex(modIndex); modInfo->addInstalledFile(modID, fileID); - if (priority != -1) { + if (priority != -1 && !result.mergedOrReplaced()) { m_ModList.changeModPriority(modIndex, priority); } @@ -891,7 +891,7 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) } m_ModList.notifyModInstalled(modInfo.get()); - m_InstallationManager.notifyInstallationEnd(IPluginInstaller::RESULT_SUCCESS, modInfo); + m_InstallationManager.notifyInstallationEnd(result, modInfo); } else { reportError(tr("mod not found: %1").arg(qUtf8Printable(modName))); } -- 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/organizercore.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 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/organizercore.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 6443fa4c0e027faced0af9539533e1c404badf3b Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 17:21:20 +0100 Subject: Fix category management. --- src/mainwindow.cpp | 53 +++++++++++++++++---------------------------------- src/mainwindow.h | 9 +++------ src/organizercore.cpp | 8 +------- 3 files changed, 22 insertions(+), 48 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e01f984a..3af7079f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3654,12 +3654,7 @@ void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow, int refere } } -void MainWindow::addRemoveCategories_MenuHandler(int modIndex, const QModelIndex& rowIdx) { - QMenu *menu = qobject_cast(sender()); - if (menu == nullptr) { - log::error("not a menu?"); - return; - } +void MainWindow::addRemoveCategories_MenuHandler(QMenu* menu, int modIndex, const QModelIndex& rowIdx) { QList selected; for (const QModelIndex &idx : ui->modList->selectionModel()->selectedRows()) { @@ -3695,13 +3690,8 @@ void MainWindow::addRemoveCategories_MenuHandler(int modIndex, const QModelIndex refreshFilters(); } -void MainWindow::replaceCategories_MenuHandler(int modIndex) { - QMenu *menu = qobject_cast(sender()); - if (menu == nullptr) { - log::error("not a menu?"); - return; - } - +void MainWindow::replaceCategories_MenuHandler(QMenu* menu, int modIndex) +{ QList selected; for (const QModelIndex &idx : ui->modList->selectionModel()->selectedRows()) { selected.append(QPersistentModelIndex(idx)); @@ -3879,8 +3869,10 @@ void MainWindow::unignoreUpdate(int modIndex) } } -void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, - ModInfo::Ptr info) { +void MainWindow::setPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, + ModInfo::Ptr info) +{ + primaryCategoryMenu->clear(); const std::set &categories = info->getCategories(); for (int categoryID : categories) { int catIdx = m_CategoryFactory.getCategoryIndex(categoryID); @@ -3905,19 +3897,6 @@ void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, } } -void MainWindow::addPrimaryCategoryCandidates(int modIndex) -{ - QMenu *menu = qobject_cast(sender()); - if (menu == nullptr) { - log::error("not a menu?"); - return; - } - menu->clear(); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - - addPrimaryCategoryCandidates(menu, modInfo); -} - void MainWindow::enableVisibleMods() { if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really enable all visible mods?"), @@ -4292,10 +4271,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) ModInfo::Ptr info = ModInfo::getByIndex(modIndex); std::vector flags = info->getFlags(); - // Context menu for overwrites + // context menu for overwrites if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { if (QDir(info->absolutePath()).count() > 2) { - menu.addAction(tr("Sync to Mods..."), &m_OrganizerCore, SLOT(syncOverwrite())); + menu.addAction(tr("Sync to Mods..."), [=]() { m_OrganizerCore.syncOverwrite(); }); menu.addAction(tr("Create Mod..."), [=]() { createModFromOverwrite(); }); menu.addAction(tr("Move content to Mod..."), [=]() { moveOverwriteContentToExistingMod(); }); menu.addAction(tr("Clear Overwrite..."), [=]() { clearOverwrite(); }); @@ -4303,7 +4282,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); } - // Context menu for mod backups + // context menu for mod backups else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { menu.addAction(tr("Restore Backup"), [=]() { restoreBackup_clicked(modIndex); }); menu.addAction(tr("Remove Backup..."), [=]() { removeMod_clicked(modIndex); }); @@ -4332,10 +4311,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); - connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(modIndex, contextIdx); }); + connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); addMenuAsPushButton(&menu, addRemoveCategoriesMenu); QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); - connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { addPrimaryCategoryCandidates(modIndex); }); + connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); addMenuAsPushButton(&menu, primaryCategoryMenu); menu.addSeparator(); menu.addAction(tr("Rename Separator..."), [=]() { renameMod_clicked(); }); @@ -4350,17 +4329,21 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); } + + // foregin else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { addModSendToContextMenu(&menu); } + + // regular else { QMenu* addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); - connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(modIndex, contextIdx); }); + connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); addMenuAsPushButton(&menu, addRemoveCategoriesMenu); QMenu* primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); - connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { addPrimaryCategoryCandidates(modIndex); }); + connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); addMenuAsPushButton(&menu, primaryCategoryMenu); menu.addSeparator(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 97bca68b..9e9e9591 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -129,8 +129,6 @@ public: void saveArchiveList(); - void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info); - void installTranslator(const QString &name); void displayModInformation( @@ -434,10 +432,9 @@ private slots: void originModified(int originID); - void addRemoveCategories_MenuHandler(int modIndex, const QModelIndex& rowIdx); - void replaceCategories_MenuHandler(int modIndex); - - void addPrimaryCategoryCandidates(int modIndex); + void setPrimaryCategoryCandidates(QMenu* menu, ModInfo::Ptr info); + void addRemoveCategories_MenuHandler(QMenu* menu, int modIndex, const QModelIndex& rowIdx); + void replaceCategories_MenuHandler(QMenu* menu, int modIndex); void modInstalled(const QString &modName); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 9e3528ef..1a6083b4 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1785,13 +1785,7 @@ void OrganizerCore::loginFailedUpdate(const QString &message) void OrganizerCore::syncOverwrite() { - 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(); - }); - - ModInfo::Ptr modInfo = ModInfo::getByIndex(overwriteIndex); + ModInfo::Ptr modInfo = ModInfo::getOverwrite(); SyncOverwriteDialog syncDialog(modInfo->absolutePath(), m_DirectoryStructure, qApp->activeWindow()); if (syncDialog.exec() == QDialog::Accepted) { -- cgit v1.3.1 From 8a88421fd9748f64163f18d8b89ea9d651402014 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 22:56:34 +0100 Subject: Start moving modlist context menu actions to separate structures. --- src/CMakeLists.txt | 2 + src/mainwindow.cpp | 337 ++------------------------------------------ src/mainwindow.h | 7 - src/modlistcontextmenu.cpp | 273 ++++++++++++++++++++++++++++++++++++ src/modlistcontextmenu.h | 39 ++++++ src/modlistview.cpp | 9 +- src/modlistview.h | 9 +- src/modlistviewactions.cpp | 339 +++++++++++++++++++++++++++++++++++++++++++++ src/modlistviewactions.h | 55 ++++++++ src/organizercore.cpp | 12 +- src/organizercore.h | 5 +- 11 files changed, 742 insertions(+), 345 deletions(-) create mode 100644 src/modlistcontextmenu.cpp create mode 100644 src/modlistcontextmenu.h create mode 100644 src/modlistviewactions.cpp create mode 100644 src/modlistviewactions.h (limited to 'src/organizercore.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93b9e768..4c7bcd46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,6 +138,8 @@ add_filter(NAME src/modlist GROUPS modlistsortproxy modlistbypriorityproxy modlistview + modlistviewactions + modlistcontextmenu ) add_filter(NAME src/plugins GROUPS diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bafe05f5..58061c96 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -58,8 +58,6 @@ along with Mod Organizer. If not, see . #include "filedialogmemory.h" #include "tutorialmanager.h" #include "selectiondialog.h" -#include "csvbuilder.h" -#include "savetextasdialog.h" #include "problemsdialog.h" #include "previewdialog.h" #include "browserdialog.h" @@ -86,6 +84,8 @@ along with Mod Organizer. If not, see . #include "envshortcut.h" #include "browserdialog.h" #include "modlistbypriorityproxy.h" +#include "modlistviewactions.h" +#include "modlistcontextmenu.h" #include "directoryrefresher.h" #include "shared/directoryentry.h" @@ -392,9 +392,7 @@ MainWindow::MainWindow(Settings &settings m_LinkStartMenu = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu())); ui->linkButton->setMenu(linkMenu); - QMenu *listOptionsMenu = new QMenu(ui->listOptionsBtn); - initModListContextMenu(listOptionsMenu); - ui->listOptionsBtn->setMenu(listOptionsMenu); + ui->listOptionsBtn->setMenu(new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, ui->listOptionsBtn)); ui->openFolderMenu->setMenu(openFolderMenu()); @@ -537,7 +535,7 @@ MainWindow::MainWindow(Settings &settings void MainWindow::setupModList() { - ui->modList->setup(m_OrganizerCore, ui); + ui->modList->setup(m_OrganizerCore, new ModListViewActions(m_OrganizerCore, *m_Filters, m_CategoryFactory, this, ui->modList), ui); connect(ui->modList, &ModListView::removeSelectedMods, [=]() { removeMod_clicked(-1); }); @@ -2076,30 +2074,6 @@ void MainWindow::on_tabWidget_currentChanged(int index) } } - -void MainWindow::installMod(QString fileName) -{ - try { - if (fileName.isEmpty()) { - QStringList extensions = m_OrganizerCore.installationManager()->getSupportedExtensions(); - for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) { - *iter = "*." + *iter; - } - - fileName = FileDialogMemory::getOpenFileName("installMod", this, tr("Choose Mod"), QString(), - tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); - } - - if (fileName.isEmpty()) { - return; - } else { - m_OrganizerCore.installMod(fileName, false, nullptr, QString()); - } - } catch (const std::exception &e) { - reportError(e.what()); - } -} - void MainWindow::on_startButton_clicked() { const Executable* selectedExecutable = getSelectedExecutable(); @@ -2192,7 +2166,7 @@ void MainWindow::tutorialTriggered() void MainWindow::on_actionInstallMod_triggered() { - installMod(); + ui->modList->actions().installMod(); } void MainWindow::on_action_Refresh_triggered() @@ -2328,7 +2302,7 @@ void MainWindow::showError(const QString &message) void MainWindow::installMod_clicked() { - installMod(); + ui->modList->actions().installMod(); } void MainWindow::modRenamed(const QString &oldName, const QString &newName) @@ -3173,87 +3147,6 @@ void MainWindow::information_clicked(int modIndex) } } -void MainWindow::createEmptyMod_clicked(int modIndex) -{ - GuessedValue name; - name.setFilter(&fixDirectoryName); - - while (name->isEmpty()) { - bool ok; - name.update(QInputDialog::getText(this, tr("Create Mod..."), - tr("This will create an empty mod.\n" - "Please enter a name:"), QLineEdit::Normal, "", &ok), - GUESS_USER); - if (!ok) { - return; - } - } - - if (m_OrganizerCore.modList()->getMod(name) != nullptr) { - reportError(tr("A mod with this name already exists")); - return; - } - - int newPriority = -1; - if (modIndex >= 0 && ui->modList->sortColumn() == ModList::COL_PRIORITY) { - newPriority = m_OrganizerCore.currentProfile()->getModPriority(modIndex); - } - - IModInterface *newMod = m_OrganizerCore.createMod(name); - if (newMod == nullptr) { - return; - } - - m_OrganizerCore.refresh(); - - if (newPriority >= 0) { - m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); - } -} - -void MainWindow::createSeparator_clicked(int modIndex) -{ - GuessedValue name; - name.setFilter(&fixDirectoryName); - while (name->isEmpty()) - { - bool ok; - name.update(QInputDialog::getText(this, tr("Create Separator..."), - tr("This will create a new separator.\n" - "Please enter a name:"), QLineEdit::Normal, "", &ok), - GUESS_USER); - if (!ok) { return; } - } - if (m_OrganizerCore.modList()->getMod(name) != nullptr) - { - reportError(tr("A separator with this name already exists")); - return; - } - name->append("_separator"); - if (m_OrganizerCore.modList()->getMod(name) != nullptr) - { - return; - } - - int newPriority = -1; - if (modIndex >= 0 && ui->modList->sortColumn() == ModList::COL_PRIORITY) - { - newPriority = m_OrganizerCore.currentProfile()->getModPriority(modIndex); - } - - if (m_OrganizerCore.createMod(name) == nullptr) { return; } - m_OrganizerCore.refresh(); - - if (newPriority >= 0) - { - m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); - } - - if (auto c=m_OrganizerCore.settings().colors().previousSeparatorColor()) { - ModInfo::getByIndex(ModInfo::getIndex(name))->setColor(*c); - } -} - void MainWindow::setColor_clicked(int modIndex) { auto& settings = m_OrganizerCore.settings(); @@ -3917,22 +3810,6 @@ void MainWindow::setPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, } } -void MainWindow::enableVisibleMods() -{ - if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really enable all visible mods?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - ui->modList->enableAllVisible(); - } -} - -void MainWindow::disableVisibleMods() -{ - if (QMessageBox::question(nullptr, tr("Confirm"), tr("Really disable all visible mods?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - ui->modList->disableAllVisible(); - } -} - void MainWindow::openInstanceFolder() { QString dataPath = qApp->property("dataPath").toString(); @@ -3998,175 +3875,6 @@ void MainWindow::openMyGamesFolder() shell::Explore(m_OrganizerCore.managedGame()->documentsDirectory()); } - -void MainWindow::exportModListCSV() -{ - //SelectionDialog selection(tr("Choose what to export")); - - //selection.addChoice(tr("Everything"), tr("All installed mods are included in the list"), 0); - //selection.addChoice(tr("Active Mods"), tr("Only active (checked) mods from your current profile are included"), 1); - //selection.addChoice(tr("Visible"), tr("All mods visible in the mod list are included"), 2); - - QDialog selection(this); - QGridLayout *grid = new QGridLayout; - selection.setWindowTitle(tr("Export to csv")); - - QLabel *csvDescription = new QLabel(); - csvDescription->setText(tr("CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet.\nYou can also use online editors and converters instead.")); - grid->addWidget(csvDescription); - - QGroupBox *groupBoxRows = new QGroupBox(tr("Select what mods you want export:")); - QRadioButton *all = new QRadioButton(tr("All installed mods")); - QRadioButton *active = new QRadioButton(tr("Only active (checked) mods from your current profile")); - QRadioButton *visible = new QRadioButton(tr("All currently visible mods in the mod list")); - - QVBoxLayout *vbox = new QVBoxLayout; - vbox->addWidget(all); - vbox->addWidget(active); - vbox->addWidget(visible); - vbox->addStretch(1); - groupBoxRows->setLayout(vbox); - - - - grid->addWidget(groupBoxRows); - - QButtonGroup *buttonGroupRows = new QButtonGroup(); - buttonGroupRows->addButton(all, 0); - buttonGroupRows->addButton(active, 1); - buttonGroupRows->addButton(visible, 2); - buttonGroupRows->button(0)->setChecked(true); - - - - QGroupBox *groupBoxColumns = new QGroupBox(tr("Choose what Columns to export:")); - groupBoxColumns->setFlat(true); - - QCheckBox *mod_Priority = new QCheckBox(tr("Mod_Priority")); - mod_Priority->setChecked(true); - QCheckBox *mod_Name = new QCheckBox(tr("Mod_Name")); - mod_Name->setChecked(true); - QCheckBox *mod_Note = new QCheckBox(tr("Notes_column")); - QCheckBox *mod_Status = new QCheckBox(tr("Mod_Status")); - mod_Status->setChecked(true); - QCheckBox *primary_Category = new QCheckBox(tr("Primary_Category")); - QCheckBox *nexus_ID = new QCheckBox(tr("Nexus_ID")); - QCheckBox *mod_Nexus_URL = new QCheckBox(tr("Mod_Nexus_URL")); - QCheckBox *mod_Version = new QCheckBox(tr("Mod_Version")); - QCheckBox *install_Date = new QCheckBox(tr("Install_Date")); - QCheckBox *download_File_Name = new QCheckBox(tr("Download_File_Name")); - - QVBoxLayout *vbox1 = new QVBoxLayout; - vbox1->addWidget(mod_Priority); - vbox1->addWidget(mod_Name); - vbox1->addWidget(mod_Status); - vbox1->addWidget(mod_Note); - vbox1->addWidget(primary_Category); - vbox1->addWidget(nexus_ID); - vbox1->addWidget(mod_Nexus_URL); - vbox1->addWidget(mod_Version); - vbox1->addWidget(install_Date); - vbox1->addWidget(download_File_Name); - groupBoxColumns->setLayout(vbox1); - - grid->addWidget(groupBoxColumns); - - QPushButton *ok = new QPushButton("Ok"); - QPushButton *cancel = new QPushButton("Cancel"); - QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - - connect(buttons, SIGNAL(accepted()), &selection, SLOT(accept())); - connect(buttons, SIGNAL(rejected()), &selection, SLOT(reject())); - - grid->addWidget(buttons); - - selection.setLayout(grid); - - - if (selection.exec() == QDialog::Accepted) { - - unsigned int numMods = ModInfo::getNumMods(); - int selectedRowID = buttonGroupRows->checkedId(); - - try { - QBuffer buffer; - buffer.open(QIODevice::ReadWrite); - CSVBuilder builder(&buffer); - builder.setEscapeMode(CSVBuilder::TYPE_STRING, CSVBuilder::QUOTE_ALWAYS); - std::vector > fields; - if (mod_Priority->isChecked()) - fields.push_back(std::make_pair(QString("#Mod_Priority"), CSVBuilder::TYPE_STRING)); - if (mod_Status->isChecked()) - fields.push_back(std::make_pair(QString("#Mod_Status"), CSVBuilder::TYPE_STRING)); - if (mod_Name->isChecked()) - fields.push_back(std::make_pair(QString("#Mod_Name"), CSVBuilder::TYPE_STRING)); - if (mod_Note->isChecked()) - fields.push_back(std::make_pair(QString("#Note"), CSVBuilder::TYPE_STRING)); - if (primary_Category->isChecked()) - fields.push_back(std::make_pair(QString("#Primary_Category"), CSVBuilder::TYPE_STRING)); - if (nexus_ID->isChecked()) - fields.push_back(std::make_pair(QString("#Nexus_ID"), CSVBuilder::TYPE_INTEGER)); - if (mod_Nexus_URL->isChecked()) - fields.push_back(std::make_pair(QString("#Mod_Nexus_URL"), CSVBuilder::TYPE_STRING)); - if (mod_Version->isChecked()) - fields.push_back(std::make_pair(QString("#Mod_Version"), CSVBuilder::TYPE_STRING)); - if (install_Date->isChecked()) - fields.push_back(std::make_pair(QString("#Install_Date"), CSVBuilder::TYPE_STRING)); - if (download_File_Name->isChecked()) - fields.push_back(std::make_pair(QString("#Download_File_Name"), CSVBuilder::TYPE_STRING)); - - builder.setFields(fields); - - builder.writeHeader(); - - auto indexesByPriority = m_OrganizerCore.currentProfile()->getAllIndexesByPriority(); - for (auto& iter : indexesByPriority) { - ModInfo::Ptr info = ModInfo::getByIndex(iter.second); - bool enabled = m_OrganizerCore.currentProfile()->modEnabled(iter.second); - if ((selectedRowID == 1) && !enabled) { - continue; - } - else if ((selectedRowID == 2) && !ui->modList->isModVisible(iter.second)) { - continue; - } - std::vector flags = info->getFlags(); - if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end()) && - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end())) { - if (mod_Priority->isChecked()) - builder.setRowField("#Mod_Priority", QString("%1").arg(iter.first, 4, 10, QChar('0'))); - if (mod_Status->isChecked()) - builder.setRowField("#Mod_Status", (enabled) ? "+" : "-"); - if (mod_Name->isChecked()) - builder.setRowField("#Mod_Name", info->name()); - if (mod_Note->isChecked()) - builder.setRowField("#Note", QString("%1").arg(info->comments().remove(','))); - if (primary_Category->isChecked()) - builder.setRowField("#Primary_Category", (m_CategoryFactory.categoryExists(info->primaryCategory())) ? m_CategoryFactory.getCategoryNameByID(info->primaryCategory()) : ""); - if (nexus_ID->isChecked()) - builder.setRowField("#Nexus_ID", info->nexusId()); - if (mod_Nexus_URL->isChecked()) - builder.setRowField("#Mod_Nexus_URL",(info->nexusId()>0)? NexusInterface::instance().getModURL(info->nexusId(), info->gameName()) : ""); - if (mod_Version->isChecked()) - builder.setRowField("#Mod_Version", info->version().canonicalString()); - if (install_Date->isChecked()) - builder.setRowField("#Install_Date", info->creationTime().toString("yyyy/MM/dd HH:mm:ss")); - if (download_File_Name->isChecked()) - builder.setRowField("#Download_File_Name", info->installationFile()); - - builder.writeRow(); - } - } - - SaveTextAsDialog saveDialog(this); - saveDialog.setText(buffer.data()); - saveDialog.exec(); - } - catch (const std::exception &e) { - reportError(tr("export failed: %1").arg(e.what())); - } - } -} - static void addMenuAsPushButton(QMenu *menu, QMenu *subMenu) { QPushButton *pushBtn = new QPushButton(subMenu->title()); @@ -4205,27 +3913,6 @@ QMenu *MainWindow::openFolderMenu() return FolderMenu; } -void MainWindow::initModListContextMenu(QMenu *menu) -{ - menu->addAction(tr("Install Mod..."), [&]() { installMod_clicked(); }); - menu->addAction(tr("Create empty mod"), [&]() { createEmptyMod_clicked(-1); }); - menu->addAction(tr("Create Separator"), [&]() { createSeparator_clicked(-1); }); - - if (ui->modList->hasCollapsibleSeparators()) { - menu->addSeparator(); - menu->addAction(tr("Collapse all"), ui->modList, &QTreeView::collapseAll); - menu->addAction(tr("Expand all"), ui->modList, &QTreeView::expandAll); - } - - menu->addSeparator(); - - menu->addAction(tr("Enable all visible"), [&]() { enableVisibleMods(); }); - menu->addAction(tr("Disable all visible"), [&]() { disableVisibleMods(); }); - menu->addAction(tr("Check for updates"), [&]() { checkModsForUpdates(); }); - menu->addAction(tr("Refresh"), &m_OrganizerCore, &OrganizerCore::profileRefresh); - menu->addAction(tr("Export to csv..."), [&]() { exportModListCSV(); }); -} - void MainWindow::addModSendToContextMenu(QMenu *menu) { if (ui->modList->sortColumn() != ModList::COL_PRIORITY) @@ -4269,15 +3956,12 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) if (modIndex == -1) { // no selection - QMenu menu(this); - initModListContextMenu(&menu); - menu.exec(modList->viewport()->mapToGlobal(pos)); + ModListGlobalContextMenu(m_OrganizerCore, ui->modList).exec(modList->viewport()->mapToGlobal(pos)); } else { QMenu menu(this); - QMenu *allMods = new QMenu(&menu); - initModListContextMenu(allMods); + QMenu *allMods = new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, this); allMods->setTitle(tr("All Mods")); menu.addMenu(allMods); @@ -4711,10 +4395,7 @@ void MainWindow::languageChange(const QString &newLanguage) m_DownloadsTab->update(); } - QMenu *listOptionsMenu = new QMenu(ui->listOptionsBtn); - initModListContextMenu(listOptionsMenu); - ui->listOptionsBtn->setMenu(listOptionsMenu); - + ui->listOptionsBtn->setMenu(new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, this)); ui->openFolderMenu->setMenu(openFolderMenu()); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 9e9e9591..a49c12fe 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -207,7 +207,6 @@ private: bool refreshProfiles(bool selectProfile = true); void refreshExecutablesList(); - void installMod(QString fileName = ""); bool modifyExecutablesDialog(int selection); void displayModInformation(int row, ModInfoTabIDs tab=ModInfoTabIDs::None); @@ -251,7 +250,6 @@ private: bool createBackup(const QString &filePath, const QDateTime &time); QString queryRestore(const QString &filePath); - void initModListContextMenu(QMenu *menu); void addModSendToContextMenu(QMenu *menu); void addPluginSendToContextMenu(QMenu *menu); @@ -358,8 +356,6 @@ private slots: // modlist context menu void installMod_clicked(); - void createEmptyMod_clicked(int modIndex); - void createSeparator_clicked(int modIndex); void restoreBackup_clicked(int modIndex); void renameMod_clicked(); void removeMod_clicked(int modIndex); @@ -473,9 +469,6 @@ private slots: void trackMod(ModInfo::Ptr mod, bool doTrack); void cancelModListEditor(); - void enableVisibleMods(); - void disableVisibleMods(); - void exportModListCSV(); void openInstanceFolder(); void openLogsFolder(); void openInstallFolder(); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp new file mode 100644 index 00000000..5b45a217 --- /dev/null +++ b/src/modlistcontextmenu.cpp @@ -0,0 +1,273 @@ +#include "modlistcontextmenu.h" + +#include + +#include "modlist.h" +#include "modlistview.h" +#include "modlistviewactions.h" +#include "organizercore.h" + +using namespace MOBase; + +ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, QWidget* parent) + : QMenu(parent) +{ + addAction(tr("Install Mod..."), [=]() { view->actions().installMod(); }); + addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(-1); }); + addAction(tr("Create Separator"), [=]() { view->actions().createSeparator(-1); }); + + if (view->hasCollapsibleSeparators()) { + addSeparator(); + addAction(tr("Collapse all"), view, &QTreeView::collapseAll); + addAction(tr("Expand all"), view, &QTreeView::expandAll); + } + + addSeparator(); + + addAction(tr("Enable all visible"), [=]() { + if (QMessageBox::question(view, tr("Confirm"), tr("Really enable all visible mods?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + view->enableAllVisible(); + } + }); + addAction(tr("Disable all visible"), [=]() { + if (QMessageBox::question(view, tr("Confirm"), tr("Really disable all visible mods?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + view->disableAllVisible(); + } + }); + addAction(tr("Check for updates"), [=]() { view->actions().checkModsForUpdates(); }); + addAction(tr("Refresh"), &core, &OrganizerCore::profileRefresh); + addAction(tr("Export to csv..."), [=]() { view->actions().exportModListCSV(); }); +} + +ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndexList& index, ModListView* modListView) : + QMenu(modListView) + , m_core(core) + , m_index(index) +{ + // TODO: Change this. + QModelIndex contextIdx = index.at(0); + int contextColumn = contextIdx.column(); + int modIndex = contextIdx.data(ModList::IndexRole).toInt(); + + try { + /* + if (modIndex == -1) { + // no selection + QMenu menu(this); + initModListContextMenu(&menu); + menu.exec(modList->viewport()->mapToGlobal(pos)); + } + else { + QMenu menu(this); + + QMenu* allMods = new QMenu(&menu); + initModListContextMenu(allMods); + allMods->setTitle(tr("All Mods")); + menu.addMenu(allMods); + + if (ui->modList->hasCollapsibleSeparators()) { + 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(modIndex); + std::vector flags = info->getFlags(); + + // context menu for overwrites + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { + if (QDir(info->absolutePath()).count() > 2) { + menu.addAction(tr("Sync to Mods..."), [=]() { m_OrganizerCore.syncOverwrite(); }); + menu.addAction(tr("Create Mod..."), [=]() { createModFromOverwrite(); }); + menu.addAction(tr("Move content to Mod..."), [=]() { moveOverwriteContentToExistingMod(); }); + menu.addAction(tr("Clear Overwrite..."), [=]() { clearOverwrite(); }); + } + menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); + } + + // context menu for mod backups + else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { + menu.addAction(tr("Restore Backup"), [=]() { restoreBackup_clicked(modIndex); }); + menu.addAction(tr("Remove Backup..."), [=]() { removeMod_clicked(modIndex); }); + menu.addSeparator(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { + menu.addAction(tr("Ignore missing data"), [=]() { ignoreMissingData_clicked(modIndex); }); + } + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { + menu.addAction(tr("Mark as converted/working"), [=]() { markConverted_clicked(modIndex); }); + } + menu.addSeparator(); + if (info->nexusId() > 0) { + menu.addAction(tr("Visit on Nexus"), [=]() { visitOnNexus_clicked(modIndex); }); + } + + const auto url = info->parseCustomURL(); + if (url.isValid()) { + menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); + } + + menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); + } + + // separator + else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) { + menu.addSeparator(); + QMenu* addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); + populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); + connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); + addMenuAsPushButton(&menu, addRemoveCategoriesMenu); + QMenu* primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); + connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); + addMenuAsPushButton(&menu, primaryCategoryMenu); + menu.addSeparator(); + menu.addAction(tr("Rename Separator..."), [=]() { renameMod_clicked(); }); + menu.addAction(tr("Remove Separator..."), [=]() { removeMod_clicked(modIndex); }); + menu.addSeparator(); + addModSendToContextMenu(&menu); + menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); + + if (info->color().isValid()) { + menu.addAction(tr("Reset Color"), [=]() { resetColor_clicked(modIndex); }); + } + + menu.addSeparator(); + } + + // foregin + else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { + addModSendToContextMenu(&menu); + } + + // regular + else { + QMenu* addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); + populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); + connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); + addMenuAsPushButton(&menu, addRemoveCategoriesMenu); + + QMenu* primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); + connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); + addMenuAsPushButton(&menu, primaryCategoryMenu); + + menu.addSeparator(); + + if (info->downgradeAvailable()) { + menu.addAction(tr("Change versioning scheme"), [=]() { changeVersioningScheme(modIndex); }); + } + + if (info->nexusId() > 0) + menu.addAction(tr("Force-check updates"), [=]() { checkModUpdates_clicked(modIndex); }); + if (info->updateIgnored()) { + menu.addAction(tr("Un-ignore update"), [=]() { unignoreUpdate(modIndex); }); + } + else { + if (info->updateAvailable() || info->downgradeAvailable()) { + menu.addAction(tr("Ignore update"), [=]() { ignoreUpdate(modIndex); }); + } + } + menu.addSeparator(); + + menu.addAction(tr("Enable selected"), [=]() { enableSelectedMods_clicked(); }); + menu.addAction(tr("Disable selected"), [=]() { disableSelectedMods_clicked(); }); + + menu.addSeparator(); + + addModSendToContextMenu(&menu); + + menu.addAction(tr("Rename Mod..."), [=]() { renameMod_clicked(); }); + menu.addAction(tr("Reinstall Mod"), [=]() { reinstallMod_clicked(modIndex); }); + menu.addAction(tr("Remove Mod..."), [=]() { removeMod_clicked(modIndex); }); + menu.addAction(tr("Create Backup"), [=]() { backupMod_clicked(modIndex); }); + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { + menu.addAction(tr("Restore hidden files"), [=]() { restoreHiddenFiles_clicked(modIndex); }); + } + + menu.addSeparator(); + + if (contextColumn == ModList::COL_NOTES) { + menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); + if (info->color().isValid()) { + menu.addAction(tr("Reset Color"), [=]() { resetColor_clicked(modIndex); }); + } + menu.addSeparator(); + } + + if (info->nexusId() > 0 && Settings::instance().nexus().endorsementIntegration()) { + switch (info->endorsedState()) { + case EndorsedState::ENDORSED_TRUE: { + menu.addAction(tr("Un-Endorse"), [=]() { unendorse_clicked(); }); + } break; + case EndorsedState::ENDORSED_FALSE: { + menu.addAction(tr("Endorse"), [=]() { endorse_clicked(); }); + menu.addAction(tr("Won't endorse"), [=]() { dontendorse_clicked(modIndex); }); + } break; + case EndorsedState::ENDORSED_NEVER: { + menu.addAction(tr("Endorse"), [=]() { endorse_clicked(); }); + } break; + default: { + QAction* action = new QAction(tr("Endorsement state unknown"), &menu); + action->setEnabled(false); + menu.addAction(action); + } break; + } + } + + if (info->nexusId() > 0 && Settings::instance().nexus().trackedIntegration()) { + switch (info->trackedState()) { + case TrackedState::TRACKED_FALSE: { + menu.addAction(tr("Start tracking"), [=]() { track_clicked(); }); + } break; + case TrackedState::TRACKED_TRUE: { + menu.addAction(tr("Stop tracking"), [=]() { untrack_clicked(); }); + } break; + default: { + QAction* action = new QAction(tr("Tracked state unknown"), &menu); + action->setEnabled(false); + menu.addAction(action); + } break; + } + } + + menu.addSeparator(); + + std::vector flags = info->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { + menu.addAction(tr("Ignore missing data"), [=]() { ignoreMissingData_clicked(modIndex); }); + } + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { + menu.addAction(tr("Mark as converted/working"), [=]() { markConverted_clicked(modIndex); }); + } + + menu.addSeparator(); + + if (info->nexusId() > 0) { + menu.addAction(tr("Visit on Nexus"), [=]() { visitOnNexus_clicked(modIndex); }); + } + + const auto url = info->parseCustomURL(); + if (url.isValid()) { + menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); + } + + menu.addAction(tr("Open in Explorer"), [&, modIndex]() { openExplorer_clicked(modIndex); }); + } + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { + QAction* infoAction = menu.addAction(tr("Information..."), [=]() { information_clicked(modIndex); }); + menu.setDefaultAction(infoAction); + } + } + */ + } + catch (const std::exception& e) { + reportError(tr("Exception: ").arg(e.what())); + } + catch (...) { + reportError(tr("Unknown exception")); + } +} diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h new file mode 100644 index 00000000..bc72fbdf --- /dev/null +++ b/src/modlistcontextmenu.h @@ -0,0 +1,39 @@ +#ifndef MODLISTCONTEXTMENU_H +#define MODLISTCONTEXTMENU_H + +#include + +#include +#include +#include + +class ModListView; +class OrganizerCore; + +class ModListGlobalContextMenu : public QMenu +{ + Q_OBJECT +public: + + ModListGlobalContextMenu(OrganizerCore& core, ModListView* modListView, QWidget* parent = nullptr); + +}; + +class ModListContextMenu : public QMenu +{ + Q_OBJECT + +private: + + friend class ModListView; + + // creates a new context menu that will act on the given mod list + // index (those should be index from the modlist) + ModListContextMenu(OrganizerCore& core, const QModelIndexList& index, ModListView* modListView); + + OrganizerCore& m_core; + QModelIndexList m_index; + +}; + +#endif diff --git a/src/modlistview.cpp b/src/modlistview.cpp index b36a77d9..f8192758 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -17,6 +17,7 @@ #include "log.h" #include "modflagicondelegate.h" #include "modconflicticondelegate.h" +#include "modlistviewactions.h" #include "modlistdropinfo.h" #include "genericicondelegate.h" #include "shared/directoryentry.h" @@ -118,6 +119,11 @@ int ModListView::sortColumn() const return m_sortProxy ? m_sortProxy->sortColumn() : -1; } +ModListViewActions& ModListView::actions() const +{ + return *m_actions; +} + int ModListView::nextMod(int modIndex) const { const QModelIndex start = indexModelToView(m_core->modList()->index(modIndex, 0)); @@ -634,10 +640,11 @@ void ModListView::updateGroupByProxy(int groupIndex) } } -void ModListView::setup(OrganizerCore& core, Ui::MainWindow* mwui) +void ModListView::setup(OrganizerCore& core, ModListViewActions* actions, Ui::MainWindow* mwui) { // attributes m_core = &core; + m_actions = actions; ui = { mwui->groupCombo, mwui->activeModsCounter, mwui->modFilterEdit, mwui->clearFiltersButton }; connect(m_core, &OrganizerCore::modInstalled, this, &ModListView::onModInstalled); diff --git a/src/modlistview.h b/src/modlistview.h index 8e37161b..d5eea54f 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -13,9 +13,11 @@ namespace Ui { class MainWindow; } +class FilterList; class OrganizerCore; class Profile; class ModListByPriorityProxy; +class ModListViewActions; class ModListView : public QTreeView { @@ -35,7 +37,7 @@ public: explicit ModListView(QWidget* parent = 0); void setModel(QAbstractItemModel* model) override; - void setup(OrganizerCore& core, Ui::MainWindow* mwui); + void setup(OrganizerCore& core, ModListViewActions* actions, Ui::MainWindow* mwui); // set the current profile // @@ -49,6 +51,10 @@ public: // int sortColumn() const; + // retrieve the actions from the view + // + ModListViewActions& actions() const; + // retrieve the next/previous mod in the current view, the given index // should be a mod index (not a model row), and the return value will be // a mod index or -1 if no mod was found @@ -180,6 +186,7 @@ private: OrganizerCore* m_core; ModListViewUi ui; + ModListViewActions* m_actions; ModListSortProxy* m_sortProxy; ModListByPriorityProxy* m_byPriorityProxy; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp new file mode 100644 index 00000000..8b234b81 --- /dev/null +++ b/src/modlistviewactions.cpp @@ -0,0 +1,339 @@ +#include "modlistviewactions.h" + +#include +#include +#include +#include + +#include + +#include "categories.h" +#include "filedialogmemory.h" +#include "filterlist.h" +#include "modlist.h" +#include "modlistview.h" +#include "nexusinterface.h" +#include "nxmaccessmanager.h" +#include "savetextasdialog.h" +#include "organizercore.h" +#include "csvbuilder.h" + +using namespace MOBase; + +ModListViewActions::ModListViewActions( + OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, QObject* nxmReceiver, ModListView* view) : + QObject(view) + , m_core(core) + , m_filters(filters) + , m_categories(categoryFactory) + , m_receiver(nxmReceiver) + , m_view(view) +{ + +} + +void ModListViewActions::installMod(const QString& archivePath) const +{ + try { + QString path = archivePath; + if (path.isEmpty()) { + QStringList extensions = m_core.installationManager()->getSupportedExtensions(); + for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) { + *iter = "*." + *iter; + } + + path = FileDialogMemory::getOpenFileName("installMod", m_view, tr("Choose Mod"), QString(), + tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); + } + + if (path.isEmpty()) { + return; + } + else { + m_core.installMod(path, false, nullptr, QString()); + } + } + catch (const std::exception& e) { + reportError(e.what()); + } +} + +void ModListViewActions::createEmptyMod(int modIndex) const +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + + while (name->isEmpty()) { + bool ok; + name.update(QInputDialog::getText(m_view, tr("Create Mod..."), + tr("This will create an empty mod.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { + return; + } + } + + if (m_core.modList()->getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists")); + return; + } + + int newPriority = -1; + if (modIndex >= 0 && m_view->sortColumn() == ModList::COL_PRIORITY) { + newPriority = m_core.currentProfile()->getModPriority(modIndex); + } + + IModInterface* newMod = m_core.createMod(name); + if (newMod == nullptr) { + return; + } + + m_core.refresh(); + + if (newPriority >= 0) { + m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); + } +} + +void ModListViewActions::createSeparator(int modIndex) const +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + while (name->isEmpty()) + { + bool ok; + name.update(QInputDialog::getText(m_view, tr("Create Separator..."), + tr("This will create a new separator.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { return; } + } + if (m_core.modList()->getMod(name) != nullptr) + { + reportError(tr("A separator with this name already exists")); + return; + } + name->append("_separator"); + if (m_core.modList()->getMod(name) != nullptr) + { + return; + } + + int newPriority = -1; + if (modIndex >= 0 && m_view->sortColumn() == ModList::COL_PRIORITY) + { + newPriority = m_core.currentProfile()->getModPriority(modIndex); + } + + if (m_core.createMod(name) == nullptr) { return; } + m_core.refresh(); + + if (newPriority >= 0) + { + m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); + } + + if (auto c = m_core.settings().colors().previousSeparatorColor()) { + ModInfo::getByIndex(ModInfo::getIndex(name))->setColor(*c); + } +} + +void ModListViewActions::checkModsForUpdates() const +{ + bool checkingModsForUpdate = false; + if (NexusInterface::instance().getAccessManager()->validated()) { + checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_receiver); + NexusInterface::instance().requestEndorsementInfo(m_receiver, QVariant(), QString()); + NexusInterface::instance().requestTrackingInfo(m_receiver, QVariant(), QString()); + } else { + QString apiKey; + if (GlobalSettings::nexusApiKey(apiKey)) { + m_core.doAfterLogin([=] () { checkModsForUpdates(); }); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); + } else { + log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); + } + } + + bool updatesAvailable = false; + for (auto mod : m_core.modList()->allMods()) { + ModInfo::Ptr modInfo = ModInfo::getByName(mod); + if (modInfo->updateAvailable()) { + updatesAvailable = true; + break; + } + } + + if (updatesAvailable || checkingModsForUpdate) { + m_view->setFilterCriteria({{ + ModListSortProxy::TypeSpecial, + CategoryFactory::UpdateAvailable, + false} + }); + + m_filters.setSelection({{ + ModListSortProxy::TypeSpecial, + CategoryFactory::UpdateAvailable, + false + }}); + } +} + +void ModListViewActions::exportModListCSV() const +{ + QDialog selection(m_view); + QGridLayout* grid = new QGridLayout; + selection.setWindowTitle(tr("Export to csv")); + + QLabel* csvDescription = new QLabel(); + csvDescription->setText(tr("CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet.\nYou can also use online editors and converters instead.")); + grid->addWidget(csvDescription); + + QGroupBox* groupBoxRows = new QGroupBox(tr("Select what mods you want export:")); + QRadioButton* all = new QRadioButton(tr("All installed mods")); + QRadioButton* active = new QRadioButton(tr("Only active (checked) mods from your current profile")); + QRadioButton* visible = new QRadioButton(tr("All currently visible mods in the mod list")); + + QVBoxLayout* vbox = new QVBoxLayout; + vbox->addWidget(all); + vbox->addWidget(active); + vbox->addWidget(visible); + vbox->addStretch(1); + groupBoxRows->setLayout(vbox); + + grid->addWidget(groupBoxRows); + + QButtonGroup* buttonGroupRows = new QButtonGroup(); + buttonGroupRows->addButton(all, 0); + buttonGroupRows->addButton(active, 1); + buttonGroupRows->addButton(visible, 2); + buttonGroupRows->button(0)->setChecked(true); + + QGroupBox* groupBoxColumns = new QGroupBox(tr("Choose what Columns to export:")); + groupBoxColumns->setFlat(true); + + QCheckBox* mod_Priority = new QCheckBox(tr("Mod_Priority")); + mod_Priority->setChecked(true); + QCheckBox* mod_Name = new QCheckBox(tr("Mod_Name")); + mod_Name->setChecked(true); + QCheckBox* mod_Note = new QCheckBox(tr("Notes_column")); + QCheckBox* mod_Status = new QCheckBox(tr("Mod_Status")); + mod_Status->setChecked(true); + QCheckBox* primary_Category = new QCheckBox(tr("Primary_Category")); + QCheckBox* nexus_ID = new QCheckBox(tr("Nexus_ID")); + QCheckBox* mod_Nexus_URL = new QCheckBox(tr("Mod_Nexus_URL")); + QCheckBox* mod_Version = new QCheckBox(tr("Mod_Version")); + QCheckBox* install_Date = new QCheckBox(tr("Install_Date")); + QCheckBox* download_File_Name = new QCheckBox(tr("Download_File_Name")); + + QVBoxLayout* vbox1 = new QVBoxLayout; + vbox1->addWidget(mod_Priority); + vbox1->addWidget(mod_Name); + vbox1->addWidget(mod_Status); + vbox1->addWidget(mod_Note); + vbox1->addWidget(primary_Category); + vbox1->addWidget(nexus_ID); + vbox1->addWidget(mod_Nexus_URL); + vbox1->addWidget(mod_Version); + vbox1->addWidget(install_Date); + vbox1->addWidget(download_File_Name); + groupBoxColumns->setLayout(vbox1); + + grid->addWidget(groupBoxColumns); + + QPushButton* ok = new QPushButton("Ok"); + QPushButton* cancel = new QPushButton("Cancel"); + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(buttons, SIGNAL(accepted()), &selection, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), &selection, SLOT(reject())); + + grid->addWidget(buttons); + + selection.setLayout(grid); + + + if (selection.exec() == QDialog::Accepted) { + + unsigned int numMods = ModInfo::getNumMods(); + int selectedRowID = buttonGroupRows->checkedId(); + + try { + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + CSVBuilder builder(&buffer); + builder.setEscapeMode(CSVBuilder::TYPE_STRING, CSVBuilder::QUOTE_ALWAYS); + std::vector > fields; + if (mod_Priority->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Priority"), CSVBuilder::TYPE_STRING)); + if (mod_Status->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Status"), CSVBuilder::TYPE_STRING)); + if (mod_Name->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Name"), CSVBuilder::TYPE_STRING)); + if (mod_Note->isChecked()) + fields.push_back(std::make_pair(QString("#Note"), CSVBuilder::TYPE_STRING)); + if (primary_Category->isChecked()) + fields.push_back(std::make_pair(QString("#Primary_Category"), CSVBuilder::TYPE_STRING)); + if (nexus_ID->isChecked()) + fields.push_back(std::make_pair(QString("#Nexus_ID"), CSVBuilder::TYPE_INTEGER)); + if (mod_Nexus_URL->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Nexus_URL"), CSVBuilder::TYPE_STRING)); + if (mod_Version->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Version"), CSVBuilder::TYPE_STRING)); + if (install_Date->isChecked()) + fields.push_back(std::make_pair(QString("#Install_Date"), CSVBuilder::TYPE_STRING)); + if (download_File_Name->isChecked()) + fields.push_back(std::make_pair(QString("#Download_File_Name"), CSVBuilder::TYPE_STRING)); + + builder.setFields(fields); + + builder.writeHeader(); + + auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority(); + for (auto& iter : indexesByPriority) { + ModInfo::Ptr info = ModInfo::getByIndex(iter.second); + bool enabled = m_core.currentProfile()->modEnabled(iter.second); + if ((selectedRowID == 1) && !enabled) { + continue; + } + else if ((selectedRowID == 2) && !m_view->isModVisible(iter.second)) { + continue; + } + std::vector flags = info->getFlags(); + if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end()) && + (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end())) { + if (mod_Priority->isChecked()) + builder.setRowField("#Mod_Priority", QString("%1").arg(iter.first, 4, 10, QChar('0'))); + if (mod_Status->isChecked()) + builder.setRowField("#Mod_Status", (enabled) ? "+" : "-"); + if (mod_Name->isChecked()) + builder.setRowField("#Mod_Name", info->name()); + if (mod_Note->isChecked()) + builder.setRowField("#Note", QString("%1").arg(info->comments().remove(','))); + if (primary_Category->isChecked()) + builder.setRowField("#Primary_Category", (m_categories.categoryExists(info->primaryCategory())) ? m_categories.getCategoryNameByID(info->primaryCategory()) : ""); + if (nexus_ID->isChecked()) + builder.setRowField("#Nexus_ID", info->nexusId()); + if (mod_Nexus_URL->isChecked()) + builder.setRowField("#Mod_Nexus_URL", (info->nexusId() > 0) ? NexusInterface::instance().getModURL(info->nexusId(), info->gameName()) : ""); + if (mod_Version->isChecked()) + builder.setRowField("#Mod_Version", info->version().canonicalString()); + if (install_Date->isChecked()) + builder.setRowField("#Install_Date", info->creationTime().toString("yyyy/MM/dd HH:mm:ss")); + if (download_File_Name->isChecked()) + builder.setRowField("#Download_File_Name", info->installationFile()); + + builder.writeRow(); + } + } + + SaveTextAsDialog saveDialog(m_view); + saveDialog.setText(buffer.data()); + saveDialog.exec(); + } + catch (const std::exception& e) { + reportError(tr("export failed: %1").arg(e.what())); + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h new file mode 100644 index 00000000..49cffaef --- /dev/null +++ b/src/modlistviewactions.h @@ -0,0 +1,55 @@ +#ifndef MODLISTVIEWACTIONS_H +#define MODLISTVIEWACTIONS_H + +#include +#include + +class CategoryFactory; +class FilterList; +class ModListView; +class OrganizerCore; + +class ModListViewActions : public QObject +{ + Q_OBJECT + +public: + + // the nxmReceiver is a (hopefully temporary) "hack" because it would require a lots of change + // to do otherwise since NXM is mostly based on the old Qt signal-slot system + // + ModListViewActions( + OrganizerCore& core, + FilterList& filters, + CategoryFactory& categoryFactory, + QObject* nxmReceiver, + ModListView* view); + + // install the mod from the given archive + // + void installMod(const QString& archivePath = "") const; + + // create an empty mod/a separator before the given mod or at + // the end of the list if the index is -1 + // + void createEmptyMod(int modIndex) const; + void createSeparator(int modIndex) const; + + // check all mods for update + // + void checkModsForUpdates() const; + + // start the "Export Mod List" dialog + // + void exportModListCSV() const; + +private: + + OrganizerCore& m_core; + FilterList& m_filters; + CategoryFactory& m_categories; + QObject* m_receiver; // receiver for NXM signals (temporary) + ModListView* m_view; +}; + +#endif diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 1a6083b4..986b6fbb 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1510,13 +1510,6 @@ void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) } } -ModListSortProxy *OrganizerCore::createModListProxyModel() -{ - ModListSortProxy *result = new ModListSortProxy(m_CurrentProfile.get(), this); - result->setSourceModel(&m_ModList); - return result; -} - PluginListSortProxy *OrganizerCore::createPluginListProxyModel() { PluginListSortProxy *result = new PluginListSortProxy(this); @@ -1524,6 +1517,11 @@ PluginListSortProxy *OrganizerCore::createPluginListProxyModel() return result; } +PluginContainer& OrganizerCore::pluginContainer() const +{ + return *m_PluginContainer; +} + IPluginGame const *OrganizerCore::managedGame() const { return m_GamePlugin; diff --git a/src/organizercore.h b/src/organizercore.h index 3fb4f20e..e060fbcb 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -227,9 +227,12 @@ public: MOBase::VersionInfo getVersion() const { return m_Updater.getVersion(); } - ModListSortProxy *createModListProxyModel(); PluginListSortProxy *createPluginListProxyModel(); + // return the plugin container + // + PluginContainer& pluginContainer() const; + MOBase::IPluginGame const *managedGame() const; /** -- cgit v1.3.1 From 7f0c9b8d8278f14754b375967267ff67d6fdb6ee Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 00:32:15 +0100 Subject: Move the overwrite context menu. --- src/mainwindow.cpp | 154 +++------------------------------------------ src/mainwindow.h | 12 ---- src/modlistcontextmenu.cpp | 44 +++++++------ src/modlistcontextmenu.h | 15 +++-- src/modlistviewactions.cpp | 128 ++++++++++++++++++++++++++++++++++++- src/modlistviewactions.h | 20 ++++++ src/organizercore.cpp | 2 - 7 files changed, 188 insertions(+), 187 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 91897d2f..056eeef7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -535,14 +535,17 @@ MainWindow::MainWindow(Settings &settings void MainWindow::setupModList() { - ui->modList->setup(m_OrganizerCore, new ModListViewActions(m_OrganizerCore, *m_Filters, m_CategoryFactory, this, ui->modList), ui); + auto* actions = new ModListViewActions(m_OrganizerCore, *m_Filters, m_CategoryFactory, this, ui->modList); + ui->modList->setup(m_OrganizerCore, actions, ui); + connect(actions, &ModListViewActions::overwriteCleared, [=]() { scheduleCheckForProblems(); }); connect(ui->modList, &ModListView::removeSelectedMods, [=]() { removeMod_clicked(-1); }); + connect(m_OrganizerCore.modList(), &ModList::clearOverwrite, actions, &ModListViewActions::clearOverwrite); + connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); }); // 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() @@ -2925,21 +2928,6 @@ void MainWindow::visitNexusOrWebPage_clicked(int index) { } } -void MainWindow::openExplorer_clicked(int index) -{ - 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()); - shell::Explore(info->absolutePath()); - } - } - else { - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - shell::Explore(modInfo->absolutePath()); - } -} - void MainWindow::openPluginOriginExplorer_clicked() { QItemSelectionModel *selection = ui->espList->selectionModel(); @@ -3121,127 +3109,6 @@ void MainWindow::resetColor_clicked(int modIndex) m_OrganizerCore.settings().colors().removePreviousSeparatorColor(); } -void MainWindow::createModFromOverwrite() -{ - GuessedValue name; - name.setFilter(&fixDirectoryName); - - while (name->isEmpty()) { - bool ok; - name.update(QInputDialog::getText(this, tr("Create Mod..."), - tr("This will move all files from overwrite into a new, regular mod.\n" - "Please enter a name:"), QLineEdit::Normal, "", &ok), - GUESS_USER); - if (!ok) { - return; - } - } - - if (m_OrganizerCore.modList()->getMod(name) != nullptr) { - reportError(tr("A mod with this name already exists")); - return; - } - - const IModInterface *newMod = m_OrganizerCore.createMod(name); - if (newMod == nullptr) { - return; - } - - doMoveOverwriteContentToMod(newMod->absolutePath()); -} - -void MainWindow::moveOverwriteContentToExistingMod() -{ - QStringList mods; - auto indexesByPriority = m_OrganizerCore.currentProfile()->getAllIndexesByPriority(); - for (auto & iter : indexesByPriority) { - if ((iter.second != UINT_MAX)) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(iter.second); - if (!modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN) && !modInfo->hasFlag(ModInfo::FLAG_OVERWRITE)) { - mods << modInfo->name(); - } - } - } - - ListDialog dialog(this); - dialog.setWindowTitle("Select a mod..."); - dialog.setChoices(mods); - - if (dialog.exec() == QDialog::Accepted) { - QString result = dialog.getChoice(); - if (!result.isEmpty()) { - - QString modAbsolutePath; - - for (const auto& mod : m_OrganizerCore.modsSortedByProfilePriority(m_OrganizerCore.currentProfile())) { - if (result.compare(mod) == 0) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(mod)); - modAbsolutePath = modInfo->absolutePath(); - break; - } - } - - if (modAbsolutePath.isNull()) { - log::warn("Mod {} has not been found, for some reason", result); - return; - } - - doMoveOverwriteContentToMod(modAbsolutePath); - } - } -} - -void MainWindow::doMoveOverwriteContentToMod(const QString &modAbsolutePath) -{ - 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(); }); - - ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex); - bool successful = shellMove((QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), - (QDir::toNativeSeparators(modAbsolutePath)), false, this); - - if (successful) { - MessageDialog::showMessage(tr("Move successful."), this); - } - else { - const auto e = GetLastError(); - log::error("Move operation failed: {}", formatSystemMessage(e)); - } - - m_OrganizerCore.refresh(); -} - -void MainWindow::clearOverwrite() -{ - 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(); - }); - - ModInfo::Ptr modInfo = ModInfo::getByIndex(overwriteIndex); - if (modInfo) - { - QDir overwriteDir(modInfo->absolutePath()); - if (QMessageBox::question(this, tr("Are you sure?"), - tr("About to recursively delete:\n") + overwriteDir.absolutePath(), - QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) - { - QStringList delList; - for (auto f : overwriteDir.entryList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot)) - delList.push_back(overwriteDir.absoluteFilePath(f)); - if (shellDelete(delList, true)) { - scheduleCheckForProblems(); - m_OrganizerCore.refresh(); - } else { - const auto e = GetLastError(); - log::error("Delete operation failed: {}", formatSystemMessage(e)); - } - } - } -} - void MainWindow::cancelModListEditor() { ui->modList->setEnabled(false); @@ -3869,13 +3736,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // context menu for overwrites if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { - if (QDir(info->absolutePath()).count() > 2) { - menu.addAction(tr("Sync to Mods..."), [=]() { m_OrganizerCore.syncOverwrite(); }); - menu.addAction(tr("Create Mod..."), [=]() { createModFromOverwrite(); }); - menu.addAction(tr("Move content to Mod..."), [=]() { moveOverwriteContentToExistingMod(); }); - menu.addAction(tr("Clear Overwrite..."), [=]() { clearOverwrite(); }); - } - menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); } // context menu for mod backups @@ -3899,7 +3759,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); } - menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); + menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); } // separator @@ -4049,7 +3909,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); } - menu.addAction(tr("Open in Explorer"), [&, modIndex]() { openExplorer_clicked(modIndex); }); + menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); } if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 790960e1..21c8f2aa 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -365,7 +365,6 @@ private slots: void visitOnNexus_clicked(int modIndex); void visitWebPage_clicked(int modIndex); void visitNexusOrWebPage_clicked(int modIndex); - void openExplorer_clicked(int modIndex); void openPluginOriginExplorer_clicked(); void openOriginInformation_clicked(); void information_clicked(int modIndex); @@ -390,17 +389,6 @@ private slots: BSA::EErrorCode extractBSA(BSA::Archive &archive, BSA::Folder::Ptr folder, const QString &destination, QProgressDialog &extractProgress); - void createModFromOverwrite(); - /** - * @brief sends the content of the overwrite folder to an already existing mod - */ - void moveOverwriteContentToExistingMod(); - /** - * @brief actually sends the content of the overwrite folder to specified mod - */ - void doMoveOverwriteContentToMod(const QString &modAbsolutePath); - void clearOverwrite(); - // nexus related void checkModsForUpdates(); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index c4a82e46..dda88735 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -44,14 +44,14 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* view) : QMenu(view) , m_core(core) - , m_index() + , m_index(index) , m_view(view) { if (view->selectionModel()->hasSelection()) { - m_index = view->indexViewToModel(view->selectionModel()->selectedRows()); + m_selected = view->indexViewToModel(view->selectionModel()->selectedRows()); } else { - m_index = { index }; + m_selected = { index }; } @@ -73,24 +73,24 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i // - Don't forget to check for the sort priority for "Send To... " if (info->isOverwrite()) { - addOverwriteActions(); + addOverwriteActions(info); } else if (info->isBackup()) { - addBackupActions(); + addBackupActions(info); } else if (info->isSeparator()) { - addSeparatorActions(); + addSeparatorActions(info); } else if (info->isForeign()) { - addForeignActions(); + addForeignActions(info); } else { - addRegularActions(); + addRegularActions(info); } // add information for all except foreign if (!info->isForeign()) { - QAction* infoAction = addAction(tr("Information..."), [=]() { view->actions().displayModInformation(m_index[0].row()); }); + QAction* infoAction = addAction(tr("Information..."), [=]() { view->actions().displayModInformation(m_index.data(ModList::IndexRole).toInt()); }); setDefaultAction(infoAction); } } @@ -99,36 +99,42 @@ 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); }); + menu->addAction(tr("Top"), [=]() { m_view->actions().sendModsToTop(m_selected); }); + menu->addAction(tr("Bottom"), [=]() { m_view->actions().sendModsToBottom(m_selected); }); + menu->addAction(tr("Priority..."), [=]() { m_view->actions().sendModsToPriority(m_selected); }); + menu->addAction(tr("Separator..."), [=]() { m_view->actions().sendModsToSeparator(m_selected); }); return menu; } -void ModListContextMenu::addOverwriteActions() +void ModListContextMenu::addOverwriteActions(ModInfo::Ptr mod) { - + if (QDir(mod->absolutePath()).count() > 2) { + addAction(tr("Sync to Mods..."), [=]() { m_core.syncOverwrite(); }); + addAction(tr("Create Mod..."), [=]() { m_view->actions().createModFromOverwrite(); }); + addAction(tr("Move content to Mod..."), [=]() { m_view->actions().moveOverwriteContentToExistingMod(); }); + addAction(tr("Clear Overwrite..."), [=]() { m_view->actions().clearOverwrite(); }); + } + addAction(tr("Open in Explorer"), [=]() { m_view->actions().openExplorer(m_selected); }); } -void ModListContextMenu::addSeparatorActions() +void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) { } -void ModListContextMenu::addForeignActions() +void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) { if (m_view->sortColumn() == ModList::COL_PRIORITY) { addMenu(createSendToContextMenu()); } } -void ModListContextMenu::addBackupActions() +void ModListContextMenu::addBackupActions(ModInfo::Ptr mod) { } -void ModListContextMenu::addRegularActions() +void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) { } diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 3bc15c57..22318575 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -7,6 +7,8 @@ #include #include +#include "modinfo.h" + class ModListView; class OrganizerCore; @@ -37,14 +39,15 @@ public: // TODO: Move this to private when all is done // add actions/menus to this menu for each type of mod // - void addOverwriteActions(); - void addSeparatorActions(); - void addForeignActions(); - void addBackupActions(); - void addRegularActions(); + void addOverwriteActions(ModInfo::Ptr mod); + void addSeparatorActions(ModInfo::Ptr mod); + void addForeignActions(ModInfo::Ptr mod); + void addBackupActions(ModInfo::Ptr mod); + void addRegularActions(ModInfo::Ptr mod); OrganizerCore& m_core; - QModelIndexList m_index; + QModelIndex m_index; + QModelIndexList m_selected; ModListView* m_view; }; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 0d556e64..a07fb27c 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "categories.h" #include "filedialogmemory.h" @@ -16,6 +17,7 @@ #include "modlist.h" #include "modlistview.h" #include "mainwindow.h" +#include "messagedialog.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "savetextasdialog.h" @@ -387,7 +389,11 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in dialog->show(); dialog->raise(); dialog->activateWindow(); - connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); + connect(dialog, &QDialog::finished, [=]() { + m_core.modList()->modInfoChanged(modInfo); + dialog->deleteLater(); + m_core.refreshDirectoryStructure(); + }); } catch (const std::exception& e) { reportError(tr("Failed to display overwrite dialog: %1").arg(e.what())); @@ -498,3 +504,123 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const } } } + +void ModListViewActions::openExplorer(const QModelIndexList& index) const +{ + for (auto& idx : index) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + shell::Explore(info->absolutePath()); + } +} + +void ModListViewActions::moveOverwriteContentsTo(const QString& absolutePath) const +{ + ModInfo::Ptr overwriteInfo = ModInfo::getOverwrite(); + bool successful = shellMove((QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), + (QDir::toNativeSeparators(absolutePath)), false, m_view); + + if (successful) { + MessageDialog::showMessage(tr("Move successful."), m_view); + } + else { + const auto e = GetLastError(); + log::error("Move operation failed: {}", formatSystemMessage(e)); + } + + m_core.refresh(); +} + +void ModListViewActions::createModFromOverwrite() const +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + + while (name->isEmpty()) { + bool ok; + name.update(QInputDialog::getText(m_view, tr("Create Mod..."), + tr("This will move all files from overwrite into a new, regular mod.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { + return; + } + } + + if (m_core.modList()->getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists")); + return; + } + + const IModInterface* newMod = m_core.createMod(name); + if (newMod == nullptr) { + return; + } + + moveOverwriteContentsTo(newMod->absolutePath()); +} + +void ModListViewActions::moveOverwriteContentToExistingMod() const +{ + QStringList mods; + auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority(); + for (auto& iter : indexesByPriority) { + if ((iter.second != UINT_MAX)) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(iter.second); + if (!modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN) && !modInfo->hasFlag(ModInfo::FLAG_OVERWRITE)) { + mods << modInfo->name(); + } + } + } + + ListDialog dialog(m_view); + dialog.setWindowTitle("Select a mod..."); + dialog.setChoices(mods); + + if (dialog.exec() == QDialog::Accepted) { + QString result = dialog.getChoice(); + if (!result.isEmpty()) { + + QString modAbsolutePath; + + for (const auto& mod : m_core.modsSortedByProfilePriority(m_core.currentProfile())) { + if (result.compare(mod) == 0) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(mod)); + modAbsolutePath = modInfo->absolutePath(); + break; + } + } + + if (modAbsolutePath.isNull()) { + log::warn("Mod {} has not been found, for some reason", result); + return; + } + + moveOverwriteContentsTo(modAbsolutePath); + } + } +} + +void ModListViewActions::clearOverwrite() const +{ + ModInfo::Ptr modInfo = ModInfo::getOverwrite(); + if (modInfo) + { + QDir overwriteDir(modInfo->absolutePath()); + if (QMessageBox::question(m_view, tr("Are you sure?"), + tr("About to recursively delete:\n") + overwriteDir.absolutePath(), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) + { + QStringList delList; + for (auto f : overwriteDir.entryList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot)) + delList.push_back(overwriteDir.absoluteFilePath(f)); + if (shellDelete(delList, true)) { + emit overwriteCleared(); + m_core.refresh(); + } + else { + const auto e = GetLastError(); + log::error("Delete operation failed: {}", formatSystemMessage(e)); + } + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index d8e1a3d0..10aa6c39 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -60,6 +60,26 @@ public: void sendModsToPriority(const QModelIndexList& index) const; void sendModsToSeparator(const QModelIndexList& index) const; + // open the Windows explorer for the specified mods + // + void openExplorer(const QModelIndexList& index) const; + + // overwrite-specific actions + // + void createModFromOverwrite() const; + void moveOverwriteContentToExistingMod() const; + void clearOverwrite() const; + +signals: + + // emitted when the overwrite mod has been clear + // + void overwriteCleared() const; + +private: + + void moveOverwriteContentsTo(const QString& absolutePath) const; + private: OrganizerCore& m_core; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 986b6fbb..507932e2 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(clearOverwrite()), w, - SLOT(clearOverwrite())); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), w, SLOT(fileMoved(QString, QString, QString))); connect(&m_PluginList, SIGNAL(writePluginsList()), w, -- cgit v1.3.1 From a105e4c8c881ac720c41ef342769adee14caca47 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 12:34:17 +0100 Subject: Move more stuff from MainWindow. Minor improvements for prev/next button in ModInfoDialog. --- src/mainwindow.cpp | 29 ---------------------------- src/mainwindow.h | 4 ---- src/modinfodialog.cpp | 48 +++++++++++++++++++++++++++++----------------- src/modinfodialog.h | 16 ++++++++++------ src/modlistview.cpp | 20 +++++++++++++++---- src/modlistview.h | 8 ++++---- src/modlistviewactions.cpp | 7 ++++++- src/organizercore.cpp | 1 - 8 files changed, 66 insertions(+), 67 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2ca15b00..3d509379 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -48,7 +48,6 @@ along with Mod Organizer. If not, see . #include "categories.h" #include "categoriesdialog.h" #include "genericicondelegate.h" -#include "modinfodialog.h" #include "overwriteinfodialog.h" #include "downloadlist.h" #include "downloadlistwidget.h" @@ -2377,16 +2376,6 @@ void MainWindow::windowTutorialFinished(const QString &windowName) m_OrganizerCore.settings().interface().setTutorialCompleted(windowName); } -void MainWindow::overwriteClosed(int) -{ - OverwriteInfoDialog *dialog = this->findChild("__overwriteDialog"); - if (dialog != nullptr) { - m_OrganizerCore.modList()->modInfoChanged(dialog->modInfo()); - dialog->deleteLater(); - } - m_OrganizerCore.refreshDirectoryStructure(); -} - void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) { ui->modList->actions().displayModInformation(modInfo, modIndex, tabID); @@ -2402,24 +2391,6 @@ void MainWindow::setWindowEnabled(bool enabled) setEnabled(enabled); } -ModInfo::Ptr MainWindow::nextModInList(int modIndex) -{ - modIndex = ui->modList->nextMod(modIndex); - if (modIndex == -1) { - return {}; - } - return ModInfo::getByIndex(modIndex); -} - -ModInfo::Ptr MainWindow::previousModInList(int modIndex) -{ - modIndex = ui->modList->prevMod(modIndex); - if (modIndex == -1) { - return {}; - } - return ModInfo::getByIndex(modIndex); -} - void MainWindow::openPluginOriginExplorer_clicked() { QItemSelectionModel *selection = ui->espList->selectionModel(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 1beed2f2..d80f4f63 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -142,9 +142,6 @@ public: virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; } - ModInfo::Ptr nextModInList(int modIndex); - ModInfo::Ptr previousModInList(int modIndex); - public slots: void esplist_changed(); void refresherProgress(const DirectoryRefreshProgress* p); @@ -428,7 +425,6 @@ private slots: void toolBar_customContextMenuRequested(const QPoint &point); void removeFromToolbar(QAction* action); - void overwriteClosed(int); void about(); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index cb282195..54c97406 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -21,7 +21,7 @@ along with Mod Organizer. If not, see . #include "ui_modinfodialog.h" #include "plugincontainer.h" #include "organizercore.h" -#include "mainwindow.h" +#include "modlistview.h" #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" #include "modinfodialogesps.h" @@ -176,11 +176,14 @@ bool ModInfoDialog::TabInfo::isVisible() const ModInfoDialog::ModInfoDialog( - MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, - ModInfo::Ptr mod) : - TutorableDialog("ModInfoDialog", mw), - ui(new Ui::ModInfoDialog), m_mainWindow(mw), - m_core(core), m_plugin(plugin), m_initialTab(ModInfoTabIDs::None), + OrganizerCore& core, PluginContainer& plugin, + ModInfo::Ptr mod, ModListView* modListView) : + TutorableDialog("ModInfoDialog", modListView), + ui(new Ui::ModInfoDialog), + m_core(core), + m_plugin(plugin), + m_modListView(modListView), + m_initialTab(ModInfoTabIDs::None), m_arrangingTabs(false) { ui->setupUi(this); @@ -204,7 +207,7 @@ template std::unique_ptr createTab(ModInfoDialog& d, ModInfoTabIDs id) { return std::make_unique(ModInfoDialogTabContext( - *d.m_core, *d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); + d.m_core, d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); } void ModInfoDialog::createTabs() @@ -269,7 +272,7 @@ int ModInfoDialog::exec() update(true); if (noCustomTabRequested) { - m_core->settings().widgets().restoreIndex(ui->tabWidget); + m_core.settings().widgets().restoreIndex(ui->tabWidget); } const int r = TutorableDialog::exec(); @@ -430,7 +433,7 @@ void ModInfoDialog::reAddTabs( Q_ASSERT(visibility.size() == m_tabs.size()); // ordered tab names from settings - const auto orderedNames = m_core->settings().geometry().modInfoTabOrder(); + const auto orderedNames = m_core.settings().geometry().modInfoTabOrder(); // whether the tabs can be sorted // @@ -586,7 +589,7 @@ void ModInfoDialog::feedFiles(std::vector& interestedTabs) void ModInfoDialog::setTabsColors() { - const auto p = m_mainWindow->palette(); + const auto p = m_modListView->parentWidget()->palette(); for (const auto& tabInfo : m_tabs) { if (!tabInfo.isVisible()) { @@ -619,7 +622,7 @@ void ModInfoDialog::switchToTab(ModInfoTabIDs id) MOShared::FilesOrigin* ModInfoDialog::getOrigin() { - auto* ds = m_core->directoryStructure(); + auto* ds = m_core.directoryStructure(); if (!ds->originExists(m_mod->name().toStdWString())) { return nullptr; @@ -639,7 +642,7 @@ void ModInfoDialog::saveState() const // save state for each tab for (const auto& tabInfo : m_tabs) { - tabInfo.tab->saveState(m_core->settings()); + tabInfo.tab->saveState(m_core.settings()); } } @@ -650,7 +653,7 @@ void ModInfoDialog::restoreState() // restore state for each tab for (const auto& tabInfo : m_tabs) { - tabInfo.tab->restoreState(m_core->settings()); + tabInfo.tab->restoreState(m_core.settings()); } } @@ -678,9 +681,9 @@ void ModInfoDialog::saveTabOrder() const names += ui->tabWidget->widget(i)->objectName(); } - m_core->settings().geometry().setModInfoTabOrder(names); + m_core.settings().geometry().setModInfoTabOrder(names); // save last opened index - m_core->settings().widgets().saveIndex(ui->tabWidget); + m_core.settings().widgets().saveIndex(ui->tabWidget); } void ModInfoDialog::onOriginModified(int originID) @@ -776,22 +779,31 @@ void ModInfoDialog::onTabMoved() void ModInfoDialog::onNextMod() { - auto mod = m_mainWindow->nextModInList(ModInfo::getIndex(m_mod->name())); + auto index = m_modListView->nextMod(ModInfo::getIndex(m_mod->name())); + if (!index) { + return; + } + auto mod = ModInfo::getByIndex(*index); if (!mod || mod == m_mod) { return; } setMod(mod); update(); + + emit modChanged(*index); } void ModInfoDialog::onPreviousMod() { - auto mod = m_mainWindow->previousModInList(ModInfo::getIndex(m_mod->name())); - if (!mod || mod == m_mod) { + auto index = m_modListView->prevMod(ModInfo::getIndex(m_mod->name())); + if (!index) { return; } + auto mod = ModInfo::getByIndex(*index); setMod(mod); update(); + + emit modChanged(*index); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 48680ca4..a3b6ffdb 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -33,7 +33,7 @@ class PluginContainer; class OrganizerCore; class Settings; class ModInfoDialogTab; -class MainWindow; +class ModListView; /** * this is a larger dialog used to visualise information about the mod. @@ -52,8 +52,8 @@ class ModInfoDialog : public MOBase::TutorableDialog public: ModInfoDialog( - MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, - ModInfo::Ptr mod); + OrganizerCore& core, PluginContainer& plugin, + ModInfo::Ptr mod, ModListView* view); ~ModInfoDialog(); @@ -71,6 +71,10 @@ signals: // void originModified(int originID); + // emitted when the mod of the dialog is changed + // + void modChanged(unsigned int modIndex); + protected: // forwards to tryClose() // @@ -115,10 +119,10 @@ private: }; std::unique_ptr ui; - MainWindow* m_mainWindow; + OrganizerCore& m_core; + PluginContainer& m_plugin; + ModListView* m_modListView; ModInfo::Ptr m_mod; - OrganizerCore* m_core; - PluginContainer* m_plugin; std::vector m_tabs; // initial tab requested by the main window when the dialog is opened; whether diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 40959e6d..f2b83beb 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -128,7 +128,7 @@ ModListViewActions& ModListView::actions() const return *m_actions; } -int ModListView::nextMod(int modIndex) const +std::optional ModListView::nextMod(unsigned int modIndex) const { const QModelIndex start = indexModelToView(m_core->modList()->index(modIndex, 0)); @@ -156,10 +156,10 @@ int ModListView::nextMod(int modIndex) const return modIndex; } - return -1; + return {}; } -int ModListView::prevMod(int modIndex) const +std::optional ModListView::prevMod(unsigned int modIndex) const { const QModelIndex start = indexModelToView(m_core->modList()->index(modIndex, 0)); @@ -187,7 +187,7 @@ int ModListView::prevMod(int modIndex) const return modIndex; } - return -1; + return {}; } void ModListView::enableAllVisible() @@ -730,6 +730,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, FilterLis m_byPriorityProxy->refreshExpandedItems(); } }); + connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &ModListView::onSelectionChanged); } void ModListView::setModel(QAbstractItemModel* model) @@ -848,6 +849,17 @@ void ModListView::onDoubleClicked(const QModelIndex& index) } } +void ModListView::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) +{ + if (hasCollapsibleSeparators()) { + for (auto& idx : selected.indexes()) { + if (idx.parent().isValid() && !isExpanded(idx.parent())) { + setExpanded(idx.parent(), true); + } + } + } +} + void ModListView::dragEnterEvent(QDragEnterEvent* event) { emit dragEntered(event->mimeData()); diff --git a/src/modlistview.h b/src/modlistview.h index 1678d01e..c7f4e5f0 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -58,11 +58,10 @@ public: ModListViewActions& actions() const; // retrieve the next/previous mod in the current view, the given index - // should be a mod index (not a model row), and the return value will be - // a mod index or -1 if no mod was found + // should be a mod index (not a model row) // - int nextMod(int index) const; - int prevMod(int index) const; + std::optional nextMod(unsigned int index) const; + std::optional prevMod(unsigned int index) const; // check if the given mod is visible // @@ -142,6 +141,7 @@ protected slots: void onCustomContextMenuRequested(const QPoint& pos); void onDoubleClicked(const QModelIndex& index); + void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); private: diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 2b5bf026..3cc2a841 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -433,8 +433,13 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in else { modInfo->saveMeta(); - ModInfoDialog dialog(m_main, &m_core, &m_core.pluginContainer(), modInfo); + ModInfoDialog dialog(m_core, m_core.pluginContainer(), modInfo, m_view); connect(&dialog, &ModInfoDialog::originModified, this, &ModListViewActions::originModified); + connect(&dialog, &ModInfoDialog::modChanged, [=](unsigned int index) { + auto idx = m_view->indexModelToView(m_core.modList()->index(index, 0)); + m_view->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + m_view->scrollTo(idx); + }); //Open the tab first if we want to use the standard indexes of the tabs. if (tab != ModInfoTabIDs::None) { diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 507932e2..17cd80c2 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -15,7 +15,6 @@ #include "profile.h" #include "credentialsdialog.h" #include "filedialogmemory.h" -#include "modinfodialog.h" #include "spawn.h" #include "syncoverwritedialog.h" #include "nxmaccessmanager.h" -- cgit v1.3.1 From f2d8469ed6cd0fafc22097cdba2ee8f325f00513 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 21:02:04 +0100 Subject: Start moving stuff from MainWindow to PluginListView. --- src/CMakeLists.txt | 1 + src/mainwindow.cpp | 88 +++----------------------- src/mainwindow.h | 5 -- src/modelutils.cpp | 58 ++++++++++++++++++ src/modelutils.h | 14 +++++ src/modlistview.cpp | 51 +++------------ src/modlistviewactions.cpp | 23 ++++--- src/modlistviewactions.h | 12 ++-- src/organizercore.cpp | 8 --- src/organizercore.h | 2 - src/pluginlistview.cpp | 150 ++++++++++++++++++++++++++++++++++----------- src/pluginlistview.h | 55 ++++++++++++++--- 12 files changed, 270 insertions(+), 197 deletions(-) create mode 100644 src/modelutils.cpp create mode 100644 src/modelutils.h (limited to 'src/organizercore.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c7bcd46..7773e845 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -227,6 +227,7 @@ add_filter(NAME src/widgets GROUPS qtgroupingproxy texteditor viewmarkingscrollbar + modelutils ) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b000ba57..60e2a1e0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -320,14 +320,7 @@ MainWindow::MainWindow(Settings &settings TaskProgressManager::instance().tryCreateTaskbar(); setupModList(); - - // set up plugin list - m_PluginListSortProxy = m_OrganizerCore.createPluginListProxyModel(); - - ui->espList->setModel(m_PluginListSortProxy); - ui->espList->sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder); - ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(ui->espList)); - ui->espList->installEventFilter(m_OrganizerCore.pluginList()); + ui->espList->setup(m_OrganizerCore, this, ui); ui->bsaList->setLocalMoveOnly(true); ui->bsaList->setHeaderHidden(true); @@ -400,9 +393,6 @@ MainWindow::MainWindow(Settings &settings connect(&m_PluginContainer, SIGNAL(diagnosisUpdate()), this, SLOT(scheduleCheckForProblems())); - connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString))); - connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); - connect(m_OrganizerCore.directoryRefresher(), SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect( m_OrganizerCore.directoryRefresher(), @@ -513,10 +503,10 @@ MainWindow::MainWindow(Settings &settings refreshExecutablesList(); updatePinnedExecutables(); resetActionIcons(); - updatePluginCount(); processUpdates(); ui->modList->updateModCount(); + ui->espList->updatePluginCount(); ui->statusBar->updateNormalMessage(m_OrganizerCore); } @@ -1129,18 +1119,6 @@ void MainWindow::createHelpMenu() menu->addAction(tr("About Qt"), qApp, SLOT(aboutQt())); } -void MainWindow::espFilterChanged(const QString &filter) -{ - if (!filter.isEmpty()) { - ui->espList->setStyleSheet("QTreeView { border: 2px ridge #f00; }"); - ui->activePluginsCounter->setStyleSheet("QLCDNumber { border: 2px ridge #f00; }"); - } else { - ui->espList->setStyleSheet(""); - ui->activePluginsCounter->setStyleSheet(""); - } - updatePluginCount(); -} - bool MainWindow::addProfile() { QComboBox *profileBox = findChild("profileBox"); @@ -1544,7 +1522,7 @@ void MainWindow::activateSelectedProfile() m_SavesTab->refreshSaveList(); m_OrganizerCore.refresh(); ui->modList->updateModCount(); - updatePluginCount(); + ui->espList->updatePluginCount(); ui->statusBar->updateNormalMessage(m_OrganizerCore); } @@ -2238,7 +2216,7 @@ void MainWindow::directory_refreshed() void MainWindow::esplist_changed() { - updatePluginCount(); + ui->espList->updatePluginCount(); } void MainWindow::modInstalled(const QString &modName) @@ -2332,6 +2310,7 @@ 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) @@ -2427,57 +2406,6 @@ void MainWindow::refreshProfile_activated() m_OrganizerCore.profileRefresh(); } -void MainWindow::updatePluginCount() -{ - int activeMasterCount = 0; - int activeLightMasterCount = 0; - int activeRegularCount = 0; - int masterCount = 0; - int lightMasterCount = 0; - int regularCount = 0; - int activeVisibleCount = 0; - - PluginList *list = m_OrganizerCore.pluginList(); - QString filter = ui->espFilterEdit->text(); - - for (QString plugin : list->pluginNames()) { - bool active = list->isEnabled(plugin); - bool visible = m_PluginListSortProxy->filterMatchesPlugin(plugin); - if (list->isLight(plugin) || list->isLightFlagged(plugin)) { - lightMasterCount++; - activeLightMasterCount += active; - activeVisibleCount += visible && active; - } else if (list->isMaster(plugin)) { - masterCount++; - activeMasterCount += active; - activeVisibleCount += visible && active; - } else { - regularCount++; - activeRegularCount += active; - activeVisibleCount += visible && active; - } - } - - int activeCount = activeMasterCount + activeLightMasterCount + activeRegularCount; - int totalCount = masterCount + lightMasterCount + regularCount; - - ui->activePluginsCounter->display(activeVisibleCount); - ui->activePluginsCounter->setToolTip(tr("" - "" - "" - "" - "" - "" - "" - "
    TypeActive Total
    All plugins:%1 %2
    ESMs:%3 %4
    ESPs:%7 %8
    ESMs+ESPs:%9 %10
    ESLs:%5 %6
    ") - .arg(activeCount).arg(totalCount) - .arg(activeMasterCount).arg(masterCount) - .arg(activeLightMasterCount).arg(lightMasterCount) - .arg(activeRegularCount).arg(regularCount) - .arg(activeMasterCount+activeRegularCount).arg(masterCount+regularCount) - ); -} - void MainWindow::openOriginInformation_clicked() { try { @@ -2680,7 +2608,7 @@ QMenu *MainWindow::openFolderMenu() void MainWindow::addPluginSendToContextMenu(QMenu *menu) { - if (m_PluginListSortProxy->sortColumn() != PluginList::COL_PRIORITY) + if (ui->espList->sortColumn() != PluginList::COL_PRIORITY) return; QMenu *sub_menu = new QMenu(this); @@ -3623,7 +3551,7 @@ void MainWindow::toolBar_customContextMenuRequested(const QPoint &point) void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) { - int espIndex = m_PluginListSortProxy->mapToSource(ui->espList->indexAt(pos)).row(); + int espIndex = ui->espList->indexViewToModel(ui->espList->indexAt(pos)).row(); QMenu menu; menu.addAction(tr("Enable selected"), [=]() { enableSelectedPlugins_clicked(); }); @@ -3642,7 +3570,7 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) bool hasLocked = false; bool hasUnlocked = false; for (const QModelIndex &idx : currentSelection.indexes()) { - int row = m_PluginListSortProxy->mapToSource(idx).row(); + int row = ui->espList->indexViewToModel(idx).row(); if (m_OrganizerCore.pluginList()->isEnabled(row)) { if (m_OrganizerCore.pluginList()->isESPLocked(row)) { hasLocked = true; diff --git a/src/mainwindow.h b/src/mainwindow.h index 75e7124c..71845874 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -148,8 +148,6 @@ public slots: void directory_refreshed(); - void updatePluginCount(); - signals: /** @@ -262,8 +260,6 @@ private: QStringList m_DefaultArchives; - PluginListSortProxy *m_PluginListSortProxy; - int m_OldExecutableIndex; QAction *m_ContextAction; @@ -406,7 +402,6 @@ private slots: void modlistChanged(const QModelIndexList &indicies, int role); void fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName); - void espFilterChanged(const QString &filter); void resizeLists(bool pluginListCustom); /** diff --git a/src/modelutils.cpp b/src/modelutils.cpp new file mode 100644 index 00000000..43aa6b99 --- /dev/null +++ b/src/modelutils.cpp @@ -0,0 +1,58 @@ +#include "modelutils.h" + +#include + +QModelIndex indexModelToView(const QModelIndex& index, const QAbstractItemView* view) +{ + // we need to stack the proxy + std::vector proxies; + { + auto* currentModel = view->model(); + while (auto* proxy = qobject_cast(currentModel)) { + proxies.push_back(proxy); + currentModel = proxy->sourceModel(); + } + } + + if (proxies.empty() || proxies.back()->sourceModel() != index.model()) { + return QModelIndex(); + } + + auto qindex = index; + for (auto rit = proxies.rbegin(); rit != proxies.rend(); ++rit) { + qindex = (*rit)->mapFromSource(qindex); + } + + return qindex; +} + +QModelIndexList indexModelToView(const QModelIndexList& index, const QAbstractItemView* view) +{ + QModelIndexList result; + for (auto& idx : index) { + result.append(indexModelToView(idx, view)); + } + return result; +} + +QModelIndex indexViewToModel(const QModelIndex& index, const QAbstractItemModel* model) +{ + if (index.model() == model) { + return index; + } + else if (auto* proxy = qobject_cast(index.model())) { + return indexViewToModel(proxy->mapToSource(index), model); + } + else { + return QModelIndex(); + } +} + +QModelIndexList indexViewToModel(const QModelIndexList& index, const QAbstractItemModel* model) +{ + QModelIndexList result; + for (auto& idx : index) { + result.append(indexViewToModel(idx, model)); + } + return result; +} diff --git a/src/modelutils.h b/src/modelutils.h new file mode 100644 index 00000000..f355c0d6 --- /dev/null +++ b/src/modelutils.h @@ -0,0 +1,14 @@ +#ifndef MODELUTILS_H +#define MODELUTILS_H + +#include +#include + +// convert back-and-forth through model proxies +QModelIndex indexModelToView(const QModelIndex& index, const QAbstractItemView* view); +QModelIndexList indexModelToView(const QModelIndexList& index, const QAbstractItemView* view); +QModelIndex indexViewToModel(const QModelIndex& index, const QAbstractItemModel* model); +QModelIndexList indexViewToModel(const QModelIndexList& index, const QAbstractItemModel* model); + + +#endif diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 99cc4c4c..082d947e 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -24,6 +24,8 @@ #include "genericicondelegate.h" #include "shared/directoryentry.h" #include "shared/filesorigin.h" +#include "mainwindow.h" +#include "modelutils.h" using namespace MOBase; @@ -197,61 +199,22 @@ bool ModListView::isModVisible(ModInfo::Ptr mod) const QModelIndex ModListView::indexModelToView(const QModelIndex& index) const { - if (index.model() != m_core->modList()) { - return QModelIndex(); - } - - // we need to stack the proxy - std::vector proxies; - { - auto* currentModel = model(); - while (auto* proxy = qobject_cast(currentModel)) { - proxies.push_back(proxy); - currentModel = proxy->sourceModel(); - } - } - - if (proxies.empty() || proxies.back()->sourceModel() != m_core->modList()) { - return QModelIndex(); - } - - auto qindex = index; - for (auto rit = proxies.rbegin(); rit != proxies.rend(); ++rit) { - qindex = (*rit)->mapFromSource(qindex); - } - - return qindex; + return ::indexModelToView(index, this); } QModelIndexList ModListView::indexModelToView(const QModelIndexList& index) const { - QModelIndexList result; - for (auto& idx : index) { - result.append(indexModelToView(idx)); - } - return result; + return ::indexModelToView(index, this); } QModelIndex ModListView::indexViewToModel(const QModelIndex& index) const { - if (index.model() == m_core->modList()) { - return index; - } - else if (auto* proxy = qobject_cast(index.model())) { - return indexViewToModel(proxy->mapToSource(index)); - } - else { - return QModelIndex(); - } + return ::indexViewToModel(index, m_core->modList()); } QModelIndexList ModListView::indexViewToModel(const QModelIndexList& index) const { - QModelIndexList result; - for (auto& idx : index) { - result.append(indexViewToModel(idx)); - } - return result; + return ::indexViewToModel(index, m_core->modList()); } QModelIndex ModListView::nextIndex(const QModelIndex& index) const @@ -625,7 +588,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo m_core = &core; m_filters.reset(new FilterList(mwui, core, factory)); m_categories = &factory; - m_actions = new ModListViewActions(core, *m_filters, factory, mw, this); + m_actions = new ModListViewActions(core, *m_filters, factory, this, mwui->espList, mw, mw); ui = { mwui->groupCombo, mwui->activeModsCounter, mwui->modFilterEdit, mwui->currentCategoryLabel, mwui->clearFiltersButton }; connect(m_core, &OrganizerCore::modInstalled, this, &ModListView::onModInstalled); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index d9841517..f0d4c4c7 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -17,7 +17,6 @@ #include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" -#include "mainwindow.h" #include "messagedialog.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" @@ -25,6 +24,7 @@ #include "organizercore.h" #include "overwriteinfodialog.h" #include "csvbuilder.h" +#include "pluginlistview.h" #include "shared/filesorigin.h" #include "shared/directoryentry.h" #include "shared/fileregister.h" @@ -33,15 +33,18 @@ using namespace MOBase; using namespace MOShared; + ModListViewActions::ModListViewActions( - OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, MainWindow* mainWindow, ModListView* view) : - QObject(view) + OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, + ModListView* view, PluginListView* pluginView, QObject* nxmReceiver, QWidget* parent) : + QObject(parent) , m_core(core) , m_filters(filters) , m_categories(categoryFactory) , m_view(view) - , m_parent(mainWindow) - , m_main(mainWindow) + , m_pluginView(pluginView) + , m_parent(parent) + , m_receiver(nxmReceiver) { } @@ -157,9 +160,9 @@ void ModListViewActions::checkModsForUpdates() const { bool checkingModsForUpdate = false; if (NexusInterface::instance().getAccessManager()->validated()) { - checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_main); - NexusInterface::instance().requestEndorsementInfo(m_main, QVariant(), QString()); - NexusInterface::instance().requestTrackingInfo(m_main, QVariant(), QString()); + checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_receiver); + NexusInterface::instance().requestEndorsementInfo(m_receiver, QVariant(), QString()); + NexusInterface::instance().requestTrackingInfo(m_receiver, QVariant(), QString()); } else { QString apiKey; if (GlobalSettings::nexusApiKey(apiKey)) { @@ -201,7 +204,7 @@ void ModListViewActions::checkModsForUpdates(std::multimap const& } if (NexusInterface::instance().getAccessManager()->validated()) { - ModInfo::manualUpdateCheck(m_main, IDs); + ModInfo::manualUpdateCheck(m_receiver, IDs); } else { QString apiKey; @@ -596,7 +599,7 @@ void ModListViewActions::removeMods(const QModelIndexList& indices) const m_core.modList()->removeRow(indices[0].data(ModList::IndexRole).toInt(), QModelIndex()); } m_view->updateModCount(); - m_main->updatePluginCount(); + m_pluginView->updatePluginCount(); } catch (const std::exception& e) { reportError(tr("failed to remove mod: %1").arg(e.what())); diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 135b3035..cb7cdbda 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -11,6 +11,7 @@ class CategoryFactory; class FilterList; class MainWindow; class ModListView; +class PluginListView; class OrganizerCore; class ModListViewActions : public QObject @@ -26,8 +27,10 @@ public: OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, - MainWindow* mainWindow, - ModListView* view); + ModListView* view, + PluginListView* pluginView, + QObject* nxmReceiver, + QWidget* parent); // install the mod from the given archive // @@ -142,10 +145,9 @@ private: FilterList& m_filters; CategoryFactory& m_categories; ModListView* m_view; + PluginListView* m_pluginView; + QObject* m_receiver; QWidget* m_parent; - - // hope to get rid of this some day - MainWindow* m_main; }; #endif diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 17cd80c2..a8911d4f 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -11,7 +11,6 @@ #include "modrepositoryfileinfo.h" #include "nexusinterface.h" #include "plugincontainer.h" -#include "pluginlistsortproxy.h" #include "profile.h" #include "credentialsdialog.h" #include "filedialogmemory.h" @@ -1507,13 +1506,6 @@ void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) } } -PluginListSortProxy *OrganizerCore::createPluginListProxyModel() -{ - PluginListSortProxy *result = new PluginListSortProxy(this); - result->setSourceModel(&m_PluginList); - return result; -} - PluginContainer& OrganizerCore::pluginContainer() const { return *m_PluginContainer; diff --git a/src/organizercore.h b/src/organizercore.h index e060fbcb..24de26ee 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -227,8 +227,6 @@ public: MOBase::VersionInfo getVersion() const { return m_Updater.getVersion(); } - PluginListSortProxy *createPluginListProxyModel(); - // return the plugin container // PluginContainer& pluginContainer() const; diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index a265d5d4..a401ab06 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -1,58 +1,136 @@ #include "pluginlistview.h" -#include + #include #include -#include +#include -class PluginListViewStyle : public QProxyStyle { -public: - PluginListViewStyle(QStyle *style, int indentation); +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "organizercore.h" +#include "pluginlistsortproxy.h" +#include "genericicondelegate.h" +#include "modelutils.h" - void drawPrimitive(PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget = 0) const; -private: - int m_Indentation; -}; +PluginListView::PluginListView(QWidget *parent) + : QTreeView(parent) + , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) +{ + setVerticalScrollBar(m_Scrollbar); + MOBase::setCustomizableColumns(this); +} -PluginListViewStyle::PluginListViewStyle(QStyle *style, int indentation) - : QProxyStyle(style), m_Indentation(indentation) +void PluginListView::setModel(QAbstractItemModel *model) { + QTreeView::setModel(model); + setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); } -void PluginListViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const +int PluginListView::sortColumn() 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); - } + return m_sortProxy ? m_sortProxy->sortColumn() : -1; } -PluginListView::PluginListView(QWidget *parent) - : QTreeView(parent) - , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) +QModelIndex PluginListView::indexModelToView(const QModelIndex& index) const { - setVerticalScrollBar(m_Scrollbar); - MOBase::setCustomizableColumns(this); + return ::indexModelToView(index, this); } -void PluginListView::dragEnterEvent(QDragEnterEvent *event) +QModelIndexList PluginListView::indexModelToView(const QModelIndexList& index) const { - emit dropModeUpdate(event->mimeData()->hasUrls()); + return ::indexModelToView(index, this); +} - QTreeView::dragEnterEvent(event); +QModelIndex PluginListView::indexViewToModel(const QModelIndex& index) const +{ + return ::indexViewToModel(index, m_core->pluginList()); } -void PluginListView::setModel(QAbstractItemModel *model) +QModelIndexList PluginListView::indexViewToModel(const QModelIndexList& index) const { - QTreeView::setModel(model); - setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); + return ::indexViewToModel(index, m_core->pluginList()); +} + +void PluginListView::updatePluginCount() +{ + int activeMasterCount = 0; + int activeLightMasterCount = 0; + int activeRegularCount = 0; + int masterCount = 0; + int lightMasterCount = 0; + int regularCount = 0; + int activeVisibleCount = 0; + + PluginList* list = m_core->pluginList(); + QString filter = ui.filter->text(); + + for (QString plugin : list->pluginNames()) { + bool active = list->isEnabled(plugin); + bool visible = m_sortProxy->filterMatchesPlugin(plugin); + if (list->isLight(plugin) || list->isLightFlagged(plugin)) { + lightMasterCount++; + activeLightMasterCount += active; + activeVisibleCount += visible && active; + } + else if (list->isMaster(plugin)) { + masterCount++; + activeMasterCount += active; + activeVisibleCount += visible && active; + } + else { + regularCount++; + activeRegularCount += active; + activeVisibleCount += visible && active; + } + } + + int activeCount = activeMasterCount + activeLightMasterCount + activeRegularCount; + int totalCount = masterCount + lightMasterCount + regularCount; + + ui.counter->display(activeVisibleCount); + ui.counter->setToolTip(tr("" + "" + "" + "" + "" + "" + "" + "
    TypeActive Total
    All plugins:%1 %2
    ESMs:%3 %4
    ESPs:%7 %8
    ESMs+ESPs:%9 %10
    ESLs:%5 %6
    ") + .arg(activeCount).arg(totalCount) + .arg(activeMasterCount).arg(masterCount) + .arg(activeLightMasterCount).arg(lightMasterCount) + .arg(activeRegularCount).arg(regularCount) + .arg(activeMasterCount + activeRegularCount).arg(masterCount + regularCount) + ); +} + +void PluginListView::onFilterChanged(const QString& filter) +{ + if (!filter.isEmpty()) { + setStyleSheet("QTreeView { border: 2px ridge #f00; }"); + ui.counter->setStyleSheet("QLCDNumber { border: 2px ridge #f00; }"); + } + else { + setStyleSheet(""); + ui.counter->setStyleSheet(""); + } + updatePluginCount(); +} + +void PluginListView::setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* mwui) +{ + m_core = &core; + ui = { mwui->activePluginsCounter, mwui->espFilterEdit }; + + m_sortProxy = new PluginListSortProxy(&core); + m_sortProxy->setSourceModel(core.pluginList()); + setModel(m_sortProxy); + + sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder); + setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(this)); + installEventFilter(core.pluginList()); + + connect(ui.filter, &QLineEdit::textChanged, m_sortProxy, &PluginListSortProxy::updateFilter); + connect(ui.filter, &QLineEdit::textChanged, this, &PluginListView::onFilterChanged); + } diff --git a/src/pluginlistview.h b/src/pluginlistview.h index bdd4ee61..95450ffd 100644 --- a/src/pluginlistview.h +++ b/src/pluginlistview.h @@ -5,20 +5,61 @@ #include #include "viewmarkingscrollbar.h" +namespace Ui { + class MainWindow; +} + +class OrganizerCore; +class MainWindow; +class PluginListSortProxy; + class PluginListView : public QTreeView { Q_OBJECT public: - explicit PluginListView(QWidget *parent = 0); - virtual void dragEnterEvent(QDragEnterEvent *event); - virtual void setModel(QAbstractItemModel *model); -signals: - void dropModeUpdate(bool dropOnRows); + explicit PluginListView(QWidget* parent = nullptr); + void setModel(QAbstractItemModel* model) override; + + void setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* mwui); + + // the column by which the plugin list is currently sorted + // + int sortColumn() const; + + // update the plugin counter + // + void updatePluginCount(); + + // TODO: Move these to private when possible. + // 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; + + +protected slots: + + void onFilterChanged(const QString& filter); - public slots: private: - ViewMarkingScrollBar *m_Scrollbar; + struct PluginListViewUi + { + // the plguin counter + QLCDNumber* counter; + + // the filter + QLineEdit* filter; + }; + + OrganizerCore* m_core; + PluginListViewUi ui; + + PluginListSortProxy* m_sortProxy; + + ViewMarkingScrollBar* m_Scrollbar; }; #endif // PLUGINLISTVIEW_H -- 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/organizercore.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 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/organizercore.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 42586639a8f17a19779cb646f1327dd92b18d135 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 13:27:21 +0100 Subject: Move connection from organizer to mainwindow. --- src/mainwindow.cpp | 12 ++++++++++++ src/organizercore.cpp | 15 --------------- 2 files changed, 12 insertions(+), 15 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b979be86..6929a009 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -464,6 +464,18 @@ MainWindow::MainWindow(Settings &settings m_Tutorial.expose("espList", m_OrganizerCore.pluginList()); m_OrganizerCore.setUserInterface(this); + connect(m_OrganizerCore.modList(), &ModList::showMessage, + [=](auto&& message) { showMessage(message); }); + connect(m_OrganizerCore.modList(), &ModList::modRenamed, + [=](auto&& oldName, auto&& newName) { modRenamed(oldName, newName); }); + connect(m_OrganizerCore.modList(), &ModList::modUninstalled, + [=](auto&& name) { modRemoved(name); }); + connect(m_OrganizerCore.modList(), &ModList::fileMoved, + [=](auto&& ...args) { fileMoved(args...); }); + connect(m_OrganizerCore.installationManager(), &InstallationManager::modReplaced, + [=](auto&& name) { modRemoved(name); }); + connect(m_OrganizerCore.downloadManager(), &DownloadManager::showMessage, + [=](auto&& message) { showMessage(message); }); for (const QString &fileName : m_PluginContainer.pluginFileNames()) { installTranslator(QFileInfo(fileName).baseName()); } diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 2b1cbdc6..6eb81792 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -235,21 +235,6 @@ void OrganizerCore::setUserInterface(IUserInterface* ui) w = m_UserInterface->mainWindow(); } - if (w) { - connect(&m_ModList, SIGNAL(showMessage(QString)), w, - SLOT(showMessage(QString))); - connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), w, - SLOT(modRenamed(QString, QString))); - connect(&m_ModList, SIGNAL(modUninstalled(QString)), w, - SLOT(modRemoved(QString))); - connect(&m_InstallationManager, SIGNAL(modReplaced(QString)), w, - SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), w, - SLOT(fileMoved(QString, QString, QString))); - connect(&m_DownloadManager, SIGNAL(showMessage(QString)), w, - SLOT(showMessage(QString))); - } - m_InstallationManager.setParentWidget(w); m_Updater.setUserInterface(w); m_UILocker.setUserInterface(w); -- cgit v1.3.1 From 740c383f632712e378c7cfbafea6414a23f58e09 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 2 Jan 2021 16:28:55 +0100 Subject: Add index attribute to ModInfo. --- src/modinfo.cpp | 4 +++ src/modinfo.h | 72 ++++++++++++++++++++++++++------------------------ src/modinforegular.cpp | 6 +++-- src/organizercore.cpp | 2 ++ 4 files changed, 48 insertions(+), 36 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 509c3837..6f3b530e 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -91,6 +91,7 @@ ModInfo::Ptr ModInfo::createFrom(PluginContainer *pluginContainer, const MOBase: } else { result = ModInfo::Ptr(new ModInfoRegular(pluginContainer, game, dir, directoryStructure)); } + result->m_Index = s_Collection.size(); s_Collection.push_back(result); return result; } @@ -105,6 +106,7 @@ ModInfo::Ptr ModInfo::createFromPlugin(const QString &modName, QMutexLocker locker(&s_Mutex); ModInfo::Ptr result = ModInfo::Ptr( new ModInfoForeign(modName, espName, bsaNames, modType, game, directoryStructure, pluginContainer)); + result->m_Index = s_Collection.size(); s_Collection.push_back(result); return result; } @@ -115,6 +117,7 @@ ModInfo::Ptr ModInfo::createFromOverwrite( { QMutexLocker locker(&s_Mutex); ModInfo::Ptr overwrite = ModInfo::Ptr(new ModInfoOverwrite(pluginContainer, game, directoryStructure)); + overwrite->m_Index = s_Collection.size(); s_Collection.push_back(overwrite); return overwrite; } @@ -286,6 +289,7 @@ void ModInfo::updateIndices() QString modName = s_Collection[i]->internalName(); QString game = s_Collection[i]->gameName(); int modID = s_Collection[i]->nexusId(); + s_Collection[i]->m_Index = i; s_ModsByName[modName] = i; s_ModsByModID[std::pair(game, modID)].push_back(i); } diff --git a/src/modinfo.h b/src/modinfo.h index 3981be18..f0c7bcb5 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -210,30 +210,6 @@ public: // Static functions: static std::set filteredMods( QString gameName, QVariantList updateData, bool addOldMods = false, bool markUpdated = false); - /** - * @brief Create a new mod from the specified directory and add it to the collection. - * - * @param dir Directory to create from. - * - * @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); - - /** - * @brief Create a new "foreign-managed" mod from a tuple of plugin and archives. - * - * @param espName Name of the plugin. - * @param bsaNames Names of archives. - * - * @return a new mod. - */ - 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); - /** * @brief Check wheter a name corresponds to a separator or not, * @@ -979,33 +955,61 @@ protected: * using multiple threads for all the mods. */ virtual void prefetch() = 0; - - static void updateIndices(); static bool ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS); protected: - static std::vector s_Collection; - static ModInfo::Ptr s_Overwrite; - static std::map s_ModsByName; + // the index of the mod in s_Collection, only valid after updateIndices() + int m_Index; int m_PrimaryCategory; std::set m_Categories; - MOBase::VersionInfo m_Version; - bool m_PluginSelected = false; -private: +protected: + + friend class OrganizerCore; + + /** + * @brief Create a new mod from the specified directory and add it to the collection. + * + * @param dir Directory to create from. + * + * @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); + + /** + * @brief Create a new "foreign-managed" mod from a tuple of plugin and archives. + * + * @param espName Name of the plugin. + * @param bsaNames Names of archives. + * + * @return a new mod. + */ + 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); static ModInfo::Ptr createFromOverwrite(PluginContainer* pluginContainer, const MOBase::IPluginGame* game, MOShared::DirectoryEntry** directoryStructure); -private: + // update the m_Index attribute of all mods and the various mapping + // + static void updateIndices(); + +protected: static QMutex s_Mutex; - static std::map, std::vector > s_ModsByModID; + static std::vector s_Collection; + static ModInfo::Ptr s_Overwrite; + static std::map s_ModsByName; + static std::map, std::vector> s_ModsByModID; static int s_NextID; }; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index c712adb1..6363dd6e 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -254,7 +254,7 @@ void ModInfoRegular::saveMeta() if (m_TrackedState != TrackedState::TRACKED_UNKNOWN) { metaFile.setValue("tracked", static_cast>(m_TrackedState)); } - + metaFile.remove("installedFiles"); metaFile.beginWriteArray("installedFiles"); int idx = 0; @@ -462,6 +462,8 @@ bool ModInfoRegular::setName(const QString &name) std::map::iterator nameIter = s_ModsByName.find(m_Name); if (nameIter != s_ModsByName.end()) { + QMutexLocker locker(&s_Mutex); + unsigned int index = nameIter->second; s_ModsByName.erase(nameIter); @@ -879,7 +881,7 @@ std::vector ModInfoRegular::getIniTweaks() const } -std::map ModInfoRegular::pluginSettings(const QString& pluginName) const +std::map ModInfoRegular::pluginSettings(const QString& pluginName) const { auto itp = m_PluginSettings.find(pluginName); if (itp == std::end(m_PluginSettings)) { diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 6eb81792..bf9308b8 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -690,6 +690,8 @@ MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) settingsFile.endArray(); } + // 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(); } -- 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/organizercore.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