diff options
| author | Mikaƫl Capelle <capelle.mikael@gmail.com> | 2021-01-10 10:25:37 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-10 10:25:37 +0100 |
| commit | 80e44a9e3ade61695b4807a3a900d2866138ecac (patch) | |
| tree | 1ef9904664d8d34dba6692bbf5325aea8c199d08 /src/modlistcontextmenu.cpp | |
| parent | eaec140f7c823012c09536175d8917dddcacdb7c (diff) | |
| parent | 4a0ce804ea486744e5f6140a0ce4538d99e21ce3 (diff) | |
Merge pull request #1338 from Holt59/collapsible-separators
Collapsible separators (and refactoring).
Diffstat (limited to 'src/modlistcontextmenu.cpp')
| -rw-r--r-- | src/modlistcontextmenu.cpp | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp new file mode 100644 index 00000000..f5307dc7 --- /dev/null +++ b/src/modlistcontextmenu.cpp @@ -0,0 +1,443 @@ +#include "modlistcontextmenu.h" + +#include <report.h> + +#include "modlist.h" +#include "modlistview.h" +#include "modlistviewactions.h" +#include "organizercore.h" + +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(); }); + + 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(); + 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(parent, 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(); }); +} + + +ModListChangeCategoryMenu::ModListChangeCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent) + : QMenu(tr("Change Categories"), parent) +{ + populate(this, categories, mod); +} + +std::vector<std::pair<int, bool>> ModListChangeCategoryMenu::categories() const +{ + return categories(this); +} + +std::vector<std::pair<int, bool>> ModListChangeCategoryMenu::categories(const QMenu* menu) const +{ + std::vector<std::pair<int, bool>> 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<QWidgetAction*>(action); + if (widgetAction != nullptr) { + QCheckBox* checkbox = qobject_cast<QCheckBox*>(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<int>& 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<QCheckBox> 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<QWidgetAction> 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<int>& 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); + 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()); + } + + action->setData(categoryID); + addAction(action); + } +} + +int ModListPrimaryCategoryMenu::primaryCategory() const +{ + for (QAction* action : actions()) { + QWidgetAction* widgetAction = qobject_cast<QWidgetAction*>(action); + if (widgetAction) { + QRadioButton* button = qobject_cast<QRadioButton*>(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) + , m_core(core) + , m_categories(categories) + , m_index(index.model() == view->model() ? view->indexViewToModel(index) : index) + , m_view(view) + , m_actions(view->actions()) +{ + if (view->selectionModel()->hasSelection()) { + m_selected = view->indexViewToModel(view->selectionModel()->selectedRows()); + } + else { + m_selected = { index }; + } + + ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + + QMenu* allMods = new ModListGlobalContextMenu(core, view, m_index, view->topLevelWidget()); + allMods->setTitle(tr("All Mods")); + addMenu(allMods); + + 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(); + m_view->setExpanded(viewIndex, expanded); + }); + addAction(tr("Expand all"), view, &QTreeView::expandAll); + } + + addSeparator(); + + // Add type-specific items + if (info->isOverwrite()) { + addOverwriteActions(info); + } + else if (info->isBackup()) { + addBackupActions(info); + } + else if (info->isSeparator()) { + addSeparatorActions(info); + } + else if (info->isForeign()) { + addForeignActions(info); + } + else { + addRegularActions(info); + } + + // add information for all except foreign + if (!info->isForeign()) { + QAction* infoAction = addAction(tr("Information..."), [=]() { view->actions().displayModInformation(m_index.data(ModList::IndexRole).toInt()); }); + setDefaultAction(infoAction); + } +} + +void ModListContextMenu::addMenuAsPushButton(QMenu* menu) +{ + QPushButton* pushBtn = new QPushButton(menu->title()); + pushBtn->setMenu(menu); + QWidgetAction* action = new QWidgetAction(this); + action->setDefaultWidget(pushBtn); + addAction(action); +} + +void ModListContextMenu::addSendToContextMenu() +{ + QMenu* menu = new QMenu(m_view); + menu->setTitle(tr("Send to... ")); + 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); }); + addMenu(menu); +} + +void ModListContextMenu::addCategoryContextMenus(ModInfo::Ptr mod) +{ + 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); +} + +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(); + + + 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) { + addSendToContextMenu(); + 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); }); + } + + addSeparator(); +} + +void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) +{ + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addSendToContextMenu(); + } +} + +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) +{ + auto flags = mod->getFlags(); + + addCategoryContextMenus(mod); + 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) { + addSendToContextMenu(); + 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); }); +} |
