shadPS4/old_stuff/shadPS4/gui/GameListViewer.cpp

182 lines
6.6 KiB
C++

#include "GameListViewer.h"
#include <QFileInfo>
#include <QHeaderView>
#include <QMenu>
#include <QThreadPool>
#include <QVBoxLayout>
#include <QDirIterator>
#include <QLineEdit>
#include <QRegularExpression>
#include "../emulator/fileFormat/PSF.h"
GameListViewer::GameListViewer(QWidget* parent)
: QWidget(parent)
{
QVBoxLayout* layout = new QVBoxLayout;
QHBoxLayout* search_layout = new QHBoxLayout;
proxyModel = new QSortFilterProxyModel;
search_games = new QLineEdit;
tree_view = new QTreeView;
item_model = new QStandardItemModel(tree_view);
proxyModel->setSourceModel(item_model);
tree_view->setModel(proxyModel);
tree_view->setAlternatingRowColors(true);
tree_view->setSelectionMode(QHeaderView::SingleSelection);
tree_view->setSelectionBehavior(QHeaderView::SelectRows);
tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
tree_view->setSortingEnabled(true);
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
tree_view->setUniformRowHeights(true);
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
item_model->insertColumns(0, 7);
item_model->setHeaderData(0, Qt::Horizontal, "Icon");
item_model->setHeaderData(1, Qt::Horizontal, "Name");
item_model->setHeaderData(2, Qt::Horizontal, "Serial");
item_model->setHeaderData(3, Qt::Horizontal, "FW");
item_model->setHeaderData(4, Qt::Horizontal, "App Version");
item_model->setHeaderData(5, Qt::Horizontal, "Category");
item_model->setHeaderData(6, Qt::Horizontal, "Path");
connect(tree_view, &QTreeView::activated, this, &GameListViewer::ValidateEntry);
connect(search_games, &QLineEdit::textChanged, this, &GameListViewer::searchGame);
connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GameListViewer::RefreshGameDirectory);
// We must register all custom types with the Qt Automoc system so that we are able to use it
// with signals/slots. In this case, QList falls under the umbrells of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
layout->setContentsMargins(0, 0, 0, 0);
QSpacerItem* item = new QSpacerItem(100, 1, QSizePolicy::Expanding, QSizePolicy::Fixed);
search_layout->setContentsMargins(0, 5, 0, 0);
search_layout->addSpacerItem(item);
search_layout->addWidget(search_games);
layout->addLayout(search_layout);
layout->addWidget(tree_view);
setLayout(layout);
}
GameListViewer::~GameListViewer()
{
emit ShouldCancelWorker();
}
void GameListViewer::searchGame(QString searchText)
{
proxyModel->setFilterKeyColumn(1); //filter Name column only
QString strPattern = searchText;
QRegularExpression regExp(strPattern, QRegularExpression::CaseInsensitiveOption);
proxyModel->setFilterRegularExpression(regExp);
}
void GameListViewer::AddEntry(const QList<QStandardItem*>& entry_items) {
item_model->invisibleRootItem()->appendRow(entry_items);
}
void GameListViewer::ValidateEntry(const QModelIndex& item) {
// We don't care about the individual QStandardItem that was selected, but its row.
}
void GameListViewer::DonePopulating() {
tree_view->setEnabled(true);
tree_view->resizeColumnToContents(1);//resize tittle to fit the column
}
void GameListViewer::PopulateAsync() {
QDir game_folder(game_path);
if (!game_folder.exists())
{
//game directory doesn't exist
return;
}
tree_view->setEnabled(false);
// Delete any rows that might already exist if we're repopulating
item_model->removeRows(0, item_model->rowCount());
emit ShouldCancelWorker();
auto watch_dirs = watcher.directories();
if (!watch_dirs.isEmpty()) {
watcher.removePaths(watch_dirs);
}
UpdateWatcherList(game_path.toStdString());
GameListWorker* worker = new GameListWorker(game_path);
connect(worker, &GameListWorker::EntryReady, this, &GameListViewer::AddEntry, Qt::QueuedConnection);
connect(worker, &GameListWorker::Finished, this, &GameListViewer::DonePopulating, Qt::QueuedConnection);
// Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel without delay.
connect(this, &GameListViewer::ShouldCancelWorker, worker, &GameListWorker::Cancel, Qt::DirectConnection);
QThreadPool::globalInstance()->start(worker);
current_worker = std::move(worker);
}
void GameListViewer::RefreshGameDirectory() {
QDir game_folder(game_path);
bool empty = game_folder.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::DirsFirst).count() == 0;
if (!empty && current_worker != nullptr) {
//Change detected in the games directory. Reloading game list
PopulateAsync();
}
}
/**
* Adds the game list folder to the QFileSystemWatcher to check for updates.
*
* The file watcher will fire off an update to the game list when a change is detected in the game
* list folder.
*
* Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and
* this function is fast enough to not stall the UI thread. If performance is an issue, it should
* be moved to another thread and properly locked to prevent concurrency issues.
*
* @param dir folder to check for changes in
*/
void GameListViewer::UpdateWatcherList(const std::string& dir) {
/*watcher.addPath(QString::fromStdString(dir));
QDir parent_folder(QString::fromStdString(dir));
QFileInfoList fList = parent_folder.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::DirsFirst);
foreach(QFileInfo item, fList)
{
UpdateWatcherList(item.absoluteFilePath().toStdString());
}*/
}
void GameListWorker::AddEntriesToGameList(const std::string& dir_path) {
QDir parent_folder(QString::fromStdString(dir_path));
QFileInfoList fList = parent_folder.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::DirsFirst);
foreach(QFileInfo item, fList)
{
PSF psf;
if (!psf.open(item.absoluteFilePath().toStdString() + "/sce_sys/PARAM.SFO"))
continue;//if we can't open param.sfo go to the next entry
//TODO std::string test = psf.get_string("TITLE_ID");
QString iconpath(item.absoluteFilePath() + "/sce_sys/ICON0.PNG");
QString t;
emit EntryReady({
new GameIconItem(iconpath),
new GameListItem(QString::fromStdString(psf.get_string("TITLE"))),
new GameListItem(QString::fromStdString(psf.get_string("TITLE_ID"))),
new GameListItem((QString("%1").arg(psf.get_integer("SYSTEM_VER"), 8, 16, QLatin1Char('0'))).mid(1,3).insert(1,'.')),
new GameListItem(QString::fromStdString(psf.get_string("APP_VER"))),
new GameListItem(QString::fromStdString(psf.get_string("CATEGORY"))),
new GameListItem(item.fileName())
});
}
}
void GameListWorker::run() {
stop_processing = false;
AddEntriesToGameList(dir_path.toStdString());
emit Finished();
}
void GameListWorker::Cancel() {
this->disconnect();
stop_processing = true;
}