aboutsummaryrefslogtreecommitdiff
path: root/libs/installer_bsplugins/src/BSPluginList/PluginListContextMenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/installer_bsplugins/src/BSPluginList/PluginListContextMenu.cpp')
-rw-r--r--libs/installer_bsplugins/src/BSPluginList/PluginListContextMenu.cpp349
1 files changed, 349 insertions, 0 deletions
diff --git a/libs/installer_bsplugins/src/BSPluginList/PluginListContextMenu.cpp b/libs/installer_bsplugins/src/BSPluginList/PluginListContextMenu.cpp
new file mode 100644
index 0000000..9fc53ac
--- /dev/null
+++ b/libs/installer_bsplugins/src/BSPluginList/PluginListContextMenu.cpp
@@ -0,0 +1,349 @@
+#include "PluginListContextMenu.h"
+#include "GUI/ListDialog.h"
+#include "MOPlugin/Settings.h"
+#include "PluginListModel.h"
+#include "PluginListView.h"
+#include "TESData/FileInfo.h"
+
+#include <utility.h>
+
+#include <QInputDialog>
+#include <QMessageBox>
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+namespace BSPluginList
+{
+
+PluginListContextMenu::PluginListContextMenu(const QModelIndex& index,
+ PluginListModel* model,
+ PluginListView* view,
+ MOBase::IModList* modList,
+ MOBase::IPluginList* pluginList)
+ : QMenu(view), m_Index{index}, m_Model{model}, m_View{view}
+{
+ m_ViewSelected = view->selectionModel()->selectedRows();
+ if (!m_ViewSelected.isEmpty()) {
+ m_ModelSelected = view->indexViewToModel(m_ViewSelected, model);
+ } else if (index.isValid()) {
+ m_ModelSelected = {index};
+ }
+
+ m_FilesSelected =
+ !m_ViewSelected.isEmpty() && std::ranges::all_of(m_ViewSelected, [](const QModelIndex& idx) {
+ return !idx.model()->hasChildren(idx);
+ });
+
+ m_GroupsSelected =
+ !m_ViewSelected.isEmpty() && std::ranges::all_of(m_ViewSelected, [](const QModelIndex& idx) {
+ return idx.model()->hasChildren(idx);
+ });
+
+ m_FilesTogglable =
+ m_FilesSelected && std::ranges::all_of(m_ViewSelected, [](const QModelIndex& idx) {
+ const auto plugin =
+ idx.data(PluginListModel::InfoRole).value<const TESData::FileInfo*>();
+ return plugin && plugin->canBeToggled();
+ });
+
+ m_FilesESM =
+ m_FilesSelected && std::ranges::all_of(m_ViewSelected, [](const QModelIndex& idx) {
+ const auto plugin =
+ idx.data(PluginListModel::InfoRole).value<const TESData::FileInfo*>();
+ return plugin && !plugin->forceLoaded() && plugin->isMasterFile();
+ });
+
+ m_FilesESP =
+ m_FilesSelected && std::ranges::all_of(m_ViewSelected, [](const QModelIndex& idx) {
+ const auto plugin =
+ idx.data(PluginListModel::InfoRole).value<const TESData::FileInfo*>();
+ return plugin && !plugin->forceLoaded() && !plugin->isMasterFile();
+ });
+
+ m_FilesMovable = m_FilesESM | m_FilesESP;
+
+ addAllItemsMenu();
+ addSelectedFilesActions();
+ addSelectedGroupActions();
+ addSelectionActions();
+ addOriginActions(modList, pluginList);
+}
+
+void PluginListContextMenu::addAllItemsMenu()
+{
+ QMenu* const allItemsMenu = addMenu(tr("All Items"));
+
+ allItemsMenu->addAction(tr("Collapse all"), [this]() {
+ m_View->collapseAll();
+ m_View->scrollToTop();
+ });
+ allItemsMenu->addAction(tr("Expand all"), [this]() {
+ m_View->expandAll();
+ m_View->scrollToTop();
+ });
+
+ allItemsMenu->addSeparator();
+
+ allItemsMenu->addAction(tr("Enable all"), [this]() {
+ if (QMessageBox::question(m_View->topLevelWidget(), tr("Confirm"),
+ tr("Really enable all plugins?"),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ m_Model->setEnabledAll(true);
+ }
+ });
+ allItemsMenu->addAction(tr("Disable all"), [this]() {
+ if (QMessageBox::question(m_View->topLevelWidget(), tr("Confirm"),
+ tr("Really disable all plugins?"),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ m_Model->setEnabledAll(false);
+ }
+ });
+}
+
+void PluginListContextMenu::addSelectedFilesActions()
+{
+ if (!m_FilesTogglable)
+ return;
+
+ addSeparator();
+
+ addAction(tr("Enable selected"), [this]() {
+ m_Model->setEnabled(m_ModelSelected, true);
+ });
+ addAction(tr("Disable selected"), [this]() {
+ m_Model->setEnabled(m_ModelSelected, false);
+ });
+}
+
+void PluginListContextMenu::addSelectedGroupActions()
+{
+ if (!m_GroupsSelected || m_ViewSelected.length() != 1)
+ return;
+
+ addSeparator();
+
+ const auto selectedIndex = m_ViewSelected.first();
+ const bool expanded = m_View->isExpanded(selectedIndex);
+ addAction(tr("Collapse others"), [=, this]() {
+ m_View->collapseAll();
+ m_View->setExpanded(selectedIndex, expanded);
+ m_View->scrollTo(selectedIndex);
+ });
+}
+
+void PluginListContextMenu::addSelectionActions()
+{
+ if (m_ModelSelected.isEmpty())
+ return;
+
+ addSeparator();
+
+ addSendToMenu();
+
+ if (m_FilesSelected) {
+ addAction(tr("Create Group..."), [this]() {
+ bool ok;
+ const QString group =
+ QInputDialog::getText(m_View->topLevelWidget(), tr("Create Group..."),
+ tr("Please enter a name:"), QLineEdit::Normal, "", &ok);
+
+ if (!ok || group.isEmpty())
+ return;
+
+ QList<QPersistentModelIndex> persistent;
+ persistent.reserve(m_ViewSelected.length());
+ std::ranges::transform(m_ViewSelected, std::back_inserter(persistent),
+ [](const QModelIndex& idx) {
+ return QPersistentModelIndex(idx);
+ });
+
+ const int priority = m_ModelSelected.first()
+ .siblingAtColumn(PluginListModel::COL_PRIORITY)
+ .data()
+ .toInt();
+ m_Model->sendToPriority(m_ModelSelected, priority);
+ m_Model->setGroup(m_ModelSelected, group);
+ for (const auto& index : persistent) {
+ m_View->setExpanded(index.parent(), true);
+ }
+ });
+ } else if (m_GroupsSelected) {
+ if (m_ViewSelected.length() == 1) {
+ addAction(tr("Rename Group..."), [this]() {
+ const auto& selected = m_ViewSelected.first();
+ QModelIndexList indices;
+ for (int i = 0, count = selected.model()->rowCount(selected); i < count; ++i) {
+ const auto child = selected.model()->index(i, 0, selected);
+ auto&& index = m_View->indexViewToModel(child, m_Model);
+ indices.append(std::move(index));
+ }
+
+ bool ok;
+ const QString group = QInputDialog::getText(
+ m_View->topLevelWidget(), tr("Rename Group..."), tr("Please enter a name:"),
+ QLineEdit::Normal, "", &ok);
+
+ if (!ok || group.isEmpty())
+ return;
+
+ const auto persistentIndex =
+ QPersistentModelIndex(selected.model()->index(0, 0, selected));
+ const bool expanded = m_View->isExpanded(selected);
+
+ m_Model->setGroup(indices, group);
+
+ const auto groupIndex = persistentIndex.parent();
+ const auto groupRight =
+ groupIndex.siblingAtColumn(selected.model()->columnCount() - 1);
+ m_View->setExpanded(groupIndex, expanded);
+ m_View->selectionModel()->select(QItemSelection(groupIndex, groupRight),
+ QItemSelectionModel::ClearAndSelect);
+ m_View->selectionModel()->setCurrentIndex(groupIndex,
+ QItemSelectionModel::Current);
+ });
+ }
+
+ addAction(tr("Remove Group..."), [this]() {
+ QModelIndexList indices;
+ for (const auto& selected : m_ViewSelected) {
+ for (int i = 0, count = selected.model()->rowCount(selected); i < count; ++i) {
+ const auto child = selected.model()->index(i, 0, selected);
+ auto&& index = m_View->indexViewToModel(child, m_Model);
+ indices.append(std::move(index));
+ }
+ }
+
+ if (QMessageBox::question(m_View->topLevelWidget(), tr("Confirm"),
+ tr("Are you sure you want to remove \"%1\"?")
+ .arg(m_ViewSelected.first().data().toString()),
+ QMessageBox::Yes | QMessageBox::No) ==
+ QMessageBox::Yes) {
+ m_Model->setGroup(indices, QString());
+ }
+ });
+ }
+}
+
+void PluginListContextMenu::addSendToMenu()
+{
+ if (!m_FilesMovable) {
+ return;
+ }
+
+ QMenu* const sendToMenu = addMenu(tr("Send to... "));
+ sendToMenu->addAction(tr("Top"), [this]() {
+ const auto selectedIndex = m_ViewSelected.first();
+ const auto persistentIndex = QPersistentModelIndex(selectedIndex);
+ m_Model->sendToPriority(m_ModelSelected, 0, true);
+ m_View->scrollTo(persistentIndex);
+ });
+ sendToMenu->addAction(tr("Bottom"), [this]() {
+ const auto selectedIndex = m_ViewSelected.first();
+ const auto persistentIndex = QPersistentModelIndex(selectedIndex);
+ m_Model->sendToPriority(m_ModelSelected, std::numeric_limits<int>::max(), true);
+ m_View->scrollTo(persistentIndex);
+ });
+ sendToMenu->addAction(tr("Priority..."), [this]() {
+ const auto selectedIndex = m_ViewSelected.first();
+
+ bool ok;
+ const int newPriority =
+ QInputDialog::getInt(m_View->topLevelWidget(), tr("Set Priority"),
+ tr("Set the priority of the selected plugins"), 0, 0,
+ std::numeric_limits<int>::max(), 1, &ok);
+ if (!ok)
+ return;
+
+ const auto persistentIndex = QPersistentModelIndex(selectedIndex);
+ m_Model->sendToPriority(m_ModelSelected, newPriority);
+ m_View->scrollTo(persistentIndex);
+ });
+ sendToMenu->addAction(tr("Group..."), [this]() {
+ sendSelectedToGroup();
+ });
+}
+
+static MOBase::IModInterface* getModInfo(const QModelIndex& index,
+ MOBase::IModList* modList,
+ MOBase::IPluginList* pluginList)
+{
+ const auto info =
+ index.data(PluginListModel::InfoRole).value<const TESData::FileInfo*>();
+
+ if (!info) {
+ return nullptr;
+ }
+
+ const QString fileName = info->name();
+ return modList->getMod(pluginList->origin(fileName));
+}
+
+static void openOriginExplorer(const QModelIndexList& indices,
+ MOBase::IModList* modList,
+ MOBase::IPluginList* pluginList)
+{
+ for (const auto& idx : indices) {
+ if (const auto modInfo = getModInfo(idx, modList, pluginList)) {
+ MOBase::shell::Explore(modInfo->absolutePath());
+ }
+ }
+}
+
+void PluginListContextMenu::addOriginActions(MOBase::IModList* modList,
+ MOBase::IPluginList* pluginList)
+{
+ if (!m_Index.isValid())
+ return;
+
+ addSeparator();
+
+ const auto selectedIdx =
+ m_ModelSelected.length() == 1 ? m_ModelSelected.first() : m_Index;
+ const auto nameIdx = selectedIdx.siblingAtColumn(PluginListModel::COL_NAME);
+
+ if (std::ranges::any_of(m_ModelSelected, [=](auto&& idx) {
+ return getModInfo(idx, modList, pluginList) != nullptr;
+ })) {
+ addAction(tr("Open Origin in Explorer"), [=, this]() {
+ openOriginExplorer(m_ModelSelected, modList, pluginList);
+ });
+
+ const auto modInfo = getModInfo(nameIdx, modList, pluginList);
+ if (modInfo && !modInfo->isForeign()) {
+ addAction(tr("Open Origin Info..."), [this, nameIdx] {
+ emit openModInformation(nameIdx);
+ });
+ }
+ }
+
+ const auto pluginInfoAction = addAction("Open Plugin Info...", [this, nameIdx] {
+ emit openPluginInformation(nameIdx);
+ });
+ setDefaultAction(pluginInfoAction);
+}
+
+void PluginListContextMenu::sendSelectedToGroup()
+{
+ GUI::ListDialog dialog{*Settings::instance(), m_View->topLevelWidget()};
+ dialog.setWindowTitle(tr("Select a group..."));
+ if (m_FilesESM) {
+ dialog.setChoices(m_Model->masterGroups());
+ } else if (m_FilesESP) {
+ dialog.setChoices(m_Model->regularGroups());
+ }
+
+ if (dialog.exec() != QDialog::Accepted) {
+ return;
+ }
+
+ const QString result = dialog.getChoice();
+ if (result.isEmpty()) {
+ return;
+ }
+
+ m_Model->sendToGroup(m_ModelSelected, result, m_FilesESM);
+}
+
+} // namespace BSPluginList