Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
12 kB · 371 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372#include "contentselector.hpp"
#include "ui_contentselector.h"
#include <components/contentselector/model/esmfile.hpp>
#include <QClipboard>#include <QMenu>#include <QModelIndex>#include <QProgressDialog>#include <QSortFilterProxyModel>
ContentSelectorView::ContentSelector::ContentSelector(QWidget* parent, bool showOMWScripts) : QObject(parent) , ui(std::make_unique<Ui::ContentSelector>()){ ui->setupUi(parent); ui->addonView->setDragDropMode(QAbstractItemView::InternalMove);
if (!showOMWScripts) { ui->languageComboBox->setHidden(true); ui->refreshButton->setHidden(true); }
buildContentModel(showOMWScripts); buildGameFileView(); buildAddonView();}
ContentSelectorView::ContentSelector::~ContentSelector() = default;
void ContentSelectorView::ContentSelector::buildContentModel(bool showOMWScripts){ QIcon warningIcon(ui->addonView->style()->standardIcon(QStyle::SP_MessageBoxWarning)); QIcon errorIcon(ui->addonView->style()->standardIcon(QStyle::SP_MessageBoxCritical)); mContentModel = new ContentSelectorModel::ContentModel(this, warningIcon, errorIcon, showOMWScripts);}
void ContentSelectorView::ContentSelector::buildGameFileView(){ ui->gameFileView->addItem(tr("<No game file>")); ui->gameFileView->setVisible(true);
connect(ui->gameFileView, qOverload<int>(&ComboBox::currentIndexChanged), this, &ContentSelector::slotCurrentGameFileIndexChanged);
ui->gameFileView->setCurrentIndex(0);}
class AddOnProxyModel : public QSortFilterProxyModel{public: explicit AddOnProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) { }
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override { static const QString contentTypeAddon = QString::number(static_cast<int>(ContentSelectorModel::ContentType_Addon));
QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent); const QString userRole = sourceModel()->data(nameIndex, Qt::UserRole).toString();
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent) && userRole == contentTypeAddon; }};
bool ContentSelectorView::ContentSelector::isGamefileSelected() const{ return ui->gameFileView->currentIndex() > 0;}
QWidget* ContentSelectorView::ContentSelector::uiWidget() const{ return ui->contentGroupBox;}
QComboBox* ContentSelectorView::ContentSelector::languageBox() const{ return ui->languageComboBox;}
QToolButton* ContentSelectorView::ContentSelector::refreshButton() const{ return ui->refreshButton;}
QLineEdit* ContentSelectorView::ContentSelector::searchFilter() const{ return ui->searchFilter;}
void ContentSelectorView::ContentSelector::buildAddonView(){ ui->addonView->setVisible(true);
mAddonProxyModel = new AddOnProxyModel(this); mAddonProxyModel->setFilterRegularExpression(searchFilter()->text()); mAddonProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); mAddonProxyModel->setDynamicSortFilter(true); mAddonProxyModel->setSourceModel(mContentModel);
connect(ui->searchFilter, &QLineEdit::textEdited, mAddonProxyModel, &QSortFilterProxyModel::setFilterWildcard); connect(ui->searchFilter, &QLineEdit::textEdited, this, &ContentSelector::slotSearchFilterTextChanged);
ui->addonView->setModel(mAddonProxyModel);
connect(ui->addonView, &QTableView::activated, this, &ContentSelector::slotAddonTableItemActivated); connect(mContentModel, &ContentSelectorModel::ContentModel::dataChanged, this, &ContentSelector::signalAddonDataChanged); connect(mContentModel, &ContentSelectorModel::ContentModel::dataChanged, this, &ContentSelector::slotRowsMoved); buildContextMenu();}
void ContentSelectorView::ContentSelector::buildContextMenu(){ ui->addonView->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->addonView, &QTableView::customContextMenuRequested, this, &ContentSelector::slotShowContextMenu);
mContextMenu = new QMenu(ui->addonView); mContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems())); mContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems())); mContextMenu->addAction(tr("&Copy Path(s) to Clipboard"), this, SLOT(slotCopySelectedItemsPaths()));}
void ContentSelectorView::ContentSelector::setNonUserContent(const QStringList& fileList){ mContentModel->setNonUserContent(fileList);}
void ContentSelectorView::ContentSelector::setProfileContent(const QStringList& enabledFiles, const QStringList& order){ clearCheckStates();
for (const QString& filepath : enabledFiles) { const ContentSelectorModel::EsmFile* file = mContentModel->item(filepath); if (file && file->isGameFile()) { setGameFile(filepath); break; } }
setContentList(enabledFiles);
if (order.isEmpty()) return;
// files added since the order was saved go after the ones the user placed const QStringList current = userFilesInOrder(); QStringList complete; complete.reserve(current.size()); for (const QString& file : order) if (current.contains(file, Qt::CaseInsensitive) && !complete.contains(file, Qt::CaseInsensitive)) complete.append(file); for (const QString& file : current) if (!complete.contains(file, Qt::CaseInsensitive)) complete.append(file); setContentList(complete, true);}
void ContentSelectorView::ContentSelector::setGameFile(const QString& filename){ int index = 0;
if (!filename.isEmpty()) { const ContentSelectorModel::EsmFile* file = mContentModel->item(filename); index = ui->gameFileView->findText(file->fileName());
// verify that the current index is also checked in the model if (!mContentModel->isChecked(file) && !mContentModel->setCheckState(file, true)) { // throw error in case file not found? return; } }
ui->gameFileView->setCurrentIndex(index);}
void ContentSelectorView::ContentSelector::clearCheckStates(){ mContentModel->uncheckAll();}
void ContentSelectorView::ContentSelector::setEncoding(const QString& encoding){ mContentModel->setEncoding(encoding);}
QStringList ContentSelectorView::ContentSelector::userFilesInOrder() const{ return mContentModel->userFilesInOrder();}
void ContentSelectorView::ContentSelector::setContentList(const QStringList& list, bool orderOnly){ if (list.isEmpty() && !orderOnly) { slotCurrentGameFileIndexChanged(ui->gameFileView->currentIndex()); } else mContentModel->setContentList(list, orderOnly);}
ContentSelectorModel::ContentFileList ContentSelectorView::ContentSelector::selectedFiles() const{ if (!mContentModel) return ContentSelectorModel::ContentFileList();
return mContentModel->checkedItems();}
void ContentSelectorView::ContentSelector::addFiles(const QString& path, bool newfiles){ mContentModel->addFiles(path, newfiles);
// add any game files to the combo box for (const QString& gameFileName : mContentModel->gameFiles()) { if (ui->gameFileView->findText(gameFileName) == -1) { ui->gameFileView->addItem(gameFileName); } }
if (ui->gameFileView->currentIndex() != 0) ui->gameFileView->setCurrentIndex(0);
mContentModel->uncheckAll();}
void ContentSelectorView::ContentSelector::sortFiles(){ mContentModel->sortFiles();}
bool ContentSelectorView::ContentSelector::containsDataFiles(const QString& path){ return mContentModel->containsDataFiles(path);}
void ContentSelectorView::ContentSelector::clearFiles(){ mContentModel->clearFiles();}
QString ContentSelectorView::ContentSelector::currentFile() const{ QModelIndex currentIdx = ui->addonView->currentIndex();
if (!currentIdx.isValid() && ui->gameFileView->currentIndex() > 0) return ui->gameFileView->currentText();
QModelIndex idx = mContentModel->index(mAddonProxyModel->mapToSource(currentIdx).row(), 0, QModelIndex()); return mContentModel->data(idx, Qt::DisplayRole).toString();}
void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index){ static int oldIndex = -1;
if (index != oldIndex) { if (oldIndex > -1) { setGameFileSelected(oldIndex, false); }
oldIndex = index;
setGameFileSelected(index, true); }
emit signalCurrentGamefileIndexChanged(index);}
void ContentSelectorView::ContentSelector::setGameFileSelected(int index, bool selected){ QString fileName = ui->gameFileView->itemText(index); const ContentSelectorModel::EsmFile* file = mContentModel->item(fileName); if (file != nullptr) { QModelIndex index2(mContentModel->indexFromItem(file)); mContentModel->setData(index2, selected, Qt::UserRole + 1); } mContentModel->setCurrentGameFile(selected ? file : nullptr);}
void ContentSelectorView::ContentSelector::slotAddonTableItemActivated(const QModelIndex& index){ // toggles check state when an AddOn file is double clicked or activated by keyboard QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index);
if (!mContentModel->isEnabled(sourceIndex)) return;
Qt::CheckState checkState = Qt::Unchecked;
if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() == Qt::Unchecked) checkState = Qt::Checked;
mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole);}
void ContentSelectorView::ContentSelector::slotShowContextMenu(const QPoint& pos){ QPoint globalPos = ui->addonView->viewport()->mapToGlobal(pos); mContextMenu->exec(globalPos);}
void ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(Qt::CheckState checkState){ const QModelIndexList selectedIndexes = ui->addonView->selectionModel()->selectedIndexes();
QProgressDialog progressDialog("Updating content selection", {}, 0, static_cast<int>(selectedIndexes.size())); progressDialog.setWindowModality(Qt::WindowModal); progressDialog.setValue(0);
for (qsizetype i = 0, n = selectedIndexes.size(); i < n; ++i) { const QModelIndex sourceIndex = mAddonProxyModel->mapToSource(selectedIndexes[i]);
if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() != checkState) mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole);
progressDialog.setValue(static_cast<int>(i + 1)); }}
void ContentSelectorView::ContentSelector::slotUncheckMultiSelectedItems(){ setCheckStateForMultiSelectedItems(Qt::Unchecked);}
void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems(){ setCheckStateForMultiSelectedItems(Qt::Checked);}
void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths(){ QClipboard* clipboard = QApplication::clipboard(); QStringList filepaths; for (const QModelIndex& index : ui->addonView->selectionModel()->selectedIndexes()) { int row = mAddonProxyModel->mapToSource(index).row(); const ContentSelectorModel::EsmFile* file = mContentModel->item(row); filepaths.push_back(file->filePath()); }
if (!filepaths.isEmpty()) { clipboard->setText(filepaths.join("\n")); }}
void ContentSelectorView::ContentSelector::slotSearchFilterTextChanged(const QString& newText){ ui->addonView->setDragEnabled(newText.isEmpty());}
void ContentSelectorView::ContentSelector::slotRowsMoved(){ ui->addonView->selectionModel()->clearSelection();}