2019-01-16 20:00:10 +00:00
|
|
|
#include "bootablelistdialog.h"
|
|
|
|
#include "ui_bootablelistdialog.h"
|
|
|
|
|
|
|
|
#include <QAction>
|
|
|
|
#include <QFileDialog>
|
|
|
|
#include <QGridLayout>
|
2020-03-07 17:58:03 +00:00
|
|
|
#include <QInputDialog>
|
2019-01-16 20:00:10 +00:00
|
|
|
#include <QLabel>
|
2020-03-09 01:10:10 +00:00
|
|
|
#include <QMessageBox>
|
2019-01-16 20:00:10 +00:00
|
|
|
#include <QPixmap>
|
|
|
|
#include <QPixmapCache>
|
|
|
|
#include <iostream>
|
2019-01-19 23:09:01 +00:00
|
|
|
#include <thread>
|
2019-01-16 20:00:10 +00:00
|
|
|
|
|
|
|
#include "AppConfig.h"
|
2019-01-17 21:52:23 +00:00
|
|
|
#include "CoverUtils.h"
|
2019-01-16 20:00:10 +00:00
|
|
|
#include "http/HttpClientFactory.h"
|
2020-03-07 17:58:03 +00:00
|
|
|
#include "string_format.h"
|
2019-01-16 20:00:10 +00:00
|
|
|
#include "QStringUtils.h"
|
|
|
|
#include "ui_shared/BootablesProcesses.h"
|
|
|
|
#include "ui_shared/BootablesDbClient.h"
|
|
|
|
|
2020-03-07 17:58:03 +00:00
|
|
|
#include "S3FileBrowser.h"
|
|
|
|
#include "../s3stream/AmazonS3Client.h"
|
|
|
|
#include "../s3stream/S3ObjectStream.h"
|
|
|
|
#include "ui_shared/AmazonS3Utils.h"
|
|
|
|
|
2019-01-16 20:00:10 +00:00
|
|
|
BootableListDialog::BootableListDialog(QWidget* parent)
|
|
|
|
: QDialog(parent)
|
|
|
|
, ui(new Ui::BootableListDialog)
|
2019-01-19 23:09:01 +00:00
|
|
|
, m_thread_running(false)
|
2020-03-09 01:10:10 +00:00
|
|
|
, m_s3_processing(false)
|
2019-01-16 20:00:10 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
CAppConfig::GetInstance().RegisterPreferenceInteger("ui.sortmethod", 2);
|
|
|
|
m_sortingMethod = CAppConfig::GetInstance().GetPreferenceInteger("ui.sortmethod");
|
|
|
|
ui->comboBox->setCurrentIndex(m_sortingMethod);
|
|
|
|
|
2019-05-17 23:42:40 +01:00
|
|
|
// used as workaround to avoid direct ui access from a thread
|
|
|
|
connect(this, SIGNAL(AsyncUpdateCoverDisplay()), this, SLOT(UpdateCoverDisplay()));
|
2020-03-11 10:35:30 +00:00
|
|
|
connect(this, SIGNAL(AsyncResetModel(bool)), this, SLOT(resetModel(bool)));
|
2019-05-17 23:42:40 +01:00
|
|
|
|
2019-01-19 23:31:21 +00:00
|
|
|
//if m_sortingMethod == currentIndex == 0, setting index wont trigger on_comboBox_currentIndexChanged() thus resetModel()
|
|
|
|
if(m_sortingMethod == 0)
|
|
|
|
{
|
|
|
|
resetModel();
|
|
|
|
}
|
|
|
|
|
2019-01-19 23:09:01 +00:00
|
|
|
CoverUtils::PopulatePlaceholderCover();
|
2019-01-16 20:00:10 +00:00
|
|
|
ui->listView->setItemDelegate(new BootImageItemDelegate);
|
|
|
|
|
2019-01-17 12:26:47 +00:00
|
|
|
QAction* bootgame = new QAction("Boot", ui->listView);
|
|
|
|
QAction* removegame = new QAction("Remove", ui->listView);
|
2019-01-16 20:00:10 +00:00
|
|
|
|
|
|
|
ui->listView->addAction(bootgame);
|
|
|
|
ui->listView->addAction(removegame);
|
|
|
|
ui->listView->setContextMenuPolicy(Qt::ActionsContextMenu);
|
|
|
|
|
|
|
|
connect(bootgame, &QAction::triggered,
|
2019-01-17 12:26:47 +00:00
|
|
|
[&](bool) {
|
|
|
|
QModelIndex index = ui->listView->selectionModel()->selectedIndexes().at(0);
|
|
|
|
bootable = model->GetBootable(index);
|
2020-03-09 01:10:10 +00:00
|
|
|
if(!m_s3_processing)
|
|
|
|
{
|
|
|
|
accept();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-16 15:33:45 +01:00
|
|
|
DisplayWarningMessage();
|
2020-03-09 01:10:10 +00:00
|
|
|
}
|
2019-01-17 12:26:47 +00:00
|
|
|
});
|
2019-01-16 20:00:10 +00:00
|
|
|
connect(removegame, &QAction::triggered,
|
2019-01-17 12:26:47 +00:00
|
|
|
[&](bool) {
|
|
|
|
QModelIndex index = ui->listView->selectionModel()->selectedIndexes().at(0);
|
|
|
|
auto bootable = model->GetBootable(index);
|
|
|
|
BootablesDb::CClient::GetInstance().UnregisterBootable(bootable.path);
|
|
|
|
model->removeItem(index);
|
|
|
|
});
|
2020-03-07 17:58:03 +00:00
|
|
|
m_continuationChecker = new CContinuationChecker(this);
|
|
|
|
ui->awsS3Button->setVisible(S3FileBrowser::IsAvailable());
|
2020-03-09 01:08:59 +00:00
|
|
|
|
|
|
|
SetupStatusBar();
|
2019-01-16 20:00:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BootableListDialog::~BootableListDialog()
|
|
|
|
{
|
2019-01-19 23:09:01 +00:00
|
|
|
if(cover_loader.joinable())
|
|
|
|
cover_loader.join();
|
2019-01-16 20:00:10 +00:00
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
2020-03-11 10:35:30 +00:00
|
|
|
void BootableListDialog::resetModel(bool repopulateBootables)
|
2019-01-16 20:00:10 +00:00
|
|
|
{
|
|
|
|
ui->listView->setModel(nullptr);
|
|
|
|
if(model)
|
|
|
|
delete model;
|
2019-01-19 23:09:01 +00:00
|
|
|
|
2020-03-11 10:35:30 +00:00
|
|
|
if(repopulateBootables)
|
|
|
|
m_bootables = BootablesDb::CClient::GetInstance().GetBootables(m_sortingMethod);
|
|
|
|
|
2019-01-17 21:52:23 +00:00
|
|
|
model = new BootableModel(this, m_bootables);
|
2019-01-16 20:00:10 +00:00
|
|
|
ui->listView->setModel(model);
|
2020-02-18 13:56:53 +00:00
|
|
|
connect(ui->listView->selectionModel(), &QItemSelectionModel::currentChanged, this, &BootableListDialog::SelectionChange);
|
2019-01-19 23:09:01 +00:00
|
|
|
|
|
|
|
if(!m_thread_running)
|
|
|
|
{
|
2019-01-19 23:17:28 +00:00
|
|
|
if(cover_loader.joinable())
|
|
|
|
cover_loader.join();
|
2019-01-19 23:09:01 +00:00
|
|
|
m_thread_running = true;
|
2019-03-30 00:34:26 +00:00
|
|
|
cover_loader = std::thread([&] {
|
2019-01-19 23:09:01 +00:00
|
|
|
CoverUtils::PopulateCache(m_bootables);
|
|
|
|
|
2019-05-17 23:42:40 +01:00
|
|
|
AsyncUpdateCoverDisplay();
|
2019-01-19 23:09:01 +00:00
|
|
|
m_thread_running = false;
|
|
|
|
});
|
|
|
|
}
|
2019-01-16 20:00:10 +00:00
|
|
|
}
|
|
|
|
BootablesDb::Bootable BootableListDialog::getResult()
|
|
|
|
{
|
|
|
|
return bootable;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BootableListDialog::showEvent(QShowEvent* ev)
|
|
|
|
{
|
2020-02-18 13:56:53 +00:00
|
|
|
ui->horizontalLayout->invalidate();
|
2019-01-16 20:00:10 +00:00
|
|
|
QDialog::showEvent(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BootableListDialog::on_add_games_button_clicked()
|
|
|
|
{
|
|
|
|
QFileDialog dialog(this);
|
|
|
|
dialog.setFileMode(QFileDialog::ExistingFile);
|
|
|
|
dialog.setNameFilter(tr("All supported types(*.iso *.bin *.isz *.cso *.elf);;UltraISO Compressed Disk Images (*.isz);;CISO Compressed Disk Images (*.cso);;ELF files (*.elf);;All files (*.*)"));
|
|
|
|
if(dialog.exec())
|
|
|
|
{
|
|
|
|
auto filePath = QStringToPath(dialog.selectedFiles().first()).parent_path();
|
|
|
|
try
|
|
|
|
{
|
|
|
|
ScanBootables(filePath, false);
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
FetchGameTitles();
|
|
|
|
FetchGameCovers();
|
|
|
|
resetModel();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-17 12:26:47 +00:00
|
|
|
void BootableListDialog::on_listView_doubleClicked(const QModelIndex& index)
|
2019-01-16 20:00:10 +00:00
|
|
|
{
|
|
|
|
bootable = model->GetBootable(index);
|
2020-03-09 01:10:10 +00:00
|
|
|
if(!m_s3_processing)
|
|
|
|
{
|
|
|
|
accept();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-16 15:33:45 +01:00
|
|
|
DisplayWarningMessage();
|
2020-03-09 01:10:10 +00:00
|
|
|
}
|
2019-01-16 20:00:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BootableListDialog::on_refresh_button_clicked()
|
|
|
|
{
|
|
|
|
auto bootables_paths = GetActiveBootableDirectories();
|
|
|
|
for(auto path : bootables_paths)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2019-05-18 12:29:46 +01:00
|
|
|
ScanBootables(path, false);
|
2019-01-16 20:00:10 +00:00
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FetchGameTitles();
|
|
|
|
FetchGameCovers();
|
|
|
|
|
|
|
|
resetModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BootableListDialog::on_comboBox_currentIndexChanged(int index)
|
|
|
|
{
|
|
|
|
CAppConfig::GetInstance().SetPreferenceInteger("ui.sortmethod", index);
|
|
|
|
m_sortingMethod = index;
|
|
|
|
resetModel();
|
|
|
|
}
|
2019-05-17 23:42:40 +01:00
|
|
|
|
|
|
|
void BootableListDialog::UpdateCoverDisplay()
|
|
|
|
{
|
|
|
|
//Force redraw
|
|
|
|
ui->listView->scroll(1, 0);
|
|
|
|
ui->listView->scroll(-1, 0);
|
2019-05-18 12:29:46 +01:00
|
|
|
}
|
2019-05-19 16:16:24 +01:00
|
|
|
|
|
|
|
void BootableListDialog::resizeEvent(QResizeEvent* ev)
|
|
|
|
{
|
|
|
|
model->SetWidth(ui->listView->size().width() - ui->listView->style()->pixelMetric(QStyle::PM_ScrollBarExtent) - 5);
|
|
|
|
QDialog::resizeEvent(ev);
|
|
|
|
}
|
2020-03-07 17:58:03 +00:00
|
|
|
|
|
|
|
void BootableListDialog::on_awsS3Button_clicked()
|
|
|
|
{
|
|
|
|
std::string bucketName = CAppConfig::GetInstance().GetPreferenceString("s3.filebrowser.bucketname");
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
QString res = QInputDialog::getText(this, tr("New Function"),
|
|
|
|
tr("New Function Name:"), QLineEdit::Normal,
|
|
|
|
bucketName.c_str(), &ok);
|
|
|
|
if(!ok || res.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
bucketName = res.toStdString();
|
|
|
|
}
|
|
|
|
if(bucketName.empty())
|
|
|
|
return;
|
|
|
|
|
2020-03-09 01:08:59 +00:00
|
|
|
m_statusBar->show();
|
|
|
|
auto getListFuture = std::async(std::launch::async, [this, bucketName]() {
|
|
|
|
m_s3_processing = true;
|
2020-03-07 17:58:03 +00:00
|
|
|
auto accessKeyId = CS3ObjectStream::CConfig::GetInstance().GetAccessKeyId();
|
|
|
|
auto secretAccessKey = CS3ObjectStream::CConfig::GetInstance().GetSecretAccessKey();
|
2020-03-09 01:08:59 +00:00
|
|
|
AsyncUpdateStatus("Requesting S3 Bucket Content.");
|
2020-03-08 23:07:43 +00:00
|
|
|
auto result = AmazonS3Utils::GetListObjects(accessKeyId, secretAccessKey, bucketName);
|
2020-03-09 01:08:59 +00:00
|
|
|
auto size = result.objects.size();
|
|
|
|
int i = 1;
|
2020-03-07 17:58:03 +00:00
|
|
|
for(const auto& item : result.objects)
|
|
|
|
{
|
|
|
|
auto path = string_format("//s3/%s/%s", bucketName.c_str(), item.key.c_str());
|
|
|
|
try
|
|
|
|
{
|
2020-03-09 01:08:59 +00:00
|
|
|
std::string msg = string_format("Processing: %s (%d/%d)", path.c_str(), i, size);
|
|
|
|
AsyncUpdateStatus(msg);
|
2020-03-11 10:35:30 +00:00
|
|
|
if(TryRegisteringBootable(path))
|
|
|
|
{
|
|
|
|
m_bootables = BootablesDb::CClient::GetInstance().GetBootables(m_sortingMethod);
|
|
|
|
AsyncResetModel(false);
|
|
|
|
}
|
2020-03-07 17:58:03 +00:00
|
|
|
}
|
|
|
|
catch(const std::exception& exception)
|
|
|
|
{
|
|
|
|
//Failed to process a path, keep going
|
|
|
|
}
|
2020-03-09 01:08:59 +00:00
|
|
|
++i;
|
2020-03-07 17:58:03 +00:00
|
|
|
}
|
2020-03-08 23:07:43 +00:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
auto updateBootableCallback = [this](bool) {
|
2020-03-09 01:08:59 +00:00
|
|
|
AsyncUpdateStatus("Refreshing Model.");
|
2020-03-07 17:58:03 +00:00
|
|
|
resetModel();
|
2020-03-09 01:10:10 +00:00
|
|
|
m_s3_processing = false;
|
2020-03-09 01:08:59 +00:00
|
|
|
AsyncUpdateStatus("Complete.");
|
|
|
|
m_statusBar->hide();
|
2020-03-07 17:58:03 +00:00
|
|
|
};
|
|
|
|
m_continuationChecker->GetContinuationManager().Register(std::move(getListFuture), updateBootableCallback);
|
|
|
|
}
|
2020-02-18 13:56:53 +00:00
|
|
|
|
2020-02-20 01:19:22 +00:00
|
|
|
void BootableListDialog::SelectionChange(const QModelIndex& index)
|
2020-02-18 13:56:53 +00:00
|
|
|
{
|
|
|
|
bootable = model->GetBootable(index);
|
|
|
|
ui->pathLineEdit->setText(bootable.path.string().c_str());
|
2020-02-20 01:19:22 +00:00
|
|
|
ui->serialLineEdit->setText(bootable.discId.c_str());
|
2020-02-18 13:56:53 +00:00
|
|
|
}
|
2020-03-09 01:10:10 +00:00
|
|
|
|
|
|
|
void BootableListDialog::closeEvent(QCloseEvent* event)
|
|
|
|
{
|
|
|
|
if(m_s3_processing)
|
|
|
|
{
|
|
|
|
event->ignore();
|
2020-04-16 15:33:45 +01:00
|
|
|
DisplayWarningMessage();
|
2020-03-09 01:10:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
event->accept();
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 01:08:59 +00:00
|
|
|
|
|
|
|
void BootableListDialog::SetupStatusBar()
|
|
|
|
{
|
|
|
|
m_statusBar = new QStatusBar(this);
|
|
|
|
ui->verticalLayout_3->addWidget(m_statusBar);
|
|
|
|
|
|
|
|
m_msgLabel = new ElidedLabel();
|
|
|
|
m_msgLabel->setAlignment(Qt::AlignLeft);
|
|
|
|
QFontMetrics fm(m_msgLabel->font());
|
|
|
|
m_msgLabel->setMinimumSize(fm.boundingRect("...").size());
|
|
|
|
m_msgLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
|
|
|
|
|
|
|
qRegisterMetaType<std::string>("std::string");
|
|
|
|
connect(this, SIGNAL(AsyncUpdateStatus(std::string)), this, SLOT(UpdateStatus(std::string)));
|
|
|
|
|
|
|
|
m_statusBar->addWidget(m_msgLabel, 1);
|
|
|
|
m_statusBar->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BootableListDialog::UpdateStatus(std::string msg)
|
|
|
|
{
|
|
|
|
m_msgLabel->setText(msg.c_str());
|
2020-04-16 15:33:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void BootableListDialog::DisplayWarningMessage()
|
|
|
|
{
|
|
|
|
QMessageBox::warning(this, "Warning Message",
|
|
|
|
"Can't close dialog while background operation in progress.",
|
|
|
|
QMessageBox::Ok, QMessageBox::Ok);
|
|
|
|
}
|