summaryrefslogtreecommitdiff
path: root/src/pluginlistview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluginlistview.cpp')
-rw-r--r--src/pluginlistview.cpp344
1 files changed, 312 insertions, 32 deletions
diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp
index a265d5d4..e27d7e66 100644
--- a/src/pluginlistview.cpp
+++ b/src/pluginlistview.cpp
@@ -1,58 +1,338 @@
#include "pluginlistview.h"
-#include <widgetutility.h>
+
#include <QUrl>
#include <QMimeData>
-#include <QProxyStyle>
+#include <report.h>
+#include <widgetutility.h>
+
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+#include "organizercore.h"
+#include "pluginlistsortproxy.h"
+#include "pluginlistcontextmenu.h"
+#include "modlistview.h"
+#include "copyeventfilter.h"
+#include "modlistviewactions.h"
+#include "genericicondelegate.h"
+#include "modelutils.h"
-class PluginListViewStyle : public QProxyStyle {
-public:
- PluginListViewStyle(QStyle *style, int indentation);
+using namespace MOBase;
+
+PluginListView::PluginListView(QWidget *parent)
+ : QTreeView(parent)
+ , m_sortProxy(nullptr)
+ , m_Scrollbar(new ViewMarkingScrollBar(this, Qt::BackgroundRole))
+ , m_didUpdateMasterList(false)
+{
+ setVerticalScrollBar(m_Scrollbar);
+ MOBase::setCustomizableColumns(this);
+ installEventFilter(new CopyEventFilter(this));
+}
- void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
- QPainter *painter, const QWidget *widget = 0) const;
-private:
- int m_Indentation;
-};
+int PluginListView::sortColumn() const
+{
+ return m_sortProxy ? m_sortProxy->sortColumn() : -1;
+}
-PluginListViewStyle::PluginListViewStyle(QStyle *style, int indentation)
- : QProxyStyle(style), m_Indentation(indentation)
+QModelIndex PluginListView::indexModelToView(const QModelIndex& index) const
{
+ return MOShared::indexModelToView(index, this);
}
-void PluginListViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
- QPainter *painter, const QWidget *widget) const
+QModelIndexList PluginListView::indexModelToView(const QModelIndexList& index) const
{
- if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
- QStyleOption opt(*option);
- opt.rect.setLeft(m_Indentation);
- if (widget) {
- opt.rect.setRight(widget->width() - 5); // 5 is an arbitrary value that seems to work ok
+ return MOShared::indexModelToView(index, this);
+}
+
+QModelIndex PluginListView::indexViewToModel(const QModelIndex& index) const
+{
+ return MOShared::indexViewToModel(index, m_core->pluginList());
+}
+
+QModelIndexList PluginListView::indexViewToModel(const QModelIndexList& index) const
+{
+ return MOShared::indexViewToModel(index, m_core->pluginList());
+}
+
+void PluginListView::updatePluginCount()
+{
+ int activeMasterCount = 0;
+ int activeLightMasterCount = 0;
+ int activeRegularCount = 0;
+ int masterCount = 0;
+ int lightMasterCount = 0;
+ int regularCount = 0;
+ int activeVisibleCount = 0;
+
+ PluginList* list = m_core->pluginList();
+ QString filter = ui.filter->text();
+
+ for (QString plugin : list->pluginNames()) {
+ bool active = list->isEnabled(plugin);
+ bool visible = m_sortProxy->filterMatchesPlugin(plugin);
+ if (list->isLight(plugin) || list->isLightFlagged(plugin)) {
+ lightMasterCount++;
+ activeLightMasterCount += active;
+ activeVisibleCount += visible && active;
+ }
+ else if (list->isMaster(plugin)) {
+ masterCount++;
+ activeMasterCount += active;
+ activeVisibleCount += visible && active;
+ }
+ else {
+ regularCount++;
+ activeRegularCount += active;
+ activeVisibleCount += visible && active;
}
- QProxyStyle::drawPrimitive(element, &opt, painter, widget);
+ }
+
+ int activeCount = activeMasterCount + activeLightMasterCount + activeRegularCount;
+ int totalCount = masterCount + lightMasterCount + regularCount;
+
+ ui.counter->display(activeVisibleCount);
+ ui.counter->setToolTip(tr("<table cellspacing=\"6\">"
+ "<tr><th>Type</th><th>Active </th><th>Total</th></tr>"
+ "<tr><td>All plugins:</td><td align=right>%1 </td><td align=right>%2</td></tr>"
+ "<tr><td>ESMs:</td><td align=right>%3 </td><td align=right>%4</td></tr>"
+ "<tr><td>ESPs:</td><td align=right>%7 </td><td align=right>%8</td></tr>"
+ "<tr><td>ESMs+ESPs:</td><td align=right>%9 </td><td align=right>%10</td></tr>"
+ "<tr><td>ESLs:</td><td align=right>%5 </td><td align=right>%6</td></tr>"
+ "</table>")
+ .arg(activeCount).arg(totalCount)
+ .arg(activeMasterCount).arg(masterCount)
+ .arg(activeLightMasterCount).arg(lightMasterCount)
+ .arg(activeRegularCount).arg(regularCount)
+ .arg(activeMasterCount + activeRegularCount).arg(masterCount + regularCount)
+ );
+}
+
+void PluginListView::onFilterChanged(const QString& filter)
+{
+ if (!filter.isEmpty()) {
+ setStyleSheet("QTreeView { border: 2px ridge #f00; }");
+ ui.counter->setStyleSheet("QLCDNumber { border: 2px ridge #f00; }");
}
else {
- QProxyStyle::drawPrimitive(element, option, painter, widget);
+ setStyleSheet("");
+ ui.counter->setStyleSheet("");
}
+ updatePluginCount();
}
-PluginListView::PluginListView(QWidget *parent)
- : QTreeView(parent)
- , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this))
+void PluginListView::onSortButtonClicked()
{
- setVerticalScrollBar(m_Scrollbar);
- MOBase::setCustomizableColumns(this);
+ const bool offline = m_core->settings().network().offlineMode();
+
+ auto r = QMessageBox::No;
+
+ if (offline) {
+ r = QMessageBox::question(
+ topLevelWidget(), tr("Sorting plugins"),
+ tr("Are you sure you want to sort your plugins list?") + "\r\n\r\n" +
+ tr("Note: You are currently in offline mode and LOOT will not update the master list."),
+ QMessageBox::Yes | QMessageBox::No);
+ }
+ else {
+ r = QMessageBox::question(
+ topLevelWidget(), tr("Sorting plugins"),
+ tr("Are you sure you want to sort your plugins list?"),
+ QMessageBox::Yes | QMessageBox::No);
+ }
+
+ if (r != QMessageBox::Yes) {
+ return;
+ }
+
+ m_core->savePluginList();
+
+ topLevelWidget()->setEnabled(false);
+ Guard g([=]() { topLevelWidget()->setEnabled(true); });
+
+ // don't try to update the master list in offline mode
+ const bool didUpdateMasterList = offline ? true : m_didUpdateMasterList;
+
+ if (runLoot(topLevelWidget(), *m_core, didUpdateMasterList)) {
+ // don't assume the master list was updated in offline mode
+ if (!offline) {
+ m_didUpdateMasterList = true;
+ }
+
+ m_core->refreshESPList(false);
+ m_core->savePluginList();
+ }
+}
+
+std::pair<QModelIndex, QModelIndexList> PluginListView::selected() const
+{
+ return { indexViewToModel(currentIndex()), indexViewToModel(selectionModel()->selectedRows()) };
}
-void PluginListView::dragEnterEvent(QDragEnterEvent *event)
+void PluginListView::setSelected(const QModelIndex& current, const QModelIndexList& selected)
{
- emit dropModeUpdate(event->mimeData()->hasUrls());
+ setCurrentIndex(indexModelToView(current));
+ for (auto idx : selected) {
+ selectionModel()->select(indexModelToView(idx), QItemSelectionModel::Select | QItemSelectionModel::Rows);
+ }
+}
+
+
+void PluginListView::setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* mwui)
+{
+ m_core = &core;
+ ui = { mwui->activePluginsCounter, mwui->espFilterEdit };
+ m_modActions = &mwui->modList->actions();
+
+ m_sortProxy = new PluginListSortProxy(&core);
+ m_sortProxy->setSourceModel(core.pluginList());
+ setModel(m_sortProxy);
+
+ sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder);
+ setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(this));
- QTreeView::dragEnterEvent(event);
+ // counter
+ connect(core.pluginList(), &PluginList::writePluginsList, [=]{ updatePluginCount(); });
+ connect(core.pluginList(), &PluginList::esplist_changed, [=]{ updatePluginCount(); });
+
+ // sort
+ connect(mwui->bossButton, &QPushButton::clicked, [=]{ onSortButtonClicked(); });
+
+ // filter
+ connect(ui.filter, &QLineEdit::textChanged, m_sortProxy, &PluginListSortProxy::updateFilter);
+ connect(ui.filter, &QLineEdit::textChanged, this, &PluginListView::onFilterChanged);
+
+ // highligth mod list when selected
+ connect(selectionModel(), &QItemSelectionModel::selectionChanged, [=](auto&& selected) {
+ std::vector<unsigned int> pluginIndices;
+ for (auto& idx : indexViewToModel(selectionModel()->selectedRows())) {
+ pluginIndices.push_back(idx.row());
+ }
+ mwui->modList->setHighlightedMods(pluginIndices);
+ });
+
+ // using a lambda here to avoid storing the mod list actions
+ connect(this, &QTreeView::customContextMenuRequested, [=](auto&& pos) { onCustomContextMenuRequested(pos); });
+ connect(this, &QTreeView::doubleClicked, [=](auto&& index) { onDoubleClicked(index); });
}
-void PluginListView::setModel(QAbstractItemModel *model)
+void PluginListView::onCustomContextMenuRequested(const QPoint& pos)
{
- QTreeView::setModel(model);
- setVerticalScrollBar(new ViewMarkingScrollBar(model, this));
+ try {
+ PluginListContextMenu menu(indexViewToModel(indexAt(pos)), *m_core, this);
+ connect(&menu, &PluginListContextMenu::openModInformation, [=](auto&& modIndex) {
+ m_modActions->displayModInformation(modIndex); });
+ menu.exec(viewport()->mapToGlobal(pos));
+ }
+ catch (const std::exception& e) {
+ reportError(tr("Exception: ").arg(e.what()));
+ }
+ catch (...) {
+ reportError(tr("Unknown exception"));
+ }
+}
+
+void PluginListView::onDoubleClicked(const QModelIndex& index)
+{
+ if (!index.isValid()) {
+ return;
+ }
+
+ if (m_core->pluginList()->timeElapsedSinceLastChecked() <= QApplication::doubleClickInterval()) {
+ // don't interpret double click if we only just checked a plugin
+ return;
+ }
+
+ try {
+ if (selectionModel()->hasSelection() && selectionModel()->selectedRows().count() == 1) {
+
+ QModelIndex idx = selectionModel()->currentIndex();
+ QString fileName = idx.data().toString();
+
+ if (ModInfo::getIndex(m_core->pluginList()->origin(fileName)) == UINT_MAX) {
+ return;
+ }
+
+ auto modIndex = ModInfo::getIndex(m_core->pluginList()->origin(fileName));
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
+
+ if (modInfo->isRegular() || modInfo->isOverwrite()) {
+
+ Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
+ if (modifiers.testFlag(Qt::ControlModifier)) {
+ m_modActions->openExplorer({ m_core->modList()->index(modIndex, 0) });
+ }
+ else {
+ m_modActions->displayModInformation(ModInfo::getIndex(m_core->pluginList()->origin(fileName)));
+ }
+
+ // workaround to cancel the editor that might have opened because of
+ // selection-click
+ closePersistentEditor(index);
+ }
+ }
+ }
+ catch (const std::exception& e) {
+ reportError(e.what());
+ }
+}
+
+bool PluginListView::moveSelection(int key)
+{
+ auto [cindex, sourceRows] = selected();
+
+ int offset = key == Qt::Key_Up ? -1 : 1;
+ if (m_sortProxy->sortOrder() == Qt::DescendingOrder) {
+ offset = -offset;
+ }
+
+ m_core->pluginList()->shiftPluginsPriority(sourceRows, offset);
+
+ // reset the selection and the index
+ setSelected(cindex, sourceRows);
+
+ return true;
+}
+
+bool PluginListView::toggleSelectionState()
+{
+ if (!selectionModel()->hasSelection()) {
+ return true;
+ }
+ m_core->pluginList()->toggleState(indexViewToModel(selectionModel()->selectedRows()));
+ return true;
+}
+
+bool PluginListView::event(QEvent* event)
+{
+ auto* profile = m_core->currentProfile();
+ if (event->type() == QEvent::KeyPress && profile) {
+ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+
+ if (keyEvent->modifiers() == Qt::ControlModifier
+ && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)) {
+ if (selectionModel()->hasSelection() && selectionModel()->selectedRows().count() == 1) {
+ QModelIndex idx = selectionModel()->currentIndex();
+ QString fileName = idx.data().toString();
+
+ if (ModInfo::getIndex(m_core->pluginList()->origin(fileName)) == UINT_MAX) {
+ return false;
+ }
+
+ auto modIndex = ModInfo::getIndex(m_core->pluginList()->origin(fileName));
+ m_modActions->openExplorer({ m_core->modList()->index(modIndex, 0) });
+ return true;
+ }
+ }
+ else if (keyEvent->modifiers() == Qt::ControlModifier
+ && (sortColumn() == PluginList::COL_PRIORITY || sortColumn() == PluginList::COL_MODINDEX)
+ && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) {
+ return moveSelection(keyEvent->key());
+ }
+ else if (keyEvent->key() == Qt::Key_Space) {
+ return toggleSelectionState();
+ }
+ return QTreeView::event(event);
+ }
+ return QTreeView::event(event);
}