pcsx2/pcsx2-qt/Settings/GameListSettingsWidget.cpp

282 lines
9.5 KiB
C++

// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include <QtCore/QAbstractTableModel>
#include <QtCore/QDebug>
#include <QtCore/QSettings>
#include <QtCore/QUrl>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMessageBox>
#include <algorithm>
#include "GameListSettingsWidget.h"
#include "MainWindow.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "SettingWidgetBinder.h"
GameListSettingsWidget::GameListSettingsWidget(SettingsWindow* dialog, QWidget* parent)
: QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preferEnglishGameList, "UI", "PreferEnglishGameList", false);
connect(m_ui.preferEnglishGameList, &QCheckBox::checkStateChanged, this, [this]{ emit preferEnglishGameListChanged(); });
dialog->registerWidgetHelp(m_ui.preferEnglishGameList, tr("Prefer English Titles"), tr("Unchecked"),
tr("For games with both a title in the game's native language and one in English, prefer the English title."));
m_ui.searchDirectoryList->setSelectionMode(QAbstractItemView::SingleSelection);
m_ui.searchDirectoryList->setSelectionBehavior(QAbstractItemView::SelectRows);
m_ui.searchDirectoryList->setAlternatingRowColors(true);
m_ui.searchDirectoryList->setShowGrid(false);
m_ui.searchDirectoryList->horizontalHeader()->setHighlightSections(false);
m_ui.searchDirectoryList->verticalHeader()->hide();
m_ui.searchDirectoryList->setCurrentIndex({});
m_ui.searchDirectoryList->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
connect(m_ui.searchDirectoryList, &QTableWidget::customContextMenuRequested, this, &GameListSettingsWidget::onDirectoryListContextMenuRequested);
connect(m_ui.searchDirectoryList, &QTableWidget::itemSelectionChanged, this, &GameListSettingsWidget::onDirectoryListSelectionChanged);
connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this, &GameListSettingsWidget::onAddSearchDirectoryButtonClicked);
connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this, &GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked);
connect(m_ui.addExcludedFile, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedFileButtonClicked);
connect(m_ui.addExcludedPath, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedPathButtonClicked);
connect(m_ui.removeExcludedPath, &QPushButton::clicked, this, &GameListSettingsWidget::onRemoveExcludedPathButtonClicked);
connect(m_ui.excludedPaths, &QListWidget::itemSelectionChanged, this, &GameListSettingsWidget::onExcludedPathsSelectionChanged);
connect(m_ui.rescanAllGames, &QPushButton::clicked, this, &GameListSettingsWidget::onRescanAllGamesClicked);
connect(m_ui.scanForNewGames, &QPushButton::clicked, this, &GameListSettingsWidget::onScanForNewGamesClicked);
refreshDirectoryList();
refreshExclusionList();
}
GameListSettingsWidget::~GameListSettingsWidget() = default;
bool GameListSettingsWidget::addExcludedPath(const std::string& path)
{
if (!Host::AddBaseValueToStringList("GameList", "ExcludedPaths", path.c_str()))
return false;
Host::CommitBaseSettingChanges();
m_ui.excludedPaths->addItem(QString::fromStdString(path));
g_main_window->refreshGameList(false);
return true;
}
void GameListSettingsWidget::refreshExclusionList()
{
m_ui.excludedPaths->clear();
const std::vector<std::string> paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths"));
for (const std::string& path : paths)
m_ui.excludedPaths->addItem(QString::fromStdString(path));
m_ui.removeExcludedPath->setEnabled(false);
}
bool GameListSettingsWidget::event(QEvent* event)
{
bool res = QWidget::event(event);
switch (event->type())
{
case QEvent::LayoutRequest:
case QEvent::Resize:
QtUtils::ResizeColumnsForTableView(m_ui.searchDirectoryList, {-1, 100});
break;
default:
break;
}
return res;
}
void GameListSettingsWidget::addPathToTable(const std::string& path, bool recursive)
{
const int row = m_ui.searchDirectoryList->rowCount();
m_ui.searchDirectoryList->insertRow(row);
QTableWidgetItem* item = new QTableWidgetItem();
item->setText(QString::fromStdString(path));
item->setFlags(item->flags() & ~(Qt::ItemIsEditable));
m_ui.searchDirectoryList->setItem(row, 0, item);
QCheckBox* cb = new QCheckBox(m_ui.searchDirectoryList);
m_ui.searchDirectoryList->setCellWidget(row, 1, cb);
cb->setChecked(recursive);
connect(cb, &QCheckBox::checkStateChanged, this, [item](Qt::CheckState state) {
const std::string path(item->text().toStdString());
if (state == Qt::Checked)
{
Host::RemoveBaseValueFromStringList("GameList", "Paths", path.c_str());
Host::AddBaseValueToStringList("GameList", "RecursivePaths", path.c_str());
}
else
{
Host::RemoveBaseValueFromStringList("GameList", "RecursivePaths", path.c_str());
Host::AddBaseValueToStringList("GameList", "Paths", path.c_str());
}
Host::CommitBaseSettingChanges();
});
}
void GameListSettingsWidget::refreshDirectoryList()
{
QSignalBlocker sb(m_ui.searchDirectoryList);
while (m_ui.searchDirectoryList->rowCount() > 0)
m_ui.searchDirectoryList->removeRow(0);
std::vector<std::string> path_list = Host::GetBaseStringListSetting("GameList", "Paths");
for (const std::string& entry : path_list)
addPathToTable(entry, false);
path_list = Host::GetBaseStringListSetting("GameList", "RecursivePaths");
for (const std::string& entry : path_list)
addPathToTable(entry, true);
m_ui.searchDirectoryList->sortByColumn(0, Qt::AscendingOrder);
m_ui.removeSearchDirectoryButton->setEnabled(false);
}
void GameListSettingsWidget::addSearchDirectory(const QString& path, bool recursive)
{
const std::string spath(path.toStdString());
Host::RemoveBaseValueFromStringList("GameList", recursive ? "Paths" : "RecursivePaths", spath.c_str());
Host::AddBaseValueToStringList("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str());
Host::CommitBaseSettingChanges();
refreshDirectoryList();
g_main_window->refreshGameList(false);
}
void GameListSettingsWidget::removeSearchDirectory(const QString& path)
{
const std::string spath(path.toStdString());
if (!Host::RemoveBaseValueFromStringList("GameList", "Paths", spath.c_str()) &&
!Host::RemoveBaseValueFromStringList("GameList", "RecursivePaths", spath.c_str()))
{
return;
}
Host::CommitBaseSettingChanges();
refreshDirectoryList();
g_main_window->refreshGameList(false);
}
void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& point)
{
QModelIndexList selection = m_ui.searchDirectoryList->selectionModel()->selectedIndexes();
if (selection.size() < 1)
return;
const int row = selection[0].row();
QMenu menu;
menu.addAction(tr("Remove"), [this]() { onRemoveSearchDirectoryButtonClicked(); });
menu.addSeparator();
menu.addAction(tr("Open Directory..."), [this, row]() {
QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectoryList->item(row, 0)->text()));
});
menu.exec(m_ui.searchDirectoryList->mapToGlobal(point));
}
void GameListSettingsWidget::onDirectoryListSelectionChanged()
{
m_ui.removeSearchDirectoryButton->setEnabled(m_ui.searchDirectoryList->selectionModel()->hasSelection());
}
void GameListSettingsWidget::addSearchDirectory(QWidget* parent_widget)
{
QString dir =
QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent_widget, tr("Select Search Directory")));
if (dir.isEmpty())
return;
QMessageBox::StandardButton selection =
QMessageBox::question(this, tr("Scan Recursively?"),
tr("Would you like to scan the directory \"%1\" recursively?\n\nScanning recursively takes "
"more time, but will identify files in subdirectories.")
.arg(dir),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (selection == QMessageBox::Cancel)
return;
const bool recursive = (selection == QMessageBox::Yes);
addSearchDirectory(dir, recursive);
}
void GameListSettingsWidget::onAddSearchDirectoryButtonClicked()
{
addSearchDirectory(this);
}
void GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked()
{
const int row = m_ui.searchDirectoryList->currentRow();
QTableWidgetItem* item = (row >= 0) ? m_ui.searchDirectoryList->takeItem(row, 0) : nullptr;
if (!item)
return;
removeSearchDirectory(item->text());
delete item;
}
void GameListSettingsWidget::onAddExcludedFileButtonClicked()
{
QString path =
QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select File")));
if (path.isEmpty())
return;
addExcludedPath(path.toStdString());
}
void GameListSettingsWidget::onAddExcludedPathButtonClicked()
{
QString path =
QDir::toNativeSeparators(QFileDialog::getExistingDirectory(QtUtils::GetRootWidget(this), tr("Select Directory")));
if (path.isEmpty())
return;
addExcludedPath(path.toStdString());
}
void GameListSettingsWidget::onRemoveExcludedPathButtonClicked()
{
const int row = m_ui.excludedPaths->currentRow();
QListWidgetItem* item = (row >= 0) ? m_ui.excludedPaths->takeItem(row) : 0;
if (!item)
return;
if (Host::RemoveBaseValueFromStringList("GameList", "ExcludedPaths", item->text().toUtf8().constData()))
Host::CommitBaseSettingChanges();
delete item;
g_main_window->refreshGameList(false);
}
void GameListSettingsWidget::onExcludedPathsSelectionChanged()
{
m_ui.removeExcludedPath->setEnabled(!m_ui.excludedPaths->selectedItems().isEmpty());
}
void GameListSettingsWidget::onRescanAllGamesClicked()
{
g_main_window->refreshGameList(true);
}
void GameListSettingsWidget::onScanForNewGamesClicked()
{
g_main_window->refreshGameList(false);
}