diff options
Diffstat (limited to 'src/pluginlistview.cpp')
| -rw-r--r-- | src/pluginlistview.cpp | 344 |
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); } |
