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/modlistcontextmenu.cpp | 273 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 src/modlistcontextmenu.cpp (limited to 'src/modlistcontextmenu.cpp') 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")); + } +} -- cgit v1.3.1 From e8c2d9cd29967be928b8649fd580a3be9cae3684 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 23:35:44 +0100 Subject: More context menu stuff moved. --- src/mainwindow.cpp | 112 ++----------------- src/mainwindow.h | 7 -- src/modlistcontextmenu.cpp | 264 ++++++++++----------------------------------- src/modlistcontextmenu.h | 18 +++- src/modlistview.h | 4 +- src/modlistviewactions.cpp | 103 +++++++++++++++++- src/modlistviewactions.h | 19 +++- 7 files changed, 192 insertions(+), 335 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 58061c96..265cc5c2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2629,70 +2629,9 @@ void MainWindow::overwriteClosed(int) m_OrganizerCore.refreshDirectoryStructure(); } - -void MainWindow::displayModInformation( - ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) +void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) { - if (!m_OrganizerCore.modList()->modInfoAboutToChange(modInfo)) { - log::debug("A different mod information dialog is open. If this is incorrect, please restart MO"); - return; - } - std::vector flags = modInfo->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { - QDialog *dialog = this->findChild("__overwriteDialog"); - try { - if (dialog == nullptr) { - dialog = new OverwriteInfoDialog(modInfo, this); - dialog->setObjectName("__overwriteDialog"); - } else { - qobject_cast(dialog)->setModInfo(modInfo); - } - - dialog->show(); - dialog->raise(); - dialog->activateWindow(); - connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); - } catch (const std::exception &e) { - reportError(tr("Failed to display overwrite dialog: %1").arg(e.what())); - } - } else { - modInfo->saveMeta(); - - ModInfoDialog dialog(this, &m_OrganizerCore, &m_PluginContainer, modInfo); - connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); - - //Open the tab first if we want to use the standard indexes of the tabs. - if (tabID != ModInfoTabIDs::None) { - dialog.selectTab(tabID); - } - - dialog.exec(); - - modInfo->saveMeta(); - emit modInfoDisplayed(); - m_OrganizerCore.modList()->modInfoChanged(modInfo); - } - - if (m_OrganizerCore.currentProfile()->modEnabled(modIndex) - && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { - FilesOrigin& origin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(modInfo->name())); - origin.enable(false); - - if (m_OrganizerCore.directoryStructure()->originExists(ToWString(modInfo->name()))) { - FilesOrigin& origin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(modInfo->name())); - origin.enable(false); - - m_OrganizerCore.directoryRefresher()->addModToStructure(m_OrganizerCore.directoryStructure() - , modInfo->name() - , m_OrganizerCore.currentProfile()->getModPriority(modIndex) - , modInfo->absolutePath() - , modInfo->stealFiles() - , modInfo->archives()); - DirectoryRefresher::cleanStructure(m_OrganizerCore.directoryStructure()); - m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins(); - m_OrganizerCore.refreshLists(); - } - } + ui->modList->actions().displayModInformation(modInfo, modIndex, tabID); } bool MainWindow::closeWindow() @@ -2723,26 +2662,6 @@ ModInfo::Ptr MainWindow::previousModInList(int modIndex) return ModInfo::getByIndex(modIndex); } -void MainWindow::displayModInformation(const QString &modName, ModInfoTabIDs tabID) -{ - unsigned int index = ModInfo::getIndex(modName); - if (index == UINT_MAX) { - log::error("failed to resolve mod name {}", modName); - return; - } - - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - displayModInformation(modInfo, index, tabID); -} - - -void MainWindow::displayModInformation(int row, ModInfoTabIDs tabID) -{ - ModInfo::Ptr modInfo = ModInfo::getByIndex(row); - displayModInformation(modInfo, row, tabID); -} - - void MainWindow::ignoreMissingData_clicked(int modIndex) { const auto rows = ui->modList->selectionModel()->selectedRows(); @@ -3141,7 +3060,7 @@ void MainWindow::updatePluginCount() void MainWindow::information_clicked(int modIndex) { try { - displayModInformation(modIndex); + ui->modList->actions().displayModInformation(modIndex); } catch (const std::exception &e) { reportError(e.what()); } @@ -3388,7 +3307,7 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) case ModList::COL_CONFLICTFLAGS: tab = ModInfoTabIDs::Conflicts; break; } - displayModInformation(modIndex, tab); + ui->modList->actions().displayModInformation(modIndex, tab); // workaround to cancel the editor that might have opened because of // selection-click ui->modList->closePersistentEditor(index); @@ -3424,7 +3343,7 @@ void MainWindow::openOriginInformation_clicked() std::vector flags = modInfo->getFlags(); if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + ui->modList->actions().displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); } } catch (const std::exception &e) { @@ -3473,7 +3392,7 @@ void MainWindow::on_espList_doubleClicked(const QModelIndex &index) } else { - displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + ui->modList->actions().displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); // workaround to cancel the editor that might have opened because of // selection-click ui->espList->closePersistentEditor(index); @@ -3950,27 +3869,16 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) QTreeView *modList = findChild("modList"); QModelIndex contextIdx = mapToModel(m_OrganizerCore.modList(), ui->modList->indexAt(pos)); - int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); - - int contextColumn = contextIdx.column(); - if (modIndex == -1) { + if (!contextIdx.isValid()) { // no selection ModListGlobalContextMenu(m_OrganizerCore, ui->modList).exec(modList->viewport()->mapToGlobal(pos)); } else { - QMenu menu(this); - - QMenu *allMods = new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, this); - 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); - } + int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); + int contextColumn = contextIdx.column(); - menu.addSeparator(); + ModListContextMenu menu(m_OrganizerCore, contextIdx, ui->modList); ModInfo::Ptr info = ModInfo::getByIndex(modIndex); std::vector flags = info->getFlags(); diff --git a/src/mainwindow.h b/src/mainwindow.h index a49c12fe..57f26220 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -153,11 +153,6 @@ public slots: signals: - /** - * @brief emitted after the information dialog has been closed - */ - void modInfoDisplayed(); - /** * @brief emitted when the selected style changes */ @@ -209,7 +204,6 @@ private: void refreshExecutablesList(); bool modifyExecutablesDialog(int selection); - void displayModInformation(int row, ModInfoTabIDs tab=ModInfoTabIDs::None); /** * Sets category selections from menu; for multiple mods, this will only apply @@ -455,7 +449,6 @@ private slots: void onFiltersOptions( ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep); - void displayModInformation(const QString &modName, ModInfoTabIDs tabID); void visitNexusOrWebPage(const QModelIndex& idx); void modRenamed(const QString &oldName, const QString &newName); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 5b45a217..41031eaa 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -41,233 +41,77 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV addAction(tr("Export to csv..."), [=]() { view->actions().exportModListCSV(); }); } -ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndexList& index, ModListView* modListView) : - QMenu(modListView) +ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* view) : + QMenu(view) , m_core(core) - , m_index(index) + , m_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(); + if (view->selectionModel()->hasSelection()) { + m_index = view->indexViewToModel(view->selectionModel()->selectedRows()); + } + else { + m_index = { index }; + } - menu.addAction(tr("Enable selected"), [=]() { enableSelectedMods_clicked(); }); - menu.addAction(tr("Disable selected"), [=]() { disableSelectedMods_clicked(); }); - menu.addSeparator(); + QMenu* allMods = new ModListGlobalContextMenu(core, view, view); + allMods->setTitle(tr("All Mods")); + addMenu(allMods); - addModSendToContextMenu(&menu); + if (view->hasCollapsibleSeparators()) { + addAction(tr("Collapse all"), view, &QTreeView::collapseAll); + addAction(tr("Expand all"), view, &QTreeView::expandAll); + } - 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); }); + addSeparator(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { - menu.addAction(tr("Restore hidden files"), [=]() { restoreHiddenFiles_clicked(modIndex); }); - } + // Add type-specific items + ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); - menu.addSeparator(); + if (info->isOverwrite()) { + addOverwriteActions(core, view); + } + else if (info->isBackup()) { + addBackupActions(core, view); + } + else if (info->isSeparator()) { + addSeparatorActions(core, view); + } + else if (info->isForeign()) { + addForeignActions(core, view); + } + else { + addRegularActions(core, view); + } - 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(); - } + // add information for all except foreign + if (!info->isForeign()) { + QAction* infoAction = addAction(tr("Information..."), [=]() { view->actions().displayModInformation(m_index[0].row()); }); + setDefaultAction(infoAction); + } +} - 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; - } - } +void ModListContextMenu::addOverwriteActions(OrganizerCore& core, ModListView* modListView) +{ - 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(); +void ModListContextMenu::addSeparatorActions(OrganizerCore& core, ModListView* modListView) +{ - 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); }); - } +void ModListContextMenu::addForeignActions(OrganizerCore& core, ModListView* modListView) +{ - menu.addSeparator(); +} - if (info->nexusId() > 0) { - menu.addAction(tr("Visit on Nexus"), [=]() { visitOnNexus_clicked(modIndex); }); - } +void ModListContextMenu::addBackupActions(OrganizerCore& core, ModListView* modListView) +{ - 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); }); - } +void ModListContextMenu::addRegularActions(OrganizerCore& core, ModListView* modListView) +{ - 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 index bc72fbdf..9d015afc 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -23,13 +23,21 @@ class ModListContextMenu : public QMenu { Q_OBJECT -private: +public: - friend class ModListView; + // creates a new context menu, the given index is the one for the click and should be valid + // + ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* modListView); + +private: - // 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); + // add actions/menus to this menu for each type of mod + // + void addOverwriteActions(OrganizerCore& core, ModListView* modListView); + void addSeparatorActions(OrganizerCore& core, ModListView* modListView); + void addForeignActions(OrganizerCore& core, ModListView* modListView); + void addBackupActions(OrganizerCore& core, ModListView* modListView); + void addRegularActions(OrganizerCore& core, ModListView* modListView); OrganizerCore& m_core; QModelIndexList m_index; diff --git a/src/modlistview.h b/src/modlistview.h index d5eea54f..0f131631 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -105,8 +105,6 @@ public slots: // void updateModCount(); -protected: - // map from/to the view indexes to the model // QModelIndex indexModelToView(const QModelIndex& index) const; @@ -114,6 +112,8 @@ protected: QModelIndex indexViewToModel(const QModelIndex& index) const; QModelIndexList indexViewToModel(const QModelIndexList& index) const; +protected: + // returns the next/previous index of the given index // QModelIndex nextIndex(const QModelIndex& index) const; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 8b234b81..6faebc15 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -5,28 +5,37 @@ #include #include +#include #include #include "categories.h" #include "filedialogmemory.h" #include "filterlist.h" +#include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" +#include "mainwindow.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "savetextasdialog.h" #include "organizercore.h" +#include "overwriteinfodialog.h" #include "csvbuilder.h" +#include "shared/filesorigin.h" +#include "shared/directoryentry.h" +#include "shared/fileregister.h" +#include "directoryrefresher.h" using namespace MOBase; +using namespace MOShared; ModListViewActions::ModListViewActions( - OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, QObject* nxmReceiver, ModListView* view) : + OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, MainWindow* mainWindow, ModListView* view) : QObject(view) , m_core(core) , m_filters(filters) , m_categories(categoryFactory) - , m_receiver(nxmReceiver) + , m_main(mainWindow) , m_view(view) { @@ -143,9 +152,9 @@ 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()); + checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_main); + NexusInterface::instance().requestEndorsementInfo(m_main, QVariant(), QString()); + NexusInterface::instance().requestTrackingInfo(m_main, QVariant(), QString()); } else { QString apiKey; if (GlobalSettings::nexusApiKey(apiKey)) { @@ -337,3 +346,87 @@ void ModListViewActions::exportModListCSV() const } } } + +void ModListViewActions::displayModInformation(const QString& modName, ModInfoTabIDs tab) const +{ + unsigned int index = ModInfo::getIndex(modName); + if (index == UINT_MAX) { + log::error("failed to resolve mod name {}", modName); + return; + } + + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + displayModInformation(modInfo, index, tab); +} + +void ModListViewActions::displayModInformation(unsigned int index, ModInfoTabIDs tab) const +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + displayModInformation(modInfo, index, tab); +} + +void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tab) const +{ + if (!m_core.modList()->modInfoAboutToChange(modInfo)) { + log::debug("a different mod information dialog is open. If this is incorrect, please restart MO"); + return; + } + std::vector flags = modInfo->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { + QDialog* dialog = m_main->findChild("__overwriteDialog"); + try { + if (dialog == nullptr) { + dialog = new OverwriteInfoDialog(modInfo, m_main); + dialog->setObjectName("__overwriteDialog"); + } + else { + qobject_cast(dialog)->setModInfo(modInfo); + } + + dialog->show(); + dialog->raise(); + dialog->activateWindow(); + connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); + } + catch (const std::exception& e) { + reportError(tr("Failed to display overwrite dialog: %1").arg(e.what())); + } + } + else { + modInfo->saveMeta(); + + ModInfoDialog dialog(m_main, &m_core, &m_core.pluginContainer(), modInfo); + connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); + + //Open the tab first if we want to use the standard indexes of the tabs. + if (tab != ModInfoTabIDs::None) { + dialog.selectTab(tab); + } + + dialog.exec(); + + modInfo->saveMeta(); + m_core.modList()->modInfoChanged(modInfo); + } + + if (m_core.currentProfile()->modEnabled(modIndex) + && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { + FilesOrigin& origin = m_core.directoryStructure()->getOriginByName(ToWString(modInfo->name())); + origin.enable(false); + + if (m_core.directoryStructure()->originExists(ToWString(modInfo->name()))) { + FilesOrigin& origin = m_core.directoryStructure()->getOriginByName(ToWString(modInfo->name())); + origin.enable(false); + + m_core.directoryRefresher()->addModToStructure(m_core.directoryStructure() + , modInfo->name() + , m_core.currentProfile()->getModPriority(modIndex) + , modInfo->absolutePath() + , modInfo->stealFiles() + , modInfo->archives()); + DirectoryRefresher::cleanStructure(m_core.directoryStructure()); + m_core.directoryStructure()->getFileRegister()->sortOrigins(); + m_core.refreshLists(); + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 49cffaef..b01b8e79 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -4,8 +4,12 @@ #include #include +#include "modinfo.h" +#include "modinfodialogfwd.h" + class CategoryFactory; class FilterList; +class MainWindow; class ModListView; class OrganizerCore; @@ -15,14 +19,14 @@ class ModListViewActions : public QObject 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 + // currently passing the main window itself because a lots of stuff needs it but + // it would be nice to avoid passing it at some point // ModListViewActions( OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, - QObject* nxmReceiver, + MainWindow* mainWindow, ModListView* view); // install the mod from the given archive @@ -43,12 +47,19 @@ public: // void exportModListCSV() const; + // display mod information + // + void displayModInformation(const QString& modName, ModInfoTabIDs tabID = ModInfoTabIDs::None) const; + void displayModInformation(unsigned int index, ModInfoTabIDs tab = ModInfoTabIDs::None) const; + void displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID = ModInfoTabIDs::None) const; + + private: OrganizerCore& m_core; FilterList& m_filters; CategoryFactory& m_categories; - QObject* m_receiver; // receiver for NXM signals (temporary) + MainWindow* m_main; ModListView* m_view; }; -- cgit v1.3.1 From fb52a129b3a878511cf754daed433d9a789689c8 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 00:06:12 +0100 Subject: More stuff moved to mod list context. --- src/mainwindow.cpp | 121 +++------------------------------------------ src/mainwindow.h | 7 --- src/modlist.cpp | 22 ++++++++- src/modlist.h | 6 ++- src/modlistcontextmenu.cpp | 39 ++++++++++----- src/modlistcontextmenu.h | 17 ++++--- src/modlistview.cpp | 2 +- src/modlistviewactions.cpp | 68 +++++++++++++++++++++++++ src/modlistviewactions.h | 6 +++ 9 files changed, 148 insertions(+), 140 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 265cc5c2..91897d2f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3832,22 +3832,6 @@ QMenu *MainWindow::openFolderMenu() return FolderMenu; } -void MainWindow::addModSendToContextMenu(QMenu *menu) -{ - if (ui->modList->sortColumn() != ModList::COL_PRIORITY) - return; - - QMenu *sub_menu = new QMenu(menu); - sub_menu->setTitle(tr("Send to")); - sub_menu->addAction(tr("Top"), [&]() { sendSelectedModsToTop_clicked(); }); - sub_menu->addAction(tr("Bottom"), [&]() { sendSelectedModsToBottom_clicked(); }); - sub_menu->addAction(tr("Priority..."), [&]() { sendSelectedModsToPriority_clicked(); }); - sub_menu->addAction(tr("Separator..."), [&]() { sendSelectedModsToSeparator_clicked(); }); - - menu->addMenu(sub_menu); - menu->addSeparator(); -} - void MainWindow::addPluginSendToContextMenu(QMenu *menu) { if (m_PluginListSortProxy->sortColumn() != PluginList::COL_PRIORITY) @@ -3932,7 +3916,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Rename Separator..."), [=]() { renameMod_clicked(); }); menu.addAction(tr("Remove Separator..."), [=]() { removeMod_clicked(modIndex); }); menu.addSeparator(); - addModSendToContextMenu(&menu); + if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { + menu.addMenu(menu.createSendToContextMenu()); + menu.addSeparator(); + } menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); if (info->color().isValid()) { @@ -3944,7 +3931,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // foregin else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - addModSendToContextMenu(&menu); } // regular @@ -3981,7 +3967,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); - addModSendToContextMenu(&menu); + if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { + menu.addMenu(menu.createSendToContextMenu()); + menu.addSeparator(); + } menu.addAction(tr("Rename Mod..."), [=]() { renameMod_clicked(); }); menu.addAction(tr("Reinstall Mod"), [=]() { reinstallMod_clicked(modIndex); }); @@ -5477,97 +5466,3 @@ void MainWindow::on_clearFiltersButton_clicked() ui->modFilterEdit->clear(); deselectFilters(); } - -void MainWindow::sendSelectedModsToPriority(int newPriority) -{ - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection()) { - std::vector modsToMove; - for (auto idx : selection->selectedRows(ModList::COL_PRIORITY)) { - modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); - } - - if (modsToMove.size() == 1) { - m_OrganizerCore.modList()->changeModPriority(modsToMove[0], newPriority); - } - else { - m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); - } - } -} - -void MainWindow::sendSelectedModsToTop_clicked() -{ - sendSelectedModsToPriority(0); -} - -void MainWindow::sendSelectedModsToBottom_clicked() -{ - sendSelectedModsToPriority(INT_MAX); -} - -void MainWindow::sendSelectedModsToPriority_clicked() -{ - bool ok; - int newPriority = QInputDialog::getInt(this, - tr("Set Priority"), tr("Set the priority of the selected mods"), - 0, 0, INT_MAX, 1, &ok); - if (!ok) return; - - sendSelectedModsToPriority(newPriority); -} - -void MainWindow::sendSelectedModsToSeparator_clicked() -{ - QStringList separators; - auto indexesByPriority = m_OrganizerCore.currentProfile()->getAllIndexesByPriority(); - for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) { - if ((iter->second != UINT_MAX)) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second); - if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { - separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name - } - } - } - - ListDialog dialog(this); - dialog.setWindowTitle("Select a separator..."); - dialog.setChoices(separators); - - if (dialog.exec() == QDialog::Accepted) { - QString result = dialog.getChoice(); - if (!result.isEmpty()) { - result += "_separator"; - - int newPriority = INT_MAX; - bool foundSection = false; - for (auto mod : m_OrganizerCore.modsSortedByProfilePriority(m_OrganizerCore.currentProfile())) { - unsigned int modIndex = ModInfo::getIndex(mod); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - if (!foundSection && result.compare(mod) == 0) { - foundSection = true; - } else if (foundSection && modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { - newPriority = m_OrganizerCore.currentProfile()->getModPriority(modIndex); - break; - } - } - - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection()) { - std::vector modsToMove; - for (QModelIndex idx : selection->selectedRows(ModList::COL_PRIORITY)) { - modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); - } - if (modsToMove.size() == 1) { - int oldPriority = m_OrganizerCore.currentProfile()->getModPriority(modsToMove[0]); - if (oldPriority < newPriority) - --newPriority; - m_OrganizerCore.modList()->changeModPriority(modsToMove[0], newPriority); - } - else { - m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); - } - } - } - } -} diff --git a/src/mainwindow.h b/src/mainwindow.h index 57f26220..790960e1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -244,15 +244,12 @@ private: bool createBackup(const QString &filePath, const QDateTime &time); QString queryRestore(const QString &filePath); - void addModSendToContextMenu(QMenu *menu); void addPluginSendToContextMenu(QMenu *menu); QMenu *openFolderMenu(); void dropLocalFile(const QUrl &url, const QString &outputDir, bool move); - void sendSelectedModsToPriority(int newPriority); - void toggleMO2EndorseState(); void toggleUpdateAction(); @@ -374,10 +371,6 @@ private slots: void information_clicked(int modIndex); void enableSelectedMods_clicked(); void disableSelectedMods_clicked(); - void sendSelectedModsToTop_clicked(); - void sendSelectedModsToBottom_clicked(); - void sendSelectedModsToPriority_clicked(); - void sendSelectedModsToSeparator_clicked(); // data-tree context menu // pluginlist context menu diff --git a/src/modlist.cpp b/src/modlist.cpp index 7da6fe3b..22899aaa 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1417,7 +1417,7 @@ QString ModList::getColumnToolTip(int column) const } } -void ModList::shiftMods(const QModelIndexList& indices, int offset) +void ModList::shiftModsPriority(const QModelIndexList& indices, int offset) { // retrieve the mod index and sort them by priority to avoid issue // when moving them @@ -1451,6 +1451,26 @@ void ModList::shiftMods(const QModelIndexList& indices, int offset) emit modPrioritiesChanged(allIndex); } +void ModList::changeModsPriority(const QModelIndexList& indices, int priority) +{ + if (indices.isEmpty()) { + return; + } + + std::vector allIndex; + for (auto& idx : indices) { + auto index = idx.data(IndexRole).toInt(); + allIndex.push_back(index); + } + + if (allIndex.size() == 1) { + changeModPriority(allIndex[0], priority); + } + else { + changeModPriority(allIndex, priority); + } +} + bool ModList::toggleState(const QModelIndexList& indices) { emit aboutToChangeData(); diff --git a/src/modlist.h b/src/modlist.h index 870978f9..cabd1f32 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -226,7 +226,11 @@ public slots: // shift the priority of mods at the given indices by the given offset // - void shiftMods(const QModelIndexList& indices, int offset); + void shiftModsPriority(const QModelIndexList& indices, int offset); + + // change the priority of the mods specified by the given indices + // + void changeModsPriority(const QModelIndexList& indices, int priority); // toggle the active state of mods at the given indices // diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 41031eaa..c4a82e46 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -45,6 +45,7 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i QMenu(view) , m_core(core) , m_index() + , m_view(view) { if (view->selectionModel()->hasSelection()) { m_index = view->indexViewToModel(view->selectionModel()->selectedRows()); @@ -68,20 +69,23 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i // Add type-specific items ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + // TODO: + // - Don't forget to check for the sort priority for "Send To... " + if (info->isOverwrite()) { - addOverwriteActions(core, view); + addOverwriteActions(); } else if (info->isBackup()) { - addBackupActions(core, view); + addBackupActions(); } else if (info->isSeparator()) { - addSeparatorActions(core, view); + addSeparatorActions(); } else if (info->isForeign()) { - addForeignActions(core, view); + addForeignActions(); } else { - addRegularActions(core, view); + addRegularActions(); } // add information for all except foreign @@ -91,27 +95,40 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i } } -void ModListContextMenu::addOverwriteActions(OrganizerCore& core, ModListView* modListView) +QMenu* ModListContextMenu::createSendToContextMenu() { - + QMenu* menu = new QMenu(m_view); + menu->setTitle(tr("Send to... ")); + menu->addAction(tr("Top"), [=]() { m_view->actions().sendModsToTop(m_index); }); + menu->addAction(tr("Bottom"), [=]() { m_view->actions().sendModsToBottom(m_index); }); + menu->addAction(tr("Priority..."), [=]() { m_view->actions().sendModsToPriority(m_index); }); + menu->addAction(tr("Separator..."), [=]() { m_view->actions().sendModsToSeparator(m_index); }); + return menu; } -void ModListContextMenu::addSeparatorActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addOverwriteActions() { } -void ModListContextMenu::addForeignActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addSeparatorActions() { } -void ModListContextMenu::addBackupActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addForeignActions() +{ + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addMenu(createSendToContextMenu()); + } +} + +void ModListContextMenu::addBackupActions() { } -void ModListContextMenu::addRegularActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addRegularActions() { } diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 9d015afc..3bc15c57 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -29,18 +29,23 @@ public: // ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* modListView); -private: +public: // TODO: Move this to private when all is done + + // create the "Send to... " context menu + // + QMenu* createSendToContextMenu(); // add actions/menus to this menu for each type of mod // - void addOverwriteActions(OrganizerCore& core, ModListView* modListView); - void addSeparatorActions(OrganizerCore& core, ModListView* modListView); - void addForeignActions(OrganizerCore& core, ModListView* modListView); - void addBackupActions(OrganizerCore& core, ModListView* modListView); - void addRegularActions(OrganizerCore& core, ModListView* modListView); + void addOverwriteActions(); + void addSeparatorActions(); + void addForeignActions(); + void addBackupActions(); + void addRegularActions(); OrganizerCore& m_core; QModelIndexList m_index; + ModListView* m_view; }; diff --git a/src/modlistview.cpp b/src/modlistview.cpp index f8192758..8c826882 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -575,7 +575,7 @@ bool ModListView::moveSelection(int key) offset = -offset; } - m_core->modList()->shiftMods(sourceRows, offset); + m_core->modList()->shiftModsPriority(sourceRows, offset); // reset the selection and the index setCurrentIndex(indexModelToView(cindex)); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 6faebc15..0d556e64 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -11,6 +11,7 @@ #include "categories.h" #include "filedialogmemory.h" #include "filterlist.h" +#include "listdialog.h" #include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" @@ -430,3 +431,70 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in } } } + +void ModListViewActions::sendModsToTop(const QModelIndexList& index) const +{ + m_core.modList()->changeModsPriority(index, 0); +} + +void ModListViewActions::sendModsToBottom(const QModelIndexList& index) const +{ + m_core.modList()->changeModsPriority(index, std::numeric_limits::max()); +} + +void ModListViewActions::sendModsToPriority(const QModelIndexList& index) const +{ + bool ok; + int priority = QInputDialog::getInt(m_view, + tr("Set Priority"), tr("Set the priority of the selected mods"), + 0, 0, std::numeric_limits::max(), 1, &ok); + if (!ok) return; + + m_core.modList()->changeModsPriority(index, priority); +} + +void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const +{ + QStringList separators; + auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority(); + for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) { + if ((iter->second != UINT_MAX)) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second); + if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { + separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name + } + } + } + + ListDialog dialog(m_view); + dialog.setWindowTitle("Select a separator..."); + dialog.setChoices(separators); + + if (dialog.exec() == QDialog::Accepted) { + QString result = dialog.getChoice(); + if (!result.isEmpty()) { + result += "_separator"; + + int newPriority = std::numeric_limits::max(); + bool foundSection = false; + for (auto mod : m_core.modsSortedByProfilePriority(m_core.currentProfile())) { + unsigned int modIndex = ModInfo::getIndex(mod); + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + if (!foundSection && result.compare(mod) == 0) { + foundSection = true; + } + else if (foundSection && modInfo->isSeparator()) { + newPriority = m_core.currentProfile()->getModPriority(modIndex); + break; + } + } + + if (index.size() == 1 + && m_core.currentProfile()->getModPriority(index[0].data(ModList::IndexRole).toInt()) < newPriority) { + --newPriority; + } + + m_core.modList()->changeModsPriority(index, newPriority); + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index b01b8e79..d8e1a3d0 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -53,6 +53,12 @@ public: void displayModInformation(unsigned int index, ModInfoTabIDs tab = ModInfoTabIDs::None) const; void displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID = ModInfoTabIDs::None) const; + // move mods to top/bottom, start the "Send to priority" and "Send to separator" dialog + // + void sendModsToTop(const QModelIndexList& index) const; + void sendModsToBottom(const QModelIndexList& index) const; + void sendModsToPriority(const QModelIndexList& index) const; + void sendModsToSeparator(const QModelIndexList& index) const; private: -- cgit v1.3.1 From 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/modlistcontextmenu.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 4ee929b68a5ba3f622fd6cecf37f61b983eb0874 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 00:55:49 +0100 Subject: Move the backup context menu. --- src/mainwindow.cpp | 48 +------------ src/mainwindow.h | 4 +- src/modlistcontextmenu.cpp | 37 +++++++--- src/modlistcontextmenu.h | 2 + src/modlistviewactions.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++++ src/modlistviewactions.h | 11 +++ 6 files changed, 216 insertions(+), 57 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 056eeef7..6942f83b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2365,32 +2365,6 @@ void MainWindow::renameMod_clicked() } } - -void MainWindow::restoreBackup_clicked(int modIndex) -{ - QRegExp backupRegEx("(.*)_backup[0-9]*$"); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - if (backupRegEx.indexIn(modInfo->name()) != -1) { - QString regName = backupRegEx.cap(1); - QDir modDir(QDir::fromNativeSeparators(m_OrganizerCore.settings().paths().mods())); - if (!modDir.exists(regName) || - (QMessageBox::question(this, tr("Overwrite?"), - tr("This will replace the existing mod \"%1\". Continue?").arg(regName), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - if (modDir.exists(regName) && !shellDelete(QStringList(modDir.absoluteFilePath(regName)))) { - reportError(tr("failed to remove mod \"%1\"").arg(regName)); - } else { - QString destinationPath = QDir::fromNativeSeparators(m_OrganizerCore.settings().paths().mods()) + "/" + regName; - if (!modDir.rename(modInfo->absolutePath(), destinationPath)) { - reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath)); - } - m_OrganizerCore.refresh(); - ui->modList->updateModCount(); - } - } - } -} - void MainWindow::modlistChanged(const QModelIndex&, int) { m_OrganizerCore.currentProfile()->writeModlist(); @@ -2850,7 +2824,7 @@ void MainWindow::visitOnNexus_clicked(int modIndex) void MainWindow::visitWebPage_clicked(int index) { - QItemSelectionModel *selection = ui->modList->selectionModel(); + QItemSelectionModel* selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { int count = selection->selectedRows().count(); if (count > 10) { @@ -3740,26 +3714,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // 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"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); } // separator diff --git a/src/mainwindow.h b/src/mainwindow.h index 21c8f2aa..49ecee32 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -151,6 +151,8 @@ public slots: void directory_refreshed(); + void updatePluginCount(); + signals: /** @@ -347,7 +349,6 @@ private slots: // modlist context menu void installMod_clicked(); - void restoreBackup_clicked(int modIndex); void renameMod_clicked(); void removeMod_clicked(int modIndex); void setColor_clicked(int modIndex); @@ -498,7 +499,6 @@ private slots: void esplistSelectionsChanged(const QItemSelection ¤t); void resetActionIcons(); - void updatePluginCount(); private slots: // ui slots // actions diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index dda88735..df80f0d9 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -46,6 +46,7 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i , m_core(core) , m_index(index) , m_view(view) + , m_actions(view->actions()) { if (view->selectionModel()->hasSelection()) { m_selected = view->indexViewToModel(view->selectionModel()->selectedRows()); @@ -99,10 +100,10 @@ QMenu* ModListContextMenu::createSendToContextMenu() { QMenu* menu = new QMenu(m_view); menu->setTitle(tr("Send to... ")); - 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); }); + menu->addAction(tr("Top"), [=]() { m_actions.sendModsToTop(m_selected); }); + menu->addAction(tr("Bottom"), [=]() { m_actions.sendModsToBottom(m_selected); }); + menu->addAction(tr("Priority..."), [=]() { m_actions.sendModsToPriority(m_selected); }); + menu->addAction(tr("Separator..."), [=]() { m_actions.sendModsToSeparator(m_selected); }); return menu; } @@ -110,11 +111,11 @@ 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("Create Mod..."), [=]() { m_actions.createModFromOverwrite(); }); + addAction(tr("Move content to Mod..."), [=]() { m_actions.moveOverwriteContentToExistingMod(); }); + addAction(tr("Clear Overwrite..."), [=]() { m_actions.clearOverwrite(); }); } - addAction(tr("Open in Explorer"), [=]() { m_view->actions().openExplorer(m_selected); }); + addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); } void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) @@ -131,7 +132,27 @@ void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) void ModListContextMenu::addBackupActions(ModInfo::Ptr mod) { + auto flags = mod->getFlags(); + addAction(tr("Restore Backup"), [=]() { m_actions.restoreBackup(m_index); }); + addAction(tr("Remove Backup..."), [=]() { m_actions.removeMods(m_selected); }); + addSeparator(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { + addAction(tr("Ignore missing data"), [=]() { m_actions.ignoreMissingData(m_selected); }); + } + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { + addAction(tr("Mark as converted/working"), [=]() { m_actions.markConverted(m_selected); }); + } + addSeparator(); + if (mod->nexusId() > 0) { + addAction(tr("Visit on Nexus"), [=]() { m_actions.visitOnNexus(m_selected); }); + } + + const auto url = mod->parseCustomURL(); + if (url.isValid()) { + addAction(tr("Visit on %1").arg(url.host()), [=]() { m_actions.visitWebPage(m_selected); }); + } + addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); } void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 22318575..05e6b601 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -10,6 +10,7 @@ #include "modinfo.h" class ModListView; +class ModListViewActions; class OrganizerCore; class ModListGlobalContextMenu : public QMenu @@ -49,6 +50,7 @@ public: // TODO: Move this to private when all is done QModelIndex m_index; QModelIndexList m_selected; ModListView* m_view; + ModListViewActions& m_actions; // shortcut for m_view->actions() }; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index a07fb27c..53c29a5d 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -505,6 +506,150 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const } } +void ModListViewActions::removeMods(const QModelIndexList& indices) const +{ + const int max_items = 20; + + try { + if (indices.size() > 1) { + QString mods; + QStringList modNames; + + int i = 0; + for (auto& idx : indices) { + QString name = idx.data().toString(); + if (!ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->isRegular()) { + continue; + } + + // adds an item for the mod name until `i` reaches `max_items`, which + // adds one "..." item; subsequent mods are not shown on the list but + // are still added to `modNames` below so they can be removed correctly + + if (i < max_items) { + mods += "
  • " + name + "
  • "; + } + else if (i == max_items) { + mods += "
  • ...
  • "; + } + + modNames.append(ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->name()); + ++i; + } + if (QMessageBox::question(m_view, tr("Confirm"), + tr("Remove the following mods?
      %1
    ").arg(mods), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + // use mod names instead of indexes because those become invalid during the removal + DownloadManager::startDisableDirWatcher(); + for (QString name : modNames) { + m_core.modList()->removeRowForce(ModInfo::getIndex(name), QModelIndex()); + } + DownloadManager::endDisableDirWatcher(); + } + } + else if (!indices.isEmpty()) { + m_core.modList()->removeRow(indices[0].data(ModList::IndexRole).toInt(), QModelIndex()); + } + m_view->updateModCount(); + m_main->updatePluginCount(); + } + catch (const std::exception& e) { + reportError(tr("failed to remove mod: %1").arg(e.what())); + } +} + +void ModListViewActions::ignoreMissingData(const QModelIndexList& indices) const +{ + for (auto& idx : indices) { + int row_idx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + info->markValidated(true); + m_core.modList()->notifyChange(row_idx); + } +} + +void ModListViewActions::markConverted(const QModelIndexList& indices) const +{ + for (auto& idx : indices) { + int row_idx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + info->markConverted(true); + m_core.modList()->notifyChange(row_idx); + } +} + +void ModListViewActions::visitOnNexus(const QModelIndexList& indices) const +{ + if (indices.size() > 1) { + if (indices.size() > 10) { + if (QMessageBox::question(m_view, tr("Opening Nexus Links"), + tr("You are trying to open %1 links to Nexus Mods. Are you sure you want to do this?").arg(indices.size()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + int row_idx; + ModInfo::Ptr info; + QString gameName; + + for (auto& idx : indices) { + row_idx = idx.data(ModList::IndexRole).toInt(); + info = ModInfo::getByIndex(row_idx); + int modID = info->nexusId(); + gameName = info->gameName(); + if (modID > 0) { + shell::Open(QUrl(NexusInterface::instance().getModURL(modID, gameName))); + } + else { + log::error("mod '{}' has no nexus id", info->name()); + } + } + } + else if (!indices.isEmpty()) { + int modID = indices[0].data(Qt::UserRole).toInt(); + QString gameName = indices[0].data(Qt::UserRole + 4).toString(); + if (modID > 0) { + shell::Open(QUrl(NexusInterface::instance().getModURL(modID, gameName))); + } + else { + MessageDialog::showMessage(tr("Nexus ID for this mod is unknown"), m_view); + } + } +} + +void ModListViewActions::visitWebPage(const QModelIndexList& indices) const +{ + if (indices.size() > 1) { + if (indices.size() > 10) { + if (QMessageBox::question(m_view, tr("Opening Web Pages"), + tr("You are trying to open %1 Web Pages. Are you sure you want to do this?").arg(indices.size()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + int row_idx; + ModInfo::Ptr info; + QString gameName; + for (auto& idx : indices) { + row_idx = idx.data(ModList::IndexRole).toInt(); + info = ModInfo::getByIndex(row_idx); + + const auto url = info->parseCustomURL(); + if (url.isValid()) { + shell::Open(url); + } + } + } + else if (!indices.isEmpty()) { + ModInfo::Ptr info = ModInfo::getByIndex(indices[0].data(ModList::IndexRole).toInt()); + + const auto url = info->parseCustomURL(); + if (url.isValid()) { + shell::Open(url); + } + } +} + void ModListViewActions::openExplorer(const QModelIndexList& index) const { for (auto& idx : index) { @@ -513,6 +658,32 @@ void ModListViewActions::openExplorer(const QModelIndexList& index) const } } +void ModListViewActions::restoreBackup(const QModelIndex& index) const +{ + QRegExp backupRegEx("(.*)_backup[0-9]*$"); + ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + if (backupRegEx.indexIn(modInfo->name()) != -1) { + QString regName = backupRegEx.cap(1); + QDir modDir(QDir::fromNativeSeparators(m_core.settings().paths().mods())); + if (!modDir.exists(regName) || + (QMessageBox::question(m_view, tr("Overwrite?"), + tr("This will replace the existing mod \"%1\". Continue?").arg(regName), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { + if (modDir.exists(regName) && !shellDelete(QStringList(modDir.absoluteFilePath(regName)))) { + reportError(tr("failed to remove mod \"%1\"").arg(regName)); + } + else { + QString destinationPath = QDir::fromNativeSeparators(m_core.settings().paths().mods()) + "/" + regName; + if (!modDir.rename(modInfo->absolutePath(), destinationPath)) { + reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath)); + } + m_core.refresh(); + m_view->updateModCount(); + } + } + } +} + void ModListViewActions::moveOverwriteContentsTo(const QString& absolutePath) const { ModInfo::Ptr overwriteInfo = ModInfo::getOverwrite(); diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 10aa6c39..05994813 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -60,10 +60,21 @@ public: void sendModsToPriority(const QModelIndexList& index) const; void sendModsToSeparator(const QModelIndexList& index) const; + // actions for most type of mods + void removeMods(const QModelIndexList& indices) const; + void ignoreMissingData(const QModelIndexList& indices) const; + void markConverted(const QModelIndexList& indices) const; + void visitOnNexus(const QModelIndexList& indices) const; + void visitWebPage(const QModelIndexList& indices) const; + // open the Windows explorer for the specified mods // void openExplorer(const QModelIndexList& index) const; + // backup-specific actions + // + void restoreBackup(const QModelIndex& index) const; + // overwrite-specific actions // void createModFromOverwrite() const; -- cgit v1.3.1 From 6e4b1790ae4057dfafd39dadff04c8d9b5f2eaeb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 02:29:31 +0100 Subject: Move the separator context menu. --- src/mainwindow.cpp | 66 +------------------- src/mainwindow.h | 3 +- src/modlist.h | 2 + src/modlistcontextmenu.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++-- src/modlistcontextmenu.h | 52 +++++++++++++++- src/modlistviewactions.cpp | 98 +++++++++++++++++++++++++++++ src/modlistviewactions.h | 29 ++++++++- 7 files changed, 325 insertions(+), 75 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6942f83b..cd31a0bf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3363,45 +3363,6 @@ void MainWindow::addRemoveCategories_MenuHandler(QMenu* menu, int modIndex, cons refreshFilters(); } -void MainWindow::replaceCategories_MenuHandler(QMenu* menu, int modIndex) -{ - QList selected; - for (const QModelIndex &idx : ui->modList->selectionModel()->selectedRows()) { - selected.append(QPersistentModelIndex(idx)); - } - - if (selected.size() > 0) { - QStringList selectedMods; - int minRow = INT_MAX; - int maxRow = -1; - for (int i = 0; i < selected.size(); ++i) { - QModelIndex temp = mapToModel(m_OrganizerCore.modList(), selected.at(i)); - selectedMods.append(temp.data().toString()); - replaceCategoriesFromMenu(menu, mapToModel(m_OrganizerCore.modList(), selected.at(i)).row()); - if (temp.row() < minRow) minRow = temp.row(); - if (temp.row() > maxRow) maxRow = temp.row(); - } - - m_OrganizerCore.modList()->notifyChange(minRow, maxRow + 1); - - // find mods by their name because indices are invalidated - QAbstractItemModel *model = ui->modList->model(); - for (const QString &mod : selectedMods) { - QModelIndexList matches = model->match(model->index(0, 0), Qt::DisplayRole, mod, 1, - Qt::MatchFixedString | Qt::MatchCaseSensitive | Qt::MatchRecursive); - if (matches.size() > 0) { - ui->modList->selectionModel()->select(matches.at(0), QItemSelectionModel::Select | QItemSelectionModel::Rows); - } - } - } else { - //For single mod selections, just do a replace - replaceCategoriesFromMenu(menu, modIndex); - m_OrganizerCore.modList()->notifyChange(modIndex); - } - - refreshFilters(); -} - void MainWindow::saveArchiveList() { if (m_OrganizerCore.isArchivesInit()) { @@ -3703,7 +3664,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); int contextColumn = contextIdx.column(); - ModListContextMenu menu(m_OrganizerCore, contextIdx, ui->modList); + ModListContextMenu menu(contextIdx, m_OrganizerCore, m_CategoryFactory, ui->modList); ModInfo::Ptr info = ModInfo::getByIndex(modIndex); std::vector flags = info->getFlags(); @@ -3718,29 +3679,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // 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(); - if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { - menu.addMenu(menu.createSendToContextMenu()); - menu.addSeparator(); - } - menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); - - if (info->color().isValid()) { - menu.addAction(tr("Reset Color"), [=]() { resetColor_clicked(modIndex); }); - } - - menu.addSeparator(); } // foregin @@ -3864,9 +3802,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) } menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); - } - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { QAction* infoAction = menu.addAction(tr("Information..."), [=]() { information_clicked(modIndex); }); menu.setDefaultAction(infoAction); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 49ecee32..440e39cc 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -152,6 +152,7 @@ public slots: void directory_refreshed(); void updatePluginCount(); + void refreshFilters(); signals: @@ -406,7 +407,6 @@ private slots: 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); @@ -426,7 +426,6 @@ private slots: void onRequestsChanged(const APIStats& stats, const APIUserAccount& user); void deselectFilters(); - void refreshFilters(); void onFiltersCriteria(const std::vector& filters); void onFiltersOptions( ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep); diff --git a/src/modlist.h b/src/modlist.h index cabd1f32..e4f4dfab 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -131,6 +131,8 @@ public: void highlightMods(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry); +public: + /** * @brief Notify the mod list that the given mod has been installed. This is used * to notify the plugin that registered through onModInstalled(). diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index df80f0d9..05c2eebf 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -29,22 +29,126 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV 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 QModelIndex& index, ModListView* view) : + +ModListChangeCategoryMenu::ModListChangeCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent) + : QMenu(tr("Change Categories"), parent) +{ + populate(this, categories, mod); +} + +std::vector> ModListChangeCategoryMenu::categories() const +{ + return categories(this); +} + +std::vector> ModListChangeCategoryMenu::categories(const QMenu* menu) const +{ + std::vector> cats; + for (QAction* action : menu->actions()) { + if (action->menu() != nullptr) { + auto pcats = categories(action->menu()); + cats.insert(cats.end(), pcats.begin(), pcats.end()); + } + else { + QWidgetAction* widgetAction = qobject_cast(action); + if (widgetAction != nullptr) { + QCheckBox* checkbox = qobject_cast(widgetAction->defaultWidget()); + cats.emplace_back(widgetAction->data().toInt(), checkbox->isChecked()); + } + } + } + return cats; +} + +bool ModListChangeCategoryMenu::populate(QMenu* menu, CategoryFactory& factory, ModInfo::Ptr mod, int targetId) +{ + const std::set& categories = mod->getCategories(); + + bool childEnabled = false; + + for (unsigned int i = 1; i < factory.numCategories(); ++i) { + if (factory.getParentID(i) == targetId) { + QMenu* targetMenu = menu; + if (factory.hasChildren(i)) { + targetMenu = menu->addMenu(factory.getCategoryName(i).replace('&', "&&")); + } + + int id = factory.getCategoryID(i); + QScopedPointer checkBox(new QCheckBox(targetMenu)); + bool enabled = categories.find(id) != categories.end(); + checkBox->setText(factory.getCategoryName(i).replace('&', "&&")); + if (enabled) { + childEnabled = true; + } + checkBox->setChecked(enabled ? Qt::Checked : Qt::Unchecked); + + QScopedPointer checkableAction(new QWidgetAction(targetMenu)); + checkableAction->setDefaultWidget(checkBox.take()); + checkableAction->setData(id); + targetMenu->addAction(checkableAction.take()); + + if (factory.hasChildren(i)) { + if (populate(targetMenu, factory, mod, factory.getCategoryID(i)) || enabled) { + targetMenu->setIcon(QIcon(":/MO/gui/resources/check.png")); + } + } + } + } + return childEnabled; +} + +ModListPrimaryCategoryMenu::ModListPrimaryCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent) + : QMenu(tr("Primary Category"), parent) +{ + connect(this, &QMenu::aboutToShow, [=]() { populate(categories, mod); }); +} + +void ModListPrimaryCategoryMenu::populate(const CategoryFactory& factory, ModInfo::Ptr mod) +{ + clear(); + const std::set& categories = mod->getCategories(); + for (int categoryID : categories) { + int catIdx = factory.getCategoryIndex(categoryID); + QWidgetAction* action = new QWidgetAction(this); + try { + QRadioButton* categoryBox = new QRadioButton( + factory.getCategoryName(catIdx).replace('&', "&&"), + this); + connect(categoryBox, &QRadioButton::toggled, [mod, categoryID](bool enable) { + if (enable) { + mod->setPrimaryCategory(categoryID); + } + }); + categoryBox->setChecked(categoryID == mod->primaryCategory()); + action->setDefaultWidget(categoryBox); + } + catch (const std::exception& e) { + log::error("failed to create category checkbox: {}", e.what()); + } + + action->setData(categoryID); + addAction(action); + } +} + +ModListContextMenu::ModListContextMenu( + const QModelIndex& index, OrganizerCore& core, CategoryFactory& categories, ModListView* view) : QMenu(view) , m_core(core) - , m_index(index) + , m_categories(categories) + , m_index(index.model() == view->model() ? view->indexViewToModel(index) : index) , m_view(view) , m_actions(view->actions()) { @@ -55,7 +159,6 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i m_selected = { index }; } - QMenu* allMods = new ModListGlobalContextMenu(core, view, view); allMods->setTitle(tr("All Mods")); addMenu(allMods); @@ -96,6 +199,15 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i } } +void ModListContextMenu::addMenuAsPushButton(QMenu* menu) +{ + QPushButton* pushBtn = new QPushButton(menu->title()); + pushBtn->setMenu(menu); + QWidgetAction* action = new QWidgetAction(this); + action->setDefaultWidget(pushBtn); + addAction(action); +} + QMenu* ModListContextMenu::createSendToContextMenu() { QMenu* menu = new QMenu(m_view); @@ -120,7 +232,35 @@ void ModListContextMenu::addOverwriteActions(ModInfo::Ptr mod) void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) { + addSeparator(); + + // categories + ModListChangeCategoryMenu* categoriesMenu = new ModListChangeCategoryMenu(m_categories, mod, this); + connect(categoriesMenu, &QMenu::aboutToHide, [=]() { + m_actions.setCategories(m_selected, m_index, categoriesMenu->categories()); + }); + addMenuAsPushButton(categoriesMenu); + ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + addMenuAsPushButton(primaryCategoryMenu); + addSeparator(); + + + addAction(tr("Rename Separator..."), [=]() { m_actions.renameMod(m_index); }); + addAction(tr("Remove Separator..."), [=]() { m_actions.removeMods(m_selected); }); + addSeparator(); + + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addMenu(createSendToContextMenu()); + addSeparator(); + } + addAction(tr("Select Color..."), [=]() { m_actions.setColor(m_selected, m_index); }); + + if (mod->color().isValid()) { + addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected, m_index); }); + } + + addSeparator(); } void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 05e6b601..8452bc65 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -9,6 +9,7 @@ #include "modinfo.h" +class CategoryFactory; class ModListView; class ModListViewActions; class OrganizerCore; @@ -18,7 +19,47 @@ class ModListGlobalContextMenu : public QMenu Q_OBJECT public: - ModListGlobalContextMenu(OrganizerCore& core, ModListView* modListView, QWidget* parent = nullptr); + ModListGlobalContextMenu( + OrganizerCore& core, ModListView* modListView, QWidget* parent = nullptr); + +}; + +class ModListChangeCategoryMenu : public QMenu +{ + Q_OBJECT +public: + + ModListChangeCategoryMenu( + CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent = nullptr); + + // return a list of pair from the menu + // + std::vector> categories() const; + +private: + + // populate the tree with the category, using the enabled/disabled state from the + // given mod + // + bool populate(QMenu* menu, CategoryFactory& categories, ModInfo::Ptr mod, int targetId = 0); + + // internal implementation of categories() for recursion + // + std::vector> categories(const QMenu* menu) const; +}; + +class ModListPrimaryCategoryMenu : public QMenu +{ + Q_OBJECT +public: + + ModListPrimaryCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent = nullptr); + +private: + + // populate the categories + // + void populate(const CategoryFactory& categories, ModInfo::Ptr mod); }; @@ -30,7 +71,8 @@ public: // creates a new context menu, the given index is the one for the click and should be valid // - ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* modListView); + ModListContextMenu( + const QModelIndex& index, OrganizerCore& core, CategoryFactory& categories, ModListView* modListView); public: // TODO: Move this to private when all is done @@ -38,6 +80,11 @@ public: // TODO: Move this to private when all is done // QMenu* createSendToContextMenu(); + // special menu for categories + // + void addMenuAsPushButton(QMenu* menu); + + // add actions/menus to this menu for each type of mod // void addOverwriteActions(ModInfo::Ptr mod); @@ -47,6 +94,7 @@ public: // TODO: Move this to private when all is done void addRegularActions(ModInfo::Ptr mod); OrganizerCore& m_core; + CategoryFactory& m_categories; QModelIndex m_index; QModelIndexList m_selected; ModListView* m_view; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 53c29a5d..9a00353f 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -506,6 +506,16 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const } } +void ModListViewActions::renameMod(const QModelIndex& index) const +{ + try { + m_view->edit(index); + } + catch (const std::exception& e) { + reportError(tr("failed to rename mod: %1").arg(e.what())); + } +} + void ModListViewActions::removeMods(const QModelIndexList& indices) const { const int max_items = 20; @@ -650,6 +660,94 @@ void ModListViewActions::visitWebPage(const QModelIndexList& indices) const } } +void ModListViewActions::setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const +{ + auto& settings = m_core.settings(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(refIndex.data(ModList::IndexRole).toInt()); + + QColorDialog dialog(m_view); + dialog.setOption(QColorDialog::ShowAlphaChannel); + + QColor currentColor = modInfo->color(); + if (currentColor.isValid()) { + dialog.setCurrentColor(currentColor); + } + else if (auto c = settings.colors().previousSeparatorColor()) { + dialog.setCurrentColor(*c); + } + + if (!dialog.exec()) + return; + + currentColor = dialog.currentColor(); + if (!currentColor.isValid()) + return; + + settings.colors().setPreviousSeparatorColor(currentColor); + + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + info->setColor(currentColor); + } + +} + +void ModListViewActions::resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const +{ + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + info->setColor(QColor()); + } + m_core.settings().colors().removePreviousSeparatorColor(); +} + +void ModListViewActions::setCategories(ModInfo::Ptr mod, const std::vector>& categories) const +{ + for (auto& [id, enabled] : categories) { + mod->setCategory(id, enabled); + } +} + +void ModListViewActions::setCategoriesIf(ModInfo::Ptr mod, ModInfo::Ptr ref, const std::vector>& categories) const +{ + for (auto& [id, enabled] : categories) { + if (ref->categorySet(id) != enabled) { + mod->setCategory(id, enabled); + } + } +} + +void ModListViewActions::setCategories(const QModelIndexList& selected, const QModelIndex& ref, + const std::vector>& categories) const +{ + ModInfo::Ptr refMod = ModInfo::getByIndex(ref.data(ModList::IndexRole).toInt()); + if (selected.size() > 1) { + + for (auto& idx : selected) { + if (idx.row() != ref.row()) { + setCategoriesIf( + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()), + refMod, categories); + } + } + setCategories(refMod, categories); + } + else if (!selected.isEmpty()) { + // for single mod selections, just do a replace + setCategories(refMod, categories); + } + + for (auto& idx : selected) { + m_core.modList()->notifyChange(idx.data(ModList::IndexRole).toInt()); + } + + // reset the selection manually - still needed + auto viewIndices = m_view->indexModelToView(selected); + for (auto& idx : viewIndices) { + m_view->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); + } +} + void ModListViewActions::openExplorer(const QModelIndexList& index) const { for (auto& idx : index) { diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 05994813..33442ce7 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -60,13 +60,29 @@ public: void sendModsToPriority(const QModelIndexList& index) const; void sendModsToSeparator(const QModelIndexList& index) const; - // actions for most type of mods + // actions for most regular mods + // + void renameMod(const QModelIndex& index) const; void removeMods(const QModelIndexList& indices) const; void ignoreMissingData(const QModelIndexList& indices) const; void markConverted(const QModelIndexList& indices) const; void visitOnNexus(const QModelIndexList& indices) const; void visitWebPage(const QModelIndexList& indices) const; + // set/reset color of the given selection, using the given reference index (index + // at which the context menu was shown) + // + void setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; + void resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; + + // set the category of the mod in the given list, using the given index as reference + // - the categories are set as-is on the refernce mod + // - for the other mods, the category is only set if the current state of the category + // on the reference is different + // + void setCategories(const QModelIndexList& selected, const QModelIndex& ref, + const std::vector>& categories) const; + // open the Windows explorer for the specified mods // void openExplorer(const QModelIndexList& index) const; @@ -89,8 +105,19 @@ signals: private: + // move the contents of the overwrite to the given path + // void moveOverwriteContentsTo(const QString& absolutePath) const; + // set the category of the given mod based on the given array + // + void setCategories(ModInfo::Ptr mod, const std::vector>& categories) const; + + // set the category of the given mod if the category from the reference mod does not match + // the one in the array of categories + // + void setCategoriesIf(ModInfo::Ptr mod, ModInfo::Ptr ref, const std::vector>& categories) const; + private: OrganizerCore& m_core; -- cgit v1.3.1 From 4f89665056b2256ca353bc27314cd025db2f554c Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 03:28:18 +0100 Subject: Move the regular context menu. --- src/mainwindow.cpp | 233 +-------------------------------------------- src/mainwindow.h | 6 -- src/modlistcontextmenu.cpp | 118 ++++++++++++++++++++++- src/modlistview.cpp | 14 --- src/modlistview.h | 5 - src/modlistviewactions.cpp | 232 +++++++++++++++++++++++++++++++++++++++++++- src/modlistviewactions.h | 19 +++- 7 files changed, 366 insertions(+), 261 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index cd31a0bf..1475be50 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -539,6 +539,7 @@ void MainWindow::setupModList() ui->modList->setup(m_OrganizerCore, actions, ui); connect(actions, &ModListViewActions::overwriteCleared, [=]() { scheduleCheckForProblems(); }); + connect(actions, &ModListViewActions::originModified, this, &MainWindow::originModified); 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(); }); @@ -2287,10 +2288,7 @@ void MainWindow::modInstalled(const QString &modName) } // force an update to happen - std::multimap IDs; - ModInfo::Ptr info = ModInfo::getByIndex(index); - IDs.insert(std::make_pair(info->gameName(), info->nexusId())); - modUpdateCheck(IDs); + ui->modList->actions().checkModsForUpdates({ m_OrganizerCore.modList()->index(index, 0) }); } void MainWindow::showMessage(const QString &message) @@ -3423,35 +3421,6 @@ void MainWindow::checkModsForUpdates() } } -void MainWindow::changeVersioningScheme(int modIndex) { - if (QMessageBox::question(this, tr("Continue?"), - tr("The versioning scheme decides which version is considered newer than another.\n" - "This function will guess the versioning scheme under the assumption that the installed version is outdated."), - QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { - - ModInfo::Ptr info = ModInfo::getByIndex(modIndex); - - bool success = false; - - static VersionInfo::VersionScheme schemes[] = { VersionInfo::SCHEME_REGULAR, VersionInfo::SCHEME_DECIMALMARK, VersionInfo::SCHEME_NUMBERSANDLETTERS }; - - for (int i = 0; i < sizeof(schemes) / sizeof(VersionInfo::VersionScheme) && !success; ++i) { - VersionInfo verOld(info->version().canonicalString(), schemes[i]); - VersionInfo verNew(info->newestVersion().canonicalString(), schemes[i]); - if (verOld < verNew) { - info->setVersion(verOld); - info->setNewestVersion(verNew); - success = true; - } - } - if (!success) { - QMessageBox::information(this, tr("Sorry"), - tr("I don't know a versioning scheme where %1 is newer than %2.").arg(info->newestVersion().canonicalString()).arg(info->version().canonicalString()), - QMessageBox::Ok); - } - } -} - void MainWindow::ignoreUpdate(int modIndex) { QItemSelectionModel *selection = ui->modList->selectionModel(); @@ -3470,22 +3439,6 @@ void MainWindow::ignoreUpdate(int modIndex) } } -void MainWindow::checkModUpdates_clicked(int modIndex) -{ - std::multimap IDs; - 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()); - IDs.insert(std::make_pair(info->gameName(), info->nexusId())); - } - } else { - ModInfo::Ptr info = ModInfo::getByIndex(modIndex); - IDs.insert(std::make_pair(info->gameName(), info->nexusId())); - } - modUpdateCheck(IDs); -} - void MainWindow::unignoreUpdate(int modIndex) { QItemSelectionModel *selection = ui->modList->selectionModel(); @@ -3652,162 +3605,14 @@ void MainWindow::addPluginSendToContextMenu(QMenu *menu) void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) { try { - QTreeView *modList = findChild("modList"); - QModelIndex contextIdx = mapToModel(m_OrganizerCore.modList(), ui->modList->indexAt(pos)); if (!contextIdx.isValid()) { // no selection - ModListGlobalContextMenu(m_OrganizerCore, ui->modList).exec(modList->viewport()->mapToGlobal(pos)); + ModListGlobalContextMenu(m_OrganizerCore, ui->modList).exec(ui->modList->viewport()->mapToGlobal(pos)); } else { - int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); - int contextColumn = contextIdx.column(); - - ModListContextMenu menu(contextIdx, m_OrganizerCore, m_CategoryFactory, ui->modList); - - 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()) { - } - - // context menu for mod backups - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { - } - - // separator - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){ - } - - // foregin - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - } - - // 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(); - - if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { - menu.addMenu(menu.createSendToContextMenu()); - menu.addSeparator(); - } - - menu.addAction(tr("Rename Mod..."), [=]() { renameMod_clicked(); }); - menu.addAction(tr("Reinstall Mod"), [=]() { reinstallMod_clicked(modIndex); }); - 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"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); - - QAction* infoAction = menu.addAction(tr("Information..."), [=]() { information_clicked(modIndex); }); - menu.setDefaultAction(infoAction); - } - - menu.exec(modList->viewport()->mapToGlobal(pos)); + ModListContextMenu(contextIdx, m_OrganizerCore, m_CategoryFactory, ui->modList).exec(ui->modList->viewport()->mapToGlobal(pos)); } } catch (const std::exception &e) { reportError(tr("Exception: ").arg(e.what())); @@ -4091,18 +3896,6 @@ void MainWindow::sendSelectedPluginsToPriority_clicked() m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), newPriority); } - -void MainWindow::enableSelectedMods_clicked() -{ - ui->modList->enableSelected(); -} - - -void MainWindow::disableSelectedMods_clicked() -{ - ui->modList->disableSelected(); -} - void MainWindow::updateAvailable() { ui->actionUpdate->setEnabled(true); @@ -4166,24 +3959,6 @@ void MainWindow::actionWontEndorseMO() } } -void MainWindow::modUpdateCheck(std::multimap IDs) -{ - if (m_OrganizerCore.settings().network().offlineMode()) { - return; - } - - if (NexusInterface::instance().getAccessManager()->validated()) { - ModInfo::manualUpdateCheck(this, IDs); - } else { - QString apiKey; - if (GlobalSettings::nexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([=]() { this->modUpdateCheck(IDs); }); - NexusInterface::instance().getAccessManager()->apiCheck(apiKey); - } else - log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); - } -} - void MainWindow::toggleMO2EndorseState() { const auto& s = m_OrganizerCore.settings(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 440e39cc..61bd9326 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -370,8 +370,6 @@ private slots: void openPluginOriginExplorer_clicked(); void openOriginInformation_clicked(); void information_clicked(int modIndex); - void enableSelectedMods_clicked(); - void disableSelectedMods_clicked(); // data-tree context menu // pluginlist context menu @@ -410,8 +408,6 @@ private slots: void modInstalled(const QString &modName); - void modUpdateCheck(std::multimap IDs); - void finishUpdateInfo(); void nxmEndorsementsAvailable(QVariant userData, QVariant resultData, int); @@ -487,8 +483,6 @@ private slots: void removeFromToolbar(QAction* action); void overwriteClosed(int); - void changeVersioningScheme(int modIndex); - void checkModUpdates_clicked(int modIndex); void ignoreUpdate(int modIndex); void unignoreUpdate(int modIndex); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 05c2eebf..303362ba 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -257,7 +257,7 @@ void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) addAction(tr("Select Color..."), [=]() { m_actions.setColor(m_selected, m_index); }); if (mod->color().isValid()) { - addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected, m_index); }); + addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected); }); } addSeparator(); @@ -297,5 +297,121 @@ void ModListContextMenu::addBackupActions(ModInfo::Ptr mod) void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) { + auto flags = mod->getFlags(); + + // categories + ModListChangeCategoryMenu* categoriesMenu = new ModListChangeCategoryMenu(m_categories, mod, this); + connect(categoriesMenu, &QMenu::aboutToHide, [=]() { + m_actions.setCategories(m_selected, m_index, categoriesMenu->categories()); + }); + addMenuAsPushButton(categoriesMenu); + + ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + addMenuAsPushButton(primaryCategoryMenu); + addSeparator(); + + if (mod->downgradeAvailable()) { + addAction(tr("Change versioning scheme"), [=]() { m_actions.changeVersioningScheme(m_index); }); + } + + if (mod->nexusId() > 0) + addAction(tr("Force-check updates"), [=]() { m_actions.checkModsForUpdates(m_selected); }); + if (mod->updateIgnored()) { + addAction(tr("Un-ignore update"), [=]() { m_actions.setIgnoreUpdate(m_selected, false); }); + } + else { + if (mod->updateAvailable() || mod->downgradeAvailable()) { + addAction(tr("Ignore update"), [=]() { m_actions.setIgnoreUpdate(m_selected, true); }); + } + } + addSeparator(); + + addAction(tr("Enable selected"), [=]() { m_core.modList()->setActive(m_selected, true); }); + addAction(tr("Disable selected"), [=]() { m_core.modList()->setActive(m_selected, false); }); + + addSeparator(); + + + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addMenu(createSendToContextMenu()); + addSeparator(); + } + + addAction(tr("Rename Mod..."), [=]() { m_actions.renameMod(m_index); }); + addAction(tr("Reinstall Mod"), [=]() { m_actions.reinstallMod(m_index); }); + addAction(tr("Remove Mod..."), [=]() { m_actions.removeMods(m_selected); }); + addAction(tr("Create Backup"), [=]() { m_actions.createBackup(m_index); }); + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { + addAction(tr("Restore hidden files"), [=]() { m_actions.restoreHiddenFiles(m_selected); }); + } + + addSeparator(); + + if (m_index.column() == ModList::COL_NOTES) { + addAction(tr("Select Color..."), [=]() { m_actions.setColor(m_selected, m_index); }); + if (mod->color().isValid()) { + addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected); }); + } + addSeparator(); + } + + if (mod->nexusId() > 0 && Settings::instance().nexus().endorsementIntegration()) { + switch (mod->endorsedState()) { + case EndorsedState::ENDORSED_TRUE: { + addAction(tr("Un-Endorse"), [=]() { m_actions.setEndorsed(m_selected, false); }); + } break; + case EndorsedState::ENDORSED_FALSE: { + addAction(tr("Endorse"), [=]() { m_actions.setEndorsed(m_selected, true); }); + addAction(tr("Won't endorse"), [=]() { m_actions.willNotEndorsed(m_selected); }); + } break; + case EndorsedState::ENDORSED_NEVER: { + addAction(tr("Endorse"), [=]() { m_actions.setEndorsed(m_selected, true); }); + } break; + default: { + QAction* action = new QAction(tr("Endorsement state unknown"), this); + action->setEnabled(false); + addAction(action); + } break; + } + } + + if (mod->nexusId() > 0 && Settings::instance().nexus().trackedIntegration()) { + switch (mod->trackedState()) { + case TrackedState::TRACKED_FALSE: { + addAction(tr("Start tracking"), [=]() { m_actions.setTracked(m_selected, true); }); + } break; + case TrackedState::TRACKED_TRUE: { + addAction(tr("Stop tracking"), [=]() { m_actions.setTracked(m_selected, false); }); + } break; + default: { + QAction* action = new QAction(tr("Tracked state unknown"), this); + action->setEnabled(false); + addAction(action); + } break; + } + } + + addSeparator(); + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { + addAction(tr("Ignore missing data"), [=]() { m_actions.ignoreMissingData(m_selected); }); + } + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { + addAction(tr("Mark as converted/working"), [=]() { m_actions.markConverted(m_selected); }); + } + + addSeparator(); + + if (mod->nexusId() > 0) { + addAction(tr("Visit on Nexus"), [=]() { m_actions.visitOnNexus(m_selected); }); + } + + const auto url = mod->parseCustomURL(); + if (url.isValid()) { + addAction(tr("Visit on %1").arg(url.host()), [=]() { m_actions.visitWebPage(m_selected); }); + } + + addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); } diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 8c826882..cf35abdd 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -196,20 +196,6 @@ void ModListView::disableAllVisible() m_core->modList()->setActive(indexViewToModel(allIndex(model())), false); } -void ModListView::enableSelected() -{ - if (selectionModel()->hasSelection()) { - m_core->modList()->setActive(indexViewToModel(selectionModel()->selectedRows()), true); - } -} - -void ModListView::disableSelected() -{ - if (selectionModel()->hasSelection()) { - m_core->modList()->setActive(indexViewToModel(selectionModel()->selectedRows()), false); - } -} - void ModListView::setFilterCriteria(const std::vector& criteria) { m_sortProxy->setCriteria(criteria); diff --git a/src/modlistview.h b/src/modlistview.h index 0f131631..4f27769c 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -91,11 +91,6 @@ public slots: void enableAllVisible(); void disableAllVisible(); - // enable/disable all selected mods - // - void enableSelected(); - void disableSelected(); - // set the filter criteria/options for mods // void setFilterCriteria(const std::vector& criteria); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index f8fd0e4c..5b9c07bc 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -193,6 +193,36 @@ void ModListViewActions::checkModsForUpdates() const } } +void ModListViewActions::checkModsForUpdates(std::multimap const& IDs) const +{ + if (m_core.settings().network().offlineMode()) { + return; + } + + if (NexusInterface::instance().getAccessManager()->validated()) { + ModInfo::manualUpdateCheck(m_main, IDs); + } + else { + QString apiKey; + if (GlobalSettings::nexusApiKey(apiKey)) { + m_core.doAfterLogin([=]() { checkModsForUpdates(IDs); }); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); + } + else + log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); + } +} + +void ModListViewActions::checkModsForUpdates(const QModelIndexList& indices) const +{ + std::multimap ids; + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + ids.insert(std::make_pair(info->gameName(), info->nexusId())); + } + checkModsForUpdates(ids); +} + void ModListViewActions::exportModListCSV() const { QDialog selection(m_view); @@ -509,7 +539,7 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const void ModListViewActions::renameMod(const QModelIndex& index) const { try { - m_view->edit(index); + m_view->edit(m_view->indexModelToView(index)); } catch (const std::exception& e) { reportError(tr("failed to rename mod: %1").arg(e.what())); @@ -578,13 +608,52 @@ void ModListViewActions::ignoreMissingData(const QModelIndexList& indices) const } } +void ModListViewActions::setIgnoreUpdate(const QModelIndexList& indices, bool ignore) const +{ + for (auto& idx : indices) { + int modIdx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(modIdx); + info->ignoreUpdate(ignore); + m_core.modList()->notifyChange(modIdx); + } +} + +void ModListViewActions::changeVersioningScheme(const QModelIndex& index) const { + if (QMessageBox::question(m_view, tr("Continue?"), + tr("The versioning scheme decides which version is considered newer than another.\n" + "This function will guess the versioning scheme under the assumption that the installed version is outdated."), + QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { + + ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + + bool success = false; + + static VersionInfo::VersionScheme schemes[] = { VersionInfo::SCHEME_REGULAR, VersionInfo::SCHEME_DECIMALMARK, VersionInfo::SCHEME_NUMBERSANDLETTERS }; + + for (int i = 0; i < sizeof(schemes) / sizeof(VersionInfo::VersionScheme) && !success; ++i) { + VersionInfo verOld(info->version().canonicalString(), schemes[i]); + VersionInfo verNew(info->newestVersion().canonicalString(), schemes[i]); + if (verOld < verNew) { + info->setVersion(verOld); + info->setNewestVersion(verNew); + success = true; + } + } + if (!success) { + QMessageBox::information(m_view, tr("Sorry"), + tr("I don't know a versioning scheme where %1 is newer than %2.").arg(info->newestVersion().canonicalString()).arg(info->version().canonicalString()), + QMessageBox::Ok); + } + } +} + void ModListViewActions::markConverted(const QModelIndexList& indices) const { for (auto& idx : indices) { - int row_idx = idx.data(ModList::IndexRole).toInt(); - ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + int modIdx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(modIdx); info->markConverted(true); - m_core.modList()->notifyChange(row_idx); + m_core.modList()->notifyChange(modIdx); } } @@ -664,6 +733,159 @@ void ModListViewActions::visitNexusOrWebPage(const QModelIndexList& indices) con } } +void ModListViewActions::reinstallMod(const QModelIndex& index) const +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + QString installationFile = modInfo->installationFile(); + if (installationFile.length() != 0) { + QString fullInstallationFile; + QFileInfo fileInfo(installationFile); + if (fileInfo.isAbsolute()) { + if (fileInfo.exists()) { + fullInstallationFile = installationFile; + } + else { + fullInstallationFile = m_core.downloadManager()->getOutputDirectory() + "/" + fileInfo.fileName(); + } + } + else { + fullInstallationFile = m_core.downloadManager()->getOutputDirectory() + "/" + installationFile; + } + if (QFile::exists(fullInstallationFile)) { + m_core.installMod(fullInstallationFile, true, modInfo, modInfo->name()); + } + else { + QMessageBox::information(m_view, tr("Failed"), tr("Installation file no longer exists")); + } + } + else { + QMessageBox::information(m_view, tr("Failed"), + tr("Mods installed with old versions of MO can't be reinstalled in this way.")); + } +} + +void ModListViewActions::createBackup(const QModelIndex& index) const +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + QString backupDirectory = m_core.installationManager()->generateBackupName(modInfo->absolutePath()); + if (!copyDir(modInfo->absolutePath(), backupDirectory, false)) { + QMessageBox::information(m_view, tr("Failed"), + tr("Failed to create backup.")); + } + m_core.refresh(); + m_view->updateModCount(); +} + +void ModListViewActions::restoreHiddenFiles(const QModelIndexList& indices) const +{ + const int max_items = 20; + + QFlags flags = FileRenamer::UNHIDE; + flags |= FileRenamer::MULTIPLE; + + FileRenamer renamer(m_view, flags); + + FileRenamer::RenameResults result = FileRenamer::RESULT_OK; + + // multi selection + if (indices.size() > 1) { + + QStringList modNames; + for (auto& idx : indices) { + + ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + const auto flags = modInfo->getFlags(); + + if (!modInfo->isRegular() || + std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) == flags.end()) { + continue; + } + + modNames.append(idx.data(Qt::DisplayRole).toString()); + } + + QString mods = "
  • " + modNames.mid(0, max_items).join("
  • ") + "
  • "; + if (modNames.size() > max_items) { + mods += "
  • ...
  • "; + } + + if (QMessageBox::question(m_view, tr("Confirm"), + tr("Restore all hidden files in the following mods?
      %1
    ").arg(mods), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + + for (auto& idx : indices) { + + ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + + const auto flags = modInfo->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { + const QString modDir = modInfo->absolutePath(); + + auto partialResult = restoreHiddenFilesRecursive(renamer, modDir); + + if (partialResult == FileRenamer::RESULT_CANCEL) { + result = FileRenamer::RESULT_CANCEL; + break; + } + emit originModified((m_core.directoryStructure()->getOriginByName( + ToWString(modInfo->internalName()))).getID()); + } + } + } + } + else if (!indices.isEmpty()) { + //single selection + ModInfo::Ptr modInfo = ModInfo::getByIndex(indices[0].data(ModList::IndexRole).toInt()); + const QString modDir = modInfo->absolutePath(); + + if (QMessageBox::question(m_view, tr("Are you sure?"), + tr("About to restore all hidden files in:\n") + modInfo->name(), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { + + result = restoreHiddenFilesRecursive(renamer, modDir); + + emit originModified((m_core.directoryStructure()->getOriginByName( + ToWString(modInfo->internalName()))).getID()); + } + } + + if (result == FileRenamer::RESULT_CANCEL) { + log::debug("Restoring hidden files operation cancelled"); + } + else { + log::debug("Finished restoring hidden files"); + } +} + +void ModListViewActions::setTracked(const QModelIndexList& indices, bool tracked) const +{ + m_core.loggedInAction(m_view, [=] { + for (auto& idx : indices) { + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->track(tracked); + } + }); +} + +void ModListViewActions::setEndorsed(const QModelIndexList& indices, bool endorsed) const +{ + m_core.loggedInAction(m_view, [=] { + if (indices.size() > 1) { + MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), m_view); + } + + for (auto& idx : indices) { + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->endorse(endorsed); + } + }); +} + +void ModListViewActions::willNotEndorsed(const QModelIndexList& indices) const +{ + for (auto& idx : indices) { + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->setNeverEndorse(); + } +} + void ModListViewActions::setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const { auto& settings = m_core.settings(); @@ -696,7 +918,7 @@ void ModListViewActions::setColor(const QModelIndexList& indices, const QModelIn } -void ModListViewActions::resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const +void ModListViewActions::resetColor(const QModelIndexList& indices) const { for (auto& idx : indices) { ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 52db019c..26efde47 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -42,6 +42,7 @@ public: // check all mods for update // void checkModsForUpdates() const; + void checkModsForUpdates(const QModelIndexList& indices) const; // start the "Export Mod List" dialog // @@ -65,16 +66,24 @@ public: void renameMod(const QModelIndex& index) const; void removeMods(const QModelIndexList& indices) const; void ignoreMissingData(const QModelIndexList& indices) const; + void setIgnoreUpdate(const QModelIndexList& indices, bool ignore) const; + void changeVersioningScheme(const QModelIndex& indices) const; void markConverted(const QModelIndexList& indices) const; void visitOnNexus(const QModelIndexList& indices) const; void visitWebPage(const QModelIndexList& indices) const; void visitNexusOrWebPage(const QModelIndexList& indices) const; + void reinstallMod(const QModelIndex& index) const; + void createBackup(const QModelIndex& index) const; + void restoreHiddenFiles(const QModelIndexList& indices) const; + void setTracked(const QModelIndexList& indices, bool tracked) const; + void setEndorsed(const QModelIndexList& indices, bool endorsed) const; + void willNotEndorsed(const QModelIndexList& indices) const; // set/reset color of the given selection, using the given reference index (index // at which the context menu was shown) // void setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; - void resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; + void resetColor(const QModelIndexList& indices) const; // set the category of the mod in the given list, using the given index as reference // - the categories are set as-is on the refernce mod @@ -104,6 +113,10 @@ signals: // void overwriteCleared() const; + // emitted when the origin of a file is modified + // + void originModified(int originId) const; + private: // move the contents of the overwrite to the given path @@ -119,6 +132,10 @@ private: // void setCategoriesIf(ModInfo::Ptr mod, ModInfo::Ptr ref, const std::vector>& categories) const; + // check the given mods from update, the map should map game names to nexus ID + // + void checkModsForUpdates(std::multimap const& IDs) const; + private: OrganizerCore& m_core; -- cgit v1.3.1 From e02ba3b26d30ffb5010394992c69e94b287eacd7 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 12:24:57 +0100 Subject: Some cleaning. Avoid using Qt::UserRole. --- src/modconflicticondelegate.cpp | 7 +++-- src/modflagicondelegate.cpp | 7 +++-- src/modlist.cpp | 64 ++++++++++++++++++++++++++++------------- src/modlist.h | 32 +++++++++++++++++++-- src/modlistcontextmenu.cpp | 15 ++++++---- src/modlistsortproxy.cpp | 13 +-------- src/modlistview.cpp | 24 +++++++++------- src/pluginlist.cpp | 2 +- src/pluginlistview.cpp | 4 +-- src/viewmarkingscrollbar.cpp | 14 ++++----- src/viewmarkingscrollbar.h | 14 ++++----- 11 files changed, 121 insertions(+), 75 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/modconflicticondelegate.cpp b/src/modconflicticondelegate.cpp index 2ccf3363..9680aca3 100644 --- a/src/modconflicticondelegate.cpp +++ b/src/modconflicticondelegate.cpp @@ -1,4 +1,5 @@ #include "modconflicticondelegate.h" +#include "modlist.h" #include #include @@ -96,7 +97,7 @@ QList ModConflictIconDelegate::getIconsForFlags( QList ModConflictIconDelegate::getIcons(const QModelIndex &index) const { - QVariant modid = index.data(Qt::UserRole + 1); + QVariant modid = index.data(ModList::IndexRole); if (modid.isValid()) { ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt()); @@ -127,7 +128,7 @@ QString ModConflictIconDelegate::getFlagIcon(ModInfo::EConflictFlag flag) size_t ModConflictIconDelegate::getNumIcons(const QModelIndex &index) const { - unsigned int modIdx = index.data(Qt::UserRole + 1).toInt(); + unsigned int modIdx = index.data(ModList::IndexRole).toInt(); if (modIdx < ModInfo::getNumMods()) { ModInfo::Ptr info = ModInfo::getByIndex(modIdx); std::vector flags = info->getConflictFlags(); @@ -145,7 +146,7 @@ size_t ModConflictIconDelegate::getNumIcons(const QModelIndex &index) const QSize ModConflictIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const { size_t count = getNumIcons(modelIndex); - unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); + unsigned int index = modelIndex.data(ModList::IndexRole).toInt(); QSize result; if (index < ModInfo::getNumMods()) { result = QSize(static_cast(count) * 40, 20); diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp index bec3c97d..3ac1085e 100644 --- a/src/modflagicondelegate.cpp +++ b/src/modflagicondelegate.cpp @@ -1,4 +1,5 @@ #include "modflagicondelegate.h" +#include "modlist.h" #include #include @@ -39,7 +40,7 @@ QList ModFlagIconDelegate::getIconsForFlags( QList ModFlagIconDelegate::getIcons(const QModelIndex &index) const { - QVariant modid = index.data(Qt::UserRole + 1); + QVariant modid = index.data(ModList::IndexRole); if (modid.isValid()) { ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt()); @@ -71,7 +72,7 @@ QString ModFlagIconDelegate::getFlagIcon(ModInfo::EFlag flag) size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const { - unsigned int modIdx = index.data(Qt::UserRole + 1).toInt(); + unsigned int modIdx = index.data(ModList::IndexRole).toInt(); if (modIdx < ModInfo::getNumMods()) { ModInfo::Ptr info = ModInfo::getByIndex(modIdx); std::vector flags = info->getFlags(); @@ -85,7 +86,7 @@ size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const QSize ModFlagIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const { size_t count = getNumIcons(modelIndex); - unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); + unsigned int index = modelIndex.data(ModList::IndexRole).toInt(); QSize result; if (index < ModInfo::getNumMods()) { result = QSize(static_cast(count) * 40, 20); diff --git a/src/modlist.cpp b/src/modlist.cpp index 077f4af3..26581ba5 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -61,6 +61,8 @@ along with Mod Organizer. If not, see . using namespace MOBase; +const int ModList::ModUserRole = Qt::UserRole + QMetaEnum::fromType().keyCount(); + ModList::ModList(PluginContainer *pluginContainer, OrganizerCore *organizer) : QAbstractItemModel(organizer) , m_Organizer(organizer) @@ -130,7 +132,7 @@ QVariant ModList::getOverwriteData(int column, int role) const } } break; case Qt::TextAlignmentRole: return QVariant(Qt::AlignCenter | Qt::AlignVCenter); - case Qt::UserRole: return -1; + case GroupingRole: return -1; case Qt::ForegroundRole: return QBrush(Qt::red); case Qt::ToolTipRole: return tr("This entry contains files that have been created inside the virtual data tree (i.e. by the construction kit)"); default: return QVariant(); @@ -298,7 +300,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(); } - } else if (role == Qt::TextAlignmentRole) { + } + else if (role == Qt::TextAlignmentRole) { auto flags = modInfo->getFlags(); if (column == COL_NAME) { if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_CENTER) { @@ -313,7 +316,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(Qt::AlignCenter | Qt::AlignVCenter); } - } else if (role == Qt::UserRole) { + } + else if (role == GroupingRole) { if (column == COL_CATEGORY) { QVariantList categoryNames; std::set categories = modInfo->getCategories(); @@ -326,7 +330,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(); } - } else if (column == COL_PRIORITY) { + } + else if (column == COL_PRIORITY) { int priority = modInfo->getFixedPriority(); if (priority != INT_MIN) { return priority; @@ -336,9 +341,11 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return modInfo->nexusId(); } - } else if (role == IndexRole) { + } + else if (role == IndexRole) { return modIndex; - } else if (role == Qt::UserRole + 2) { + } + else if (role == AggrRole) { switch (column) { case COL_MODID: return QtGroupingProxy::AGGR_FIRST; case COL_VERSION: return QtGroupingProxy::AGGR_MAX; @@ -346,11 +353,23 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const case COL_PRIORITY: return QtGroupingProxy::AGGR_MIN; default: return QtGroupingProxy::AGGR_NONE; } - } else if (role == Qt::UserRole + 3) { + } + else if (role == ContentsRole) { return contentsToIcons(modInfo->getContents()); - } else if (role == Qt::UserRole + 4) { + } + else if (role == NameRole) { return modInfo->gameName(); - } else if (role == Qt::FontRole) { + } + else if (role == PriorityRole) { + int priority = modInfo->getFixedPriority(); + if (priority != std::numeric_limits::min()) { + return priority; + } + else { + return m_Profile->getModPriority(modIndex); + } + } + else if (role == Qt::FontRole) { QFont result; auto flags = modInfo->getFlags(); if (column == COL_NAME) { @@ -374,7 +393,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return result; - } else if (role == Qt::DecorationRole) { + } + else if (role == Qt::DecorationRole) { if (column == COL_VERSION) { if (modInfo->updateAvailable()) { return QIcon(":/MO/gui/update_available"); @@ -385,7 +405,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return QVariant(); - } else if (role == Qt::ForegroundRole) { + } + else if (role == Qt::ForegroundRole) { if ((modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) || (column == COL_NOTES)) && modInfo->color().isValid()) { return ColorSettings::idealTextColor(modInfo->color()); } else if (column == COL_NAME) { @@ -404,8 +425,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } return QVariant(); - } else if ((role == Qt::BackgroundRole) - || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { + } + else if (role == Qt::BackgroundRole || role == ScrollMarkRole) { bool overwrite = m_Overwrite.find(modIndex) != m_Overwrite.end(); bool archiveOverwrite = m_ArchiveOverwrite.find(modIndex) != m_ArchiveOverwrite.end(); bool archiveLooseOverwrite = m_ArchiveLooseOverwrite.find(modIndex) != m_ArchiveLooseOverwrite.end(); @@ -424,15 +445,15 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return Settings::instance().colors().modlistOverwritingArchive(); } else if (archiveOverwrite) { return Settings::instance().colors().modlistOverwrittenArchive(); - } else if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) - && modInfo->color().isValid() - && ((role != ViewMarkingScrollBar::DEFAULT_ROLE) - || Settings::instance().colors().colorSeparatorScrollbar())) { + } else if (modInfo->isSeparator() && modInfo->color().isValid() + && (role != ScrollMarkRole + || Settings::instance().colors().colorSeparatorScrollbar())) { return modInfo->color(); } else { return QVariant(); } - } else if (role == Qt::ToolTipRole) { + } + else if (role == Qt::ToolTipRole) { if (column == COL_FLAGS) { QString result; @@ -507,7 +528,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(); } - } else { + } + else { return QVariant(); } } @@ -1366,7 +1388,9 @@ QModelIndex ModList::parent(const QModelIndex&) const QMap ModList::itemData(const QModelIndex &index) const { QMap result = QAbstractItemModel::itemData(index); - result[Qt::UserRole] = data(index, Qt::UserRole); + for (int role = Qt::UserRole; role < ModUserRole; ++role) { + result[role] = data(index, role); + } return result; } diff --git a/src/modlist.h b/src/modlist.h index 4b0e0157..e77ceb1f 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -31,6 +31,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #ifndef Q_MOC_RUN #include @@ -56,9 +57,34 @@ class ModList : public QAbstractItemModel public: - // role of the index of the mod - // - constexpr static int IndexRole = Qt::UserRole + 1; + enum ModListRole { + + // data(GroupingRole) contains the "group" role - This is used by the + // category and Nexus ID grouping proxy (but not the ByPriority proxy) + GroupingRole = Qt::UserRole, + + IndexRole = Qt::UserRole + 1, + + // data(AggrRole) contains aggregation information (for + // grouping I assume?) + AggrRole = Qt::UserRole + 2, + + // data(ContentsRole) contains mod data contents as a QVariantList + // containing icon paths + ContentsRole = Qt::UserRole + 3, + + NameRole = Qt::UserRole + 4, + + PriorityRole = Qt::UserRole + 5, + + // marking role for the scrollbar + ScrollMarkRole = Qt::UserRole + 6 + }; + + Q_ENUM(ModListRole) + + // this is the first available role + static const int ModUserRole; enum EColumn { COL_NAME, diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 303362ba..01c4b471 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -159,23 +159,26 @@ ModListContextMenu::ModListContextMenu( m_selected = { index }; } + ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + QMenu* allMods = new ModListGlobalContextMenu(core, view, view); allMods->setTitle(tr("All Mods")); addMenu(allMods); - if (view->hasCollapsibleSeparators()) { + auto viewIndex = view->indexModelToView(m_index); + if (view->model()->hasChildren(viewIndex)) { + bool expanded = view->isExpanded(viewIndex); addAction(tr("Collapse all"), view, &QTreeView::collapseAll); + addAction(tr("Collapse others"), [=]() { + m_view->collapseAll(); + m_view->setExpanded(viewIndex, expanded); + }); addAction(tr("Expand all"), view, &QTreeView::expandAll); } addSeparator(); // Add type-specific items - ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); - - // TODO: - // - Don't forget to check for the sort priority for "Send To... " - if (info->isOverwrite()) { addOverwriteActions(info); } diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 455daa76..054b980c 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -120,18 +120,7 @@ bool ModListSortProxy::lessThan(const QModelIndex &left, ModInfo::Ptr leftMod = ModInfo::getByIndex(leftIndex); ModInfo::Ptr rightMod = ModInfo::getByIndex(rightIndex); - bool lt = false; - - { - QModelIndex leftPrioIdx = left.sibling(left.row(), ModList::COL_PRIORITY); - QVariant leftPrio = leftPrioIdx.data(); - if (!leftPrio.isValid()) leftPrio = left.data(Qt::UserRole); - QModelIndex rightPrioIdx = right.sibling(right.row(), ModList::COL_PRIORITY); - QVariant rightPrio = rightPrioIdx.data(); - if (!rightPrio.isValid()) rightPrio = right.data(Qt::UserRole); - - lt = leftPrio.toInt() < rightPrio.toInt(); - } + bool lt = left.data(ModList::PriorityRole).toInt() < right.data(ModList::PriorityRole).toInt(); switch (left.column()) { case ModList::COL_FLAGS: { diff --git a/src/modlistview.cpp b/src/modlistview.cpp index f62089b5..80aa6495 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -67,7 +67,7 @@ ModListView::ModListView(QWidget* parent) , m_byPriorityProxy(nullptr) , m_byCategoryProxy(nullptr) , m_byNexusIdProxy(nullptr) - , m_scrollbar(new ViewMarkingScrollBar(this->model(), this)) + , m_scrollbar(new ViewMarkingScrollBar(this->model(), ModList::ScrollMarkRole, this)) { setVerticalScrollBar(m_scrollbar); MOBase::setCustomizableColumns(this); @@ -79,6 +79,13 @@ ModListView::ModListView(QWidget* parent) connect(this, &ModListView::customContextMenuRequested, this, &ModListView::onCustomContextMenuRequested); } +void ModListView::setModel(QAbstractItemModel* model) +{ + QTreeView::setModel(model); + setVerticalScrollBar(new ViewMarkingScrollBar(model, ModList::ScrollMarkRole, this)); +} + + void ModListView::refresh() { updateGroupByProxy(-1); @@ -593,12 +600,13 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo connect(m_byPriorityProxy, &ModListByPriorityProxy::expandItem, this, &ModListView::expandItem); m_byCategoryProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_CATEGORY, - Qt::UserRole, 0, Qt::UserRole + 2); + ModList::GroupingRole, 0, ModList::AggrRole); connect(this, &QTreeView::expanded, m_byCategoryProxy, &QtGroupingProxy::expanded); connect(this, &QTreeView::collapsed, m_byCategoryProxy, &QtGroupingProxy::collapsed); connect(m_byCategoryProxy, &QtGroupingProxy::expandItem, this, &ModListView::expandItem); - m_byNexusIdProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_MODID, Qt::DisplayRole, - QtGroupingProxy::FLAG_NOGROUPNAME | QtGroupingProxy::FLAG_NOSINGLE, Qt::UserRole + 2); + m_byNexusIdProxy = new QtGroupingProxy(core.modList(), QModelIndex(), ModList::COL_MODID, + ModList::GroupingRole, QtGroupingProxy::FLAG_NOGROUPNAME | QtGroupingProxy::FLAG_NOSINGLE, + ModList::AggrRole); connect(this, &QTreeView::expanded, m_byNexusIdProxy, &QtGroupingProxy::expanded); connect(this, &QTreeView::collapsed, m_byNexusIdProxy, &QtGroupingProxy::collapsed); connect(m_byNexusIdProxy, &QtGroupingProxy::expandItem, this, &ModListView::expandItem); @@ -627,7 +635,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo connect(header(), &QHeaderView::sectionResized, [=](int logicalIndex, int oldSize, int newSize) { m_sortProxy->setColumnVisible(logicalIndex, newSize != 0); }); - GenericIconDelegate* contentDelegate = new GenericIconDelegate(this, Qt::UserRole + 3, ModList::COL_CONTENT, 150); + GenericIconDelegate* contentDelegate = new GenericIconDelegate(this, ModList::ContentsRole, ModList::COL_CONTENT, 150); ModFlagIconDelegate* flagDelegate = new ModFlagIconDelegate(this, ModList::COL_FLAGS, 120); ModConflictIconDelegate* conflictFlagDelegate = new ModConflictIconDelegate(this, ModList::COL_CONFLICTFLAGS, 80); @@ -722,12 +730,6 @@ void ModListView::saveState(Settings& s) const m_filters->saveState(s); } -void ModListView::setModel(QAbstractItemModel* model) -{ - QTreeView::setModel(model); - setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); -} - QRect ModListView::visualRect(const QModelIndex& index) const { // this shift the visualRect() from QTreeView to match the new actual diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 4d648a46..9e101f84 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -990,7 +990,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return checkstateData(modelIndex); } else if (role == Qt::ForegroundRole) { return foregroundData(modelIndex); - } else if (role == Qt::BackgroundRole || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { + } else if (role == Qt::BackgroundRole) { return backgroundData(modelIndex); } else if (role == Qt::FontRole) { return fontData(modelIndex); diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index f95226fc..589c5737 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -21,7 +21,7 @@ using namespace MOBase; PluginListView::PluginListView(QWidget *parent) : QTreeView(parent) , m_sortProxy(nullptr) - , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) + , m_Scrollbar(new ViewMarkingScrollBar(this->model(), Qt::BackgroundRole, this)) , m_didUpdateMasterList(false) { setVerticalScrollBar(m_Scrollbar); @@ -31,7 +31,7 @@ PluginListView::PluginListView(QWidget *parent) void PluginListView::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); - setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); + setVerticalScrollBar(new ViewMarkingScrollBar(model, Qt::BackgroundRole, this)); } int PluginListView::sortColumn() const diff --git a/src/viewmarkingscrollbar.cpp b/src/viewmarkingscrollbar.cpp index ed17120b..0991f171 100644 --- a/src/viewmarkingscrollbar.cpp +++ b/src/viewmarkingscrollbar.cpp @@ -4,18 +4,18 @@ #include #include -ViewMarkingScrollBar::ViewMarkingScrollBar(QAbstractItemModel *model, QWidget *parent, int role) +ViewMarkingScrollBar::ViewMarkingScrollBar(QAbstractItemModel* model, int role, QWidget *parent) : QScrollBar(parent) - , m_Model(model) - , m_Role(role) + , m_model(model) + , m_role(role) { // not implemented for horizontal sliders Q_ASSERT(this->orientation() == Qt::Vertical); } -void ViewMarkingScrollBar::paintEvent(QPaintEvent *event) +void ViewMarkingScrollBar::paintEvent(QPaintEvent* event) { - if (m_Model == nullptr) { + if (m_model == nullptr) { return; } QScrollBar::paintEvent(event); @@ -28,13 +28,13 @@ void ViewMarkingScrollBar::paintEvent(QPaintEvent *event) QRect handleRect = style()->subControlRect(QStyle::CC_ScrollBar, &styleOption, QStyle::SC_ScrollBarSlider, this); QRect innerRect = style()->subControlRect(QStyle::CC_ScrollBar, &styleOption, QStyle::SC_ScrollBarGroove, this); - auto indices = flatIndex(m_Model, 0, QModelIndex()); + auto indices = flatIndex(m_model, 0, QModelIndex()); painter.translate(innerRect.topLeft() + QPoint(0, 3)); qreal scale = static_cast(innerRect.height() - 3) / static_cast(indices.size()); for (int i = 0; i < indices.size(); ++i) { - QVariant data = indices[i].data(m_Role); + QVariant data = indices[i].data(m_role); if (data.isValid()) { QColor col = data.value(); painter.setPen(col); diff --git a/src/viewmarkingscrollbar.h b/src/viewmarkingscrollbar.h index 12a297d1..88d77039 100644 --- a/src/viewmarkingscrollbar.h +++ b/src/viewmarkingscrollbar.h @@ -1,21 +1,21 @@ #ifndef VIEWMARKINGSCROLLBAR_H #define VIEWMARKINGSCROLLBAR_H -#include #include +#include class ViewMarkingScrollBar : public QScrollBar { public: - static const int DEFAULT_ROLE = Qt::UserRole + 42; -public: - ViewMarkingScrollBar(QAbstractItemModel *model, QWidget *parent = 0, int role = DEFAULT_ROLE); + ViewMarkingScrollBar(QAbstractItemModel *model, int role, QWidget* parent = nullptr); + protected: - virtual void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event) override; + private: - QAbstractItemModel *m_Model; - int m_Role; + QAbstractItemModel* m_model; + int m_role; }; -- cgit v1.3.1 From 011e4d522ed7b0df52cdeb014608e9881f8bc76b Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 17:17:56 +0100 Subject: Fix setting the primary category of multiple mods at once. --- src/modlistcontextmenu.cpp | 32 +++++++++++++++++++++++++++----- src/modlistcontextmenu.h | 4 ++++ src/modlistviewactions.cpp | 18 +++++++++++++++++- src/modlistviewactions.h | 8 +++++++- 4 files changed, 55 insertions(+), 7 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 01c4b471..19b5fba4 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -126,13 +126,9 @@ void ModListPrimaryCategoryMenu::populate(const CategoryFactory& factory, ModInf QRadioButton* categoryBox = new QRadioButton( factory.getCategoryName(catIdx).replace('&', "&&"), this); - connect(categoryBox, &QRadioButton::toggled, [mod, categoryID](bool enable) { - if (enable) { - mod->setPrimaryCategory(categoryID); - } - }); categoryBox->setChecked(categoryID == mod->primaryCategory()); action->setDefaultWidget(categoryBox); + action->setData(categoryID); } catch (const std::exception& e) { log::error("failed to create category checkbox: {}", e.what()); @@ -143,6 +139,20 @@ void ModListPrimaryCategoryMenu::populate(const CategoryFactory& factory, ModInf } } +int ModListPrimaryCategoryMenu::primaryCategory() const +{ + for (QAction* action : actions()) { + QWidgetAction* widgetAction = qobject_cast(action); + if (widgetAction) { + QRadioButton* button = qobject_cast(widgetAction->defaultWidget()); + if (button && button->isChecked()) { + return widgetAction->data().toInt(); + } + } + } + return -1; +} + ModListContextMenu::ModListContextMenu( const QModelIndex& index, OrganizerCore& core, CategoryFactory& categories, ModListView* view) : QMenu(view) @@ -245,6 +255,12 @@ void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) addMenuAsPushButton(categoriesMenu); ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + connect(primaryCategoryMenu, &QMenu::aboutToHide, [=]() { + int category = primaryCategoryMenu->primaryCategory(); + if (category != -1) { + m_actions.setPrimaryCategory(m_selected, category); + } + }); addMenuAsPushButton(primaryCategoryMenu); addSeparator(); @@ -310,6 +326,12 @@ void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) addMenuAsPushButton(categoriesMenu); ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + connect(primaryCategoryMenu, &QMenu::aboutToHide, [=]() { + int category = primaryCategoryMenu->primaryCategory(); + if (category != -1) { + m_actions.setPrimaryCategory(m_selected, category); + } + }); addMenuAsPushButton(primaryCategoryMenu); addSeparator(); diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index d009c9d8..aa9d4c2b 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -55,6 +55,10 @@ public: ModListPrimaryCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent = nullptr); + // return the selected primary category + // + int primaryCategory() const; + private: // populate the categories diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 8d24a8d5..cb212e0b 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -956,7 +956,6 @@ void ModListViewActions::setCategories(const QModelIndexList& selected, const QM { ModInfo::Ptr refMod = ModInfo::getByIndex(ref.data(ModList::IndexRole).toInt()); if (selected.size() > 1) { - for (auto& idx : selected) { if (idx.row() != ref.row()) { setCategoriesIf( @@ -982,6 +981,23 @@ void ModListViewActions::setCategories(const QModelIndexList& selected, const QM } } +void ModListViewActions::setPrimaryCategory(const QModelIndexList& selected, int category, bool force) +{ + for (auto& idx : selected) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + if (force || info->categorySet(category)) { + info->setCategory(category, true); + info->setPrimaryCategory(category); + } + } + + // reset the selection manually - still needed + auto viewIndices = m_view->indexModelToView(selected); + for (auto& idx : viewIndices) { + m_view->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); + } +} + void ModListViewActions::openExplorer(const QModelIndexList& index) const { for (auto& idx : index) { diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 65f323d9..2850d30a 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -87,7 +87,7 @@ public: void setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; void resetColor(const QModelIndexList& indices) const; - // set the category of the mod in the given list, using the given index as reference + // set the category of the mods in the given list, using the given index as reference // - the categories are set as-is on the refernce mod // - for the other mods, the category is only set if the current state of the category // on the reference is different @@ -95,6 +95,12 @@ public: void setCategories(const QModelIndexList& selected, const QModelIndex& ref, const std::vector>& categories) const; + // set the primary category of the mods in the given list, adding the appropriate + // category to the mods unless force is false, in which case the primary category + // is set only on mods that have this category + // + void setPrimaryCategory(const QModelIndexList& selected, int category, bool force = true); + // open the Windows explorer for the specified mods // void openExplorer(const QModelIndexList& index) const; -- cgit v1.3.1 From 8a789d303b22a7ac0d0a2b179b2500478eb29a46 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 18:00:41 +0100 Subject: Remove duplicated code. --- src/modlistcontextmenu.cpp | 58 ++++++++++++++++++---------------------------- src/modlistcontextmenu.h | 8 +++++-- 2 files changed, 29 insertions(+), 37 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 19b5fba4..e557d7a0 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -221,7 +221,7 @@ void ModListContextMenu::addMenuAsPushButton(QMenu* menu) addAction(action); } -QMenu* ModListContextMenu::createSendToContextMenu() +void ModListContextMenu::addSendToContextMenu() { QMenu* menu = new QMenu(m_view); menu->setTitle(tr("Send to... ")); @@ -229,25 +229,11 @@ QMenu* ModListContextMenu::createSendToContextMenu() menu->addAction(tr("Bottom"), [=]() { m_actions.sendModsToBottom(m_selected); }); menu->addAction(tr("Priority..."), [=]() { m_actions.sendModsToPriority(m_selected); }); menu->addAction(tr("Separator..."), [=]() { m_actions.sendModsToSeparator(m_selected); }); - return menu; + addMenu(menu); } -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_actions.createModFromOverwrite(); }); - addAction(tr("Move content to Mod..."), [=]() { m_actions.moveOverwriteContentToExistingMod(); }); - addAction(tr("Clear Overwrite..."), [=]() { m_actions.clearOverwrite(); }); - } - addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); -} - -void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) +void ModListContextMenu::addCategoryContextMenus(ModInfo::Ptr mod) { - addSeparator(); - - // categories ModListChangeCategoryMenu* categoriesMenu = new ModListChangeCategoryMenu(m_categories, mod, this); connect(categoriesMenu, &QMenu::aboutToHide, [=]() { m_actions.setCategories(m_selected, m_index, categoriesMenu->categories()); @@ -262,6 +248,22 @@ void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) } }); addMenuAsPushButton(primaryCategoryMenu); +} + +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_actions.createModFromOverwrite(); }); + addAction(tr("Move content to Mod..."), [=]() { m_actions.moveOverwriteContentToExistingMod(); }); + addAction(tr("Clear Overwrite..."), [=]() { m_actions.clearOverwrite(); }); + } + addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); +} + +void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) +{ + addCategoryContextMenus(mod); addSeparator(); @@ -270,7 +272,7 @@ void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) addSeparator(); if (m_view->sortColumn() == ModList::COL_PRIORITY) { - addMenu(createSendToContextMenu()); + addSendToContextMenu(); addSeparator(); } addAction(tr("Select Color..."), [=]() { m_actions.setColor(m_selected, m_index); }); @@ -285,7 +287,7 @@ void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) { if (m_view->sortColumn() == ModList::COL_PRIORITY) { - addMenu(createSendToContextMenu()); + addSendToContextMenu(); } } @@ -318,21 +320,7 @@ void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) { auto flags = mod->getFlags(); - // categories - ModListChangeCategoryMenu* categoriesMenu = new ModListChangeCategoryMenu(m_categories, mod, this); - connect(categoriesMenu, &QMenu::aboutToHide, [=]() { - m_actions.setCategories(m_selected, m_index, categoriesMenu->categories()); - }); - addMenuAsPushButton(categoriesMenu); - - ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); - connect(primaryCategoryMenu, &QMenu::aboutToHide, [=]() { - int category = primaryCategoryMenu->primaryCategory(); - if (category != -1) { - m_actions.setPrimaryCategory(m_selected, category); - } - }); - addMenuAsPushButton(primaryCategoryMenu); + addCategoryContextMenus(mod); addSeparator(); if (mod->downgradeAvailable()) { @@ -358,7 +346,7 @@ void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) if (m_view->sortColumn() == ModList::COL_PRIORITY) { - addMenu(createSendToContextMenu()); + addSendToContextMenu(); addSeparator(); } diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index aa9d4c2b..565aac97 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -80,9 +80,13 @@ public: private: - // create the "Send to... " context menu + // adds the "Send to... " context menu // - QMenu* createSendToContextMenu(); + void addSendToContextMenu(); + + // adds the categories menu (change/primary) + // + void addCategoryContextMenus(ModInfo::Ptr mod); // special menu for categories // -- cgit v1.3.1 From c0ac46d5c020bfb9292a812b2c52dc3c6236c7b3 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 20:59:30 +0100 Subject: Fix create empty mod/separator position. --- src/mainwindow.cpp | 2 +- src/modlistcontextmenu.cpp | 15 ++++++++++----- src/modlistcontextmenu.h | 10 ++++++++-- src/modlistviewactions.cpp | 25 ++++++++++--------------- src/modlistviewactions.h | 6 +++--- 5 files changed, 32 insertions(+), 26 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 10de1b84..23050467 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -366,7 +366,7 @@ MainWindow::MainWindow(Settings &settings m_LinkStartMenu = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu())); ui->linkButton->setMenu(linkMenu); - ui->listOptionsBtn->setMenu(new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, ui->listOptionsBtn)); + ui->listOptionsBtn->setMenu(new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, this)); ui->openFolderMenu->setMenu(openFolderMenu()); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index e557d7a0..f20586c4 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -10,11 +10,16 @@ using namespace MOBase; ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, QWidget* parent) + : ModListGlobalContextMenu(core, view, QModelIndex(), parent) +{ +} + +ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, const QModelIndex& index, 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); }); + addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(index); }); + addAction(tr("Create Separator"), [=]() { view->actions().createSeparator(index); }); if (view->hasCollapsibleSeparators()) { addSeparator(); @@ -24,14 +29,14 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV addSeparator(); - addAction(tr("Enable all visible"), [=]() { + addAction(tr("Enable all parent"), [=]() { 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?"), + if (QMessageBox::question(parent, tr("Confirm"), tr("Really disable all visible mods?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { view->disableAllVisible(); } @@ -171,7 +176,7 @@ ModListContextMenu::ModListContextMenu( ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); - QMenu* allMods = new ModListGlobalContextMenu(core, view, view); + QMenu* allMods = new ModListGlobalContextMenu(core, view, m_index, view->topLevelWidget()); allMods->setTitle(tr("All Mods")); addMenu(allMods); diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 565aac97..2b3f9dcd 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -19,8 +19,14 @@ class ModListGlobalContextMenu : public QMenu Q_OBJECT public: - ModListGlobalContextMenu( - OrganizerCore& core, ModListView* modListView, QWidget* parent = nullptr); + ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, QWidget* parent = nullptr); + +protected: + + friend class ModListContextMenu; + + // creates a "All mods" context menu for the given index (can be invalid). + ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, const QModelIndex& index, QWidget* parent = nullptr); }; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index cb212e0b..9957618a 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -75,7 +75,7 @@ void ModListViewActions::installMod(const QString& archivePath) const } } -void ModListViewActions::createEmptyMod(int modIndex) const +void ModListViewActions::createEmptyMod(const QModelIndex& index) const { GuessedValue name; name.setFilter(&fixDirectoryName); @@ -97,8 +97,8 @@ void ModListViewActions::createEmptyMod(int modIndex) const } int newPriority = -1; - if (modIndex >= 0 && m_view->sortColumn() == ModList::COL_PRIORITY) { - newPriority = m_core.currentProfile()->getModPriority(modIndex); + if (index.isValid() && m_view->sortColumn() == ModList::COL_PRIORITY) { + newPriority = m_core.currentProfile()->getModPriority(index.data(ModList::IndexRole).toInt()); } IModInterface* newMod = m_core.createMod(name); @@ -113,12 +113,11 @@ void ModListViewActions::createEmptyMod(int modIndex) const } } -void ModListViewActions::createSeparator(int modIndex) const +void ModListViewActions::createSeparator(const QModelIndex& index) const { GuessedValue name; name.setFilter(&fixDirectoryName); - while (name->isEmpty()) - { + while (name->isEmpty()) { bool ok; name.update(QInputDialog::getText(m_parent, tr("Create Separator..."), tr("This will create a new separator.\n" @@ -126,28 +125,24 @@ void ModListViewActions::createSeparator(int modIndex) const GUESS_USER); if (!ok) { return; } } - if (m_core.modList()->getMod(name) != nullptr) - { + 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) - { + 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 (index.isValid() && m_view->sortColumn() == ModList::COL_PRIORITY) { + newPriority = m_core.currentProfile()->getModPriority(index.data(ModList::IndexRole).toInt()); } if (m_core.createMod(name) == nullptr) { return; } m_core.refresh(); - if (newPriority >= 0) - { + if (newPriority >= 0) { m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); } diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 2850d30a..f1215cec 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -36,10 +36,10 @@ public: 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 + // the end of the list if the index is invalid // - void createEmptyMod(int modIndex) const; - void createSeparator(int modIndex) const; + void createEmptyMod(const QModelIndex& index = QModelIndex()) const; + void createSeparator(const QModelIndex& index = QModelIndex()) const; // check all mods for update // -- cgit v1.3.1 From 3366d9f192ef88cccc2d901c666e15061db20b4e Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sun, 3 Jan 2021 12:51:54 +0100 Subject: Create CopyEventFilter. Add Ctrl+C to the plugin list. --- src/CMakeLists.txt | 1 + src/copyeventfilter.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++ src/copyeventfilter.h | 43 ++++++++++++++++++++++++++++++++++++++ src/modlistcontextmenu.cpp | 2 +- src/modlistview.cpp | 48 +++++++++++++++---------------------------- src/modlistview.h | 1 - src/pluginlistview.cpp | 2 ++ 7 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 src/copyeventfilter.cpp create mode 100644 src/copyeventfilter.h (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 663ebcbc..4b74137e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -236,6 +236,7 @@ add_filter(NAME src/widgets GROUPS texteditor viewmarkingscrollbar modelutils + copyeventfilter ) diff --git a/src/copyeventfilter.cpp b/src/copyeventfilter.cpp new file mode 100644 index 00000000..bbde13e6 --- /dev/null +++ b/src/copyeventfilter.cpp @@ -0,0 +1,51 @@ +#include "copyeventfilter.h" + +#include +#include +#include + +CopyEventFilter::CopyEventFilter(QAbstractItemView* view, int role) : + CopyEventFilter(view, [=](auto& index) { return index.data(role).toString(); }) +{ + +} + +CopyEventFilter::CopyEventFilter( + QAbstractItemView* view, std::function format) : + QObject(view), m_view(view), m_format(format) +{ + +} + +bool CopyEventFilter::copySelection() const +{ + if (!m_view->selectionModel()->hasSelection()) { + return true; + } + + // sort to reflect the visual order + QModelIndexList selectedRows = m_view->selectionModel()->selectedRows(); + std::sort(selectedRows.begin(), selectedRows.end(), [=](const auto& lidx, const auto& ridx) { + return m_view->visualRect(lidx).top() < m_view->visualRect(ridx).top(); + }); + + QStringList rows; + for (auto& idx : selectedRows) { + rows.append(m_format(idx)); + } + + QGuiApplication::clipboard()->setText(rows.join("\n")); + return true; +} + +bool CopyEventFilter::eventFilter(QObject* sender, QEvent* event) +{ + if (sender == m_view && event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + if (keyEvent->modifiers() == Qt::ControlModifier + && keyEvent->key() == Qt::Key_C) { + return copySelection(); + } + } + return QObject::eventFilter(sender, event); +} diff --git a/src/copyeventfilter.h b/src/copyeventfilter.h new file mode 100644 index 00000000..1243630b --- /dev/null +++ b/src/copyeventfilter.h @@ -0,0 +1,43 @@ +#ifndef COPY_EVENT_FILTER_H +#define COPY_EVENT_FILTER_H + +#include + +#include +#include +#include + +// this small class provides copy on Ctrl+C and also +// exposes a method to actual copy the selection +// +// the way the selection is copied can be customized by +// passing a functor to format each index, by default +// it only extracts the display role +// +// only works for view that selects whole row since it only +// considers the first cell in each row +// +class CopyEventFilter : public QObject +{ + Q_OBJECT + +public: + + CopyEventFilter(QAbstractItemView* view, int role = Qt::DisplayRole); + CopyEventFilter(QAbstractItemView* view, std::function format); + + // copy the selection of the view associated with this + // event filter into the clipboard + // + bool copySelection() const; + + bool eventFilter(QObject* sender, QEvent* event) override; + +private: + + QAbstractItemView* m_view; + std::function m_format; + +}; + +#endif diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index f20586c4..c1ee4f74 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -29,7 +29,7 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV addSeparator(); - addAction(tr("Enable all parent"), [=]() { + 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(); diff --git a/src/modlistview.cpp b/src/modlistview.cpp index bc6b5a65..29c68165 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -22,6 +22,7 @@ #include "modlistdropinfo.h" #include "modlistcontextmenu.h" #include "genericicondelegate.h" +#include "copyeventfilter.h" #include "shared/fileentry.h" #include "shared/directoryentry.h" #include "shared/filesorigin.h" @@ -135,6 +136,21 @@ ModListView::ModListView(QWidget* parent) connect(this, &ModListView::doubleClicked, this, &ModListView::onDoubleClicked); connect(this, &ModListView::customContextMenuRequested, this, &ModListView::onCustomContextMenuRequested); + + installEventFilter(new CopyEventFilter(this, [=](auto& index) { + QVariant mIndex = index.data(ModList::IndexRole); + QString name = index.data(Qt::DisplayRole).toString(); + if (mIndex.isValid() && hasCollapsibleSeparators()) { + ModInfo::Ptr info = ModInfo::getByIndex(mIndex.toInt()); + if (info->isSeparator()) { + name = "[" + name + "]"; + } + } + else if (model()->hasChildren(index)) { + name = "[" + name + "]"; + } + return name; + })); } void ModListView::refresh() @@ -615,35 +631,6 @@ bool ModListView::toggleSelectionState() return m_core->modList()->toggleState(indexViewToModel(selectionModel()->selectedRows())); } -bool ModListView::copySelection() -{ - if (!selectionModel()->hasSelection()) { - return true; - } - - // sort to reflect the visual order - QModelIndexList selectedRows = selectionModel()->selectedRows(); - std::sort(selectedRows.begin(), selectedRows.end(), [=](const auto& lidx, const auto& ridx) { - return visualRect(lidx).top() < visualRect(ridx).top(); - }); - - QStringList rows; - for (auto& idx : selectedRows) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); - QString name = idx.data(Qt::DisplayRole).toString(); - if (model()->hasChildren(idx) || ( - sortColumn() == ModList::COL_PRIORITY - && (groupByMode() == GroupByMode::NONE || groupByMode() == GroupByMode::SEPARATOR) - && info->isSeparator())) { - name = "[" + name + "]"; - } - rows.append(name); - } - - QGuiApplication::clipboard()->setText(rows.join("\n")); - return true; -} - void ModListView::updateGroupByProxy(int groupIndex) { // if the index is -1, we do not refresh unless we are grouping @@ -1225,9 +1212,6 @@ bool ModListView::event(QEvent* event) && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) { return moveSelection(keyEvent->key()); } - else if (keyEvent->key() == Qt::Key_C) { - return copySelection(); - } } else if (keyEvent->modifiers() == Qt::ShiftModifier) { // shift+enter expand diff --git a/src/modlistview.h b/src/modlistview.h index 2aa48140..9712deab 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -149,7 +149,6 @@ protected: bool moveSelection(int key); bool removeSelection(); bool toggleSelectionState(); - bool copySelection(); // re-implemented to fix indentation with collapsible separators // diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index 7388ae40..e27d7e66 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -12,6 +12,7 @@ #include "pluginlistsortproxy.h" #include "pluginlistcontextmenu.h" #include "modlistview.h" +#include "copyeventfilter.h" #include "modlistviewactions.h" #include "genericicondelegate.h" #include "modelutils.h" @@ -26,6 +27,7 @@ PluginListView::PluginListView(QWidget *parent) { setVerticalScrollBar(m_Scrollbar); MOBase::setCustomizableColumns(this); + installEventFilter(new CopyEventFilter(this)); } int PluginListView::sortColumn() const -- cgit v1.3.1 From bec0974c95284cdf469dc21db614be32bb7b1531 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 6 Jan 2021 17:55:11 +0100 Subject: Prevent creating separator or empty mod 'on' backups. --- src/modlistcontextmenu.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index c1ee4f74..a4649a9e 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -18,8 +18,15 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV : QMenu(parent) { addAction(tr("Install Mod..."), [=]() { view->actions().installMod(); }); - addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(index); }); - addAction(tr("Create Separator"), [=]() { view->actions().createSeparator(index); }); + + auto modIndex = index.data(ModList::IndexRole); + if (modIndex.isValid()) { + auto info = ModInfo::getByIndex(modIndex.toInt()); + if (!info->isBackup()) { + addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(index); }); + addAction(tr("Create Separator"), [=]() { view->actions().createSeparator(index); }); + } + } if (view->hasCollapsibleSeparators()) { addSeparator(); -- cgit v1.3.1 From 4a0ce804ea486744e5f6140a0ce4538d99e21ce3 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sun, 10 Jan 2021 10:20:46 +0100 Subject: Menu separator before collapse entries in separator context menu. --- src/modlistcontextmenu.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/modlistcontextmenu.cpp') diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index a4649a9e..f5307dc7 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -190,6 +190,7 @@ ModListContextMenu::ModListContextMenu( auto viewIndex = view->indexModelToView(m_index); if (view->model()->hasChildren(viewIndex)) { bool expanded = view->isExpanded(viewIndex); + addSeparator(); addAction(tr("Collapse all"), view, &QTreeView::collapseAll); addAction(tr("Collapse others"), [=]() { m_view->collapseAll(); -- cgit v1.3.1