2023-06-23 13:18:10 +00:00
|
|
|
/* PCSX2 - PS2 Emulator for PCs
|
|
|
|
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
|
|
|
*
|
|
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
|
2023-07-25 15:10:02 +00:00
|
|
|
#include "pcsx2/SIO/Pad/Pad.h"
|
2023-06-23 13:18:10 +00:00
|
|
|
#include "QtHost.h"
|
|
|
|
#include "QtUtils.h"
|
|
|
|
#include "SettingWidgetBinder.h"
|
|
|
|
#include "Settings/ControllerSettingWidgetBinder.h"
|
|
|
|
#include "Settings/InterfaceSettingsWidget.h"
|
|
|
|
#include "SetupWizardDialog.h"
|
|
|
|
|
|
|
|
#include <QtWidgets/QMessageBox>
|
|
|
|
|
|
|
|
SetupWizardDialog::SetupWizardDialog()
|
|
|
|
{
|
|
|
|
setupUi();
|
|
|
|
updatePageLabels(-1);
|
|
|
|
updatePageButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
SetupWizardDialog::~SetupWizardDialog()
|
|
|
|
{
|
|
|
|
if (m_bios_refresh_thread)
|
|
|
|
{
|
|
|
|
m_bios_refresh_thread->wait();
|
|
|
|
delete m_bios_refresh_thread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::resizeEvent(QResizeEvent* event)
|
|
|
|
{
|
|
|
|
QDialog::resizeEvent(event);
|
|
|
|
resizeDirectoryListColumns();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetupWizardDialog::canShowNextPage()
|
|
|
|
{
|
|
|
|
const int current_page = m_ui.pages->currentIndex();
|
|
|
|
|
|
|
|
switch (current_page)
|
|
|
|
{
|
|
|
|
case Page_BIOS:
|
|
|
|
{
|
|
|
|
if (!m_ui.biosList->currentItem())
|
|
|
|
{
|
|
|
|
if (QMessageBox::question(this, tr("Warning"),
|
|
|
|
tr("A BIOS image has not been selected. PCSX2 <strong>will not</strong> be able to run games "
|
|
|
|
"without a BIOS image.<br><br>Are you sure you wish to continue without selecting a BIOS "
|
|
|
|
"image?")) != QMessageBox::Yes)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Page_GameList:
|
|
|
|
{
|
|
|
|
if (m_ui.searchDirectoryList->rowCount() == 0)
|
|
|
|
{
|
|
|
|
if (QMessageBox::question(this, tr("Warning"),
|
|
|
|
tr("No game directories have been selected. You will have to manually open any game dumps you "
|
|
|
|
"want to play, PCSX2's list will be empty.\n\nAre you sure you want to continue?")) !=
|
|
|
|
QMessageBox::Yes)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::previousPage()
|
|
|
|
{
|
|
|
|
const int current_page = m_ui.pages->currentIndex();
|
|
|
|
if (current_page == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_ui.pages->setCurrentIndex(current_page - 1);
|
|
|
|
updatePageLabels(current_page);
|
|
|
|
updatePageButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::nextPage()
|
|
|
|
{
|
|
|
|
const int current_page = m_ui.pages->currentIndex();
|
|
|
|
if (current_page == Page_Complete)
|
|
|
|
{
|
|
|
|
accept();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!canShowNextPage())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const int new_page = current_page + 1;
|
|
|
|
m_ui.pages->setCurrentIndex(new_page);
|
|
|
|
updatePageLabels(current_page);
|
|
|
|
updatePageButtons();
|
|
|
|
pageChangedTo(new_page);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::pageChangedTo(int page)
|
|
|
|
{
|
|
|
|
switch (page)
|
|
|
|
{
|
|
|
|
case Page_GameList:
|
|
|
|
resizeDirectoryListColumns();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::updatePageLabels(int prev_page)
|
|
|
|
{
|
|
|
|
if (prev_page >= 0)
|
|
|
|
{
|
|
|
|
QFont prev_font = m_page_labels[prev_page]->font();
|
|
|
|
prev_font.setBold(false);
|
|
|
|
m_page_labels[prev_page]->setFont(prev_font);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int page = m_ui.pages->currentIndex();
|
|
|
|
QFont font = m_page_labels[page]->font();
|
|
|
|
font.setBold(true);
|
|
|
|
m_page_labels[page]->setFont(font);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::updatePageButtons()
|
|
|
|
{
|
|
|
|
const int page = m_ui.pages->currentIndex();
|
|
|
|
m_ui.next->setText((page == Page_Complete) ? "&Finish" : "&Next");
|
|
|
|
m_ui.back->setEnabled(page > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::confirmCancel()
|
|
|
|
{
|
|
|
|
if (QMessageBox::question(this, tr("Cancel Setup"),
|
|
|
|
tr("Are you sure you want to cancel PCSX2 setup?\n\nAny changes have been saved, and the wizard will run "
|
|
|
|
"again next time you start PCSX2.")) != QMessageBox::Yes)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
reject();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::setupUi()
|
|
|
|
{
|
|
|
|
m_ui.setupUi(this);
|
|
|
|
|
2023-06-25 11:41:13 +00:00
|
|
|
m_ui.logo->setPixmap(QPixmap(QStringLiteral("%1/icons/AppIconLarge.png").arg(QtHost::GetResourcesBasePath())));
|
|
|
|
|
2023-06-23 13:18:10 +00:00
|
|
|
m_ui.pages->setCurrentIndex(0);
|
|
|
|
|
|
|
|
m_page_labels[Page_Language] = m_ui.labelLanguage;
|
|
|
|
m_page_labels[Page_BIOS] = m_ui.labelBIOS;
|
|
|
|
m_page_labels[Page_GameList] = m_ui.labelGameList;
|
|
|
|
m_page_labels[Page_Controller] = m_ui.labelController;
|
|
|
|
m_page_labels[Page_Complete] = m_ui.labelComplete;
|
|
|
|
|
|
|
|
connect(m_ui.back, &QPushButton::clicked, this, &SetupWizardDialog::previousPage);
|
|
|
|
connect(m_ui.next, &QPushButton::clicked, this, &SetupWizardDialog::nextPage);
|
|
|
|
connect(m_ui.cancel, &QPushButton::clicked, this, &SetupWizardDialog::confirmCancel);
|
|
|
|
|
|
|
|
setupLanguagePage();
|
|
|
|
setupBIOSPage();
|
|
|
|
setupGameListPage();
|
|
|
|
setupControllerPage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::setupLanguagePage()
|
|
|
|
{
|
|
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(nullptr, m_ui.theme, "UI", "Theme",
|
2023-06-30 12:42:48 +00:00
|
|
|
InterfaceSettingsWidget::THEME_NAMES, InterfaceSettingsWidget::THEME_VALUES, QtHost::GetDefaultThemeName(), "InterfaceSettingsWidget");
|
2023-06-23 13:18:10 +00:00
|
|
|
connect(m_ui.theme, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SetupWizardDialog::themeChanged);
|
|
|
|
|
|
|
|
for (const std::pair<QString, QString>& it : QtHost::GetAvailableLanguageList())
|
|
|
|
m_ui.language->addItem(it.first, it.second);
|
|
|
|
SettingWidgetBinder::BindWidgetToStringSetting(
|
|
|
|
nullptr, m_ui.language, "UI", "Language", QtHost::GetDefaultLanguage());
|
|
|
|
connect(
|
|
|
|
m_ui.language, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SetupWizardDialog::languageChanged);
|
|
|
|
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
nullptr, m_ui.autoUpdateEnabled, "AutoUpdater", "CheckAtStartup", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::themeChanged()
|
|
|
|
{
|
|
|
|
// Main window gets recreated at the end here anyway, so it's fine to just yolo it.
|
|
|
|
QtHost::UpdateApplicationTheme();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::languageChanged()
|
|
|
|
{
|
|
|
|
// Skip the recreation, since we don't have many dynamic UI elements.
|
|
|
|
QtHost::InstallTranslator();
|
|
|
|
m_ui.retranslateUi(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::setupBIOSPage()
|
|
|
|
{
|
|
|
|
SettingWidgetBinder::BindWidgetToFolderSetting(nullptr, m_ui.biosSearchDirectory, m_ui.browseBiosSearchDirectory,
|
|
|
|
m_ui.openBiosSearchDirectory, m_ui.resetBiosSearchDirectory, "Folders", "Bios",
|
|
|
|
Path::Combine(EmuFolders::DataRoot, "bios"));
|
|
|
|
|
|
|
|
refreshBiosList();
|
|
|
|
|
|
|
|
connect(m_ui.biosSearchDirectory, &QLineEdit::textChanged, this, &SetupWizardDialog::refreshBiosList);
|
|
|
|
connect(m_ui.refreshBiosList, &QPushButton::clicked, this, &SetupWizardDialog::refreshBiosList);
|
|
|
|
connect(m_ui.biosList, &QTreeWidget::currentItemChanged, this, &SetupWizardDialog::biosListItemChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::refreshBiosList()
|
|
|
|
{
|
|
|
|
if (m_bios_refresh_thread)
|
|
|
|
{
|
|
|
|
m_bios_refresh_thread->wait();
|
|
|
|
delete m_bios_refresh_thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSignalBlocker blocker(m_ui.biosList);
|
|
|
|
m_ui.biosList->clear();
|
|
|
|
m_ui.biosList->setEnabled(false);
|
|
|
|
|
|
|
|
m_bios_refresh_thread = new BIOSSettingsWidget::RefreshThread(this, m_ui.biosSearchDirectory->text());
|
|
|
|
m_bios_refresh_thread->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::biosListItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous)
|
|
|
|
{
|
|
|
|
Host::SetBaseStringSettingValue("Filenames", "BIOS", current->text(0).toUtf8().constData());
|
|
|
|
Host::CommitBaseSettingChanges();
|
|
|
|
g_emu_thread->applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::listRefreshed(const QVector<BIOSInfo>& items)
|
|
|
|
{
|
|
|
|
QSignalBlocker sb(m_ui.biosList);
|
|
|
|
BIOSSettingsWidget::populateList(m_ui.biosList, items);
|
|
|
|
m_ui.biosList->setEnabled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::setupGameListPage()
|
|
|
|
{
|
|
|
|
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,
|
|
|
|
&SetupWizardDialog::onDirectoryListContextMenuRequested);
|
|
|
|
connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this,
|
|
|
|
&SetupWizardDialog::onAddSearchDirectoryButtonClicked);
|
|
|
|
connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this,
|
|
|
|
&SetupWizardDialog::onRemoveSearchDirectoryButtonClicked);
|
|
|
|
|
|
|
|
refreshDirectoryList();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::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 SetupWizardDialog::onAddSearchDirectoryButtonClicked()
|
|
|
|
{
|
|
|
|
QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, 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);
|
|
|
|
const std::string spath = dir.toStdString();
|
|
|
|
Host::RemoveBaseValueFromStringList("GameList", recursive ? "Paths" : "RecursivePaths", spath.c_str());
|
|
|
|
Host::AddBaseValueToStringList("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str());
|
|
|
|
Host::CommitBaseSettingChanges();
|
|
|
|
refreshDirectoryList();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::onRemoveSearchDirectoryButtonClicked()
|
|
|
|
{
|
|
|
|
const int row = m_ui.searchDirectoryList->currentRow();
|
|
|
|
std::unique_ptr<QTableWidgetItem> item((row >= 0) ? m_ui.searchDirectoryList->takeItem(row, 0) : nullptr);
|
|
|
|
if (!item)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const std::string spath = item->text().toStdString();
|
|
|
|
if (!Host::RemoveBaseValueFromStringList("GameList", "Paths", spath.c_str()) &&
|
|
|
|
!Host::RemoveBaseValueFromStringList("GameList", "RecursivePaths", spath.c_str()))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Host::CommitBaseSettingChanges();
|
|
|
|
refreshDirectoryList();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::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::stateChanged, [item](int 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 SetupWizardDialog::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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::resizeDirectoryListColumns()
|
|
|
|
{
|
|
|
|
QtUtils::ResizeColumnsForTableView(m_ui.searchDirectoryList, {-1, 100});
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::setupControllerPage()
|
|
|
|
{
|
|
|
|
static constexpr u32 NUM_PADS = 2;
|
|
|
|
|
|
|
|
struct PadWidgets
|
|
|
|
{
|
|
|
|
QComboBox* type_combo;
|
|
|
|
QLabel* mapping_result;
|
|
|
|
QToolButton* mapping_button;
|
|
|
|
};
|
|
|
|
const PadWidgets pad_widgets[NUM_PADS] = {
|
|
|
|
{m_ui.controller1Type, m_ui.controller1Mapping, m_ui.controller1AutomaticMapping},
|
|
|
|
{m_ui.controller2Type, m_ui.controller2Mapping, m_ui.controller2AutomaticMapping},
|
|
|
|
};
|
|
|
|
|
|
|
|
for (u32 port = 0; port < NUM_PADS; port++)
|
|
|
|
{
|
|
|
|
const std::string section = fmt::format("Pad{}", port + 1);
|
|
|
|
const PadWidgets& w = pad_widgets[port];
|
|
|
|
|
2023-07-25 15:10:02 +00:00
|
|
|
for (const auto& [name, display_name] : Pad::GetControllerTypeNames())
|
|
|
|
w.type_combo->addItem(QString::fromUtf8(display_name), QString::fromUtf8(name));
|
2023-06-23 13:18:10 +00:00
|
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
|
2023-07-25 15:10:02 +00:00
|
|
|
nullptr, w.type_combo, section, "Type", Pad::GetDefaultPadType(port));
|
2023-06-23 13:18:10 +00:00
|
|
|
|
|
|
|
w.mapping_result->setText((port == 0) ? tr("Default (Keyboard)") : tr("Default (None)"));
|
|
|
|
|
|
|
|
connect(w.mapping_button, &QAbstractButton::clicked, this,
|
|
|
|
[this, port, label = w.mapping_result]() { openAutomaticMappingMenu(port, label); });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger enumeration to populate the device list.
|
|
|
|
connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, &SetupWizardDialog::onInputDevicesEnumerated);
|
|
|
|
connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &SetupWizardDialog::onInputDeviceConnected);
|
|
|
|
connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this, &SetupWizardDialog::onInputDeviceDisconnected);
|
|
|
|
g_emu_thread->enumerateInputDevices();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::openAutomaticMappingMenu(u32 port, QLabel* update_label)
|
|
|
|
{
|
|
|
|
QMenu menu(this);
|
|
|
|
bool added = false;
|
|
|
|
|
|
|
|
for (const QPair<QString, QString>& dev : m_device_list)
|
|
|
|
{
|
|
|
|
// we set it as data, because the device list could get invalidated while the menu is up
|
|
|
|
QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.first).arg(dev.second));
|
|
|
|
action->setData(dev.first);
|
|
|
|
connect(action, &QAction::triggered, this, [this, port, update_label, action]() {
|
|
|
|
doDeviceAutomaticBinding(port, update_label, action->data().toString());
|
|
|
|
});
|
|
|
|
added = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!added)
|
|
|
|
{
|
|
|
|
QAction* action = menu.addAction(tr("No devices available"));
|
|
|
|
action->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.exec(QCursor::pos());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::doDeviceAutomaticBinding(u32 port, QLabel* update_label, const QString& device)
|
|
|
|
{
|
|
|
|
std::vector<std::pair<GenericInputBinding, std::string>> mapping =
|
|
|
|
InputManager::GetGenericBindingMapping(device.toStdString());
|
|
|
|
if (mapping.empty())
|
|
|
|
{
|
|
|
|
QMessageBox::critical(this, tr("Automatic Binding"),
|
|
|
|
tr("No generic bindings were generated for device '%1'. The controller/source may not support automatic "
|
|
|
|
"mapping.")
|
|
|
|
.arg(device));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool result;
|
|
|
|
{
|
|
|
|
auto lock = Host::GetSettingsLock();
|
2023-07-25 15:10:02 +00:00
|
|
|
result = Pad::MapController(*Host::Internal::GetBaseSettingsLayer(), port, mapping);
|
2023-06-23 13:18:10 +00:00
|
|
|
}
|
|
|
|
if (!result)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Host::CommitBaseSettingChanges();
|
|
|
|
|
|
|
|
update_label->setText(device);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices)
|
|
|
|
{
|
|
|
|
m_device_list = devices;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::onInputDeviceConnected(const QString& identifier, const QString& device_name)
|
|
|
|
{
|
|
|
|
m_device_list.emplace_back(identifier, device_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupWizardDialog::onInputDeviceDisconnected(const QString& identifier)
|
|
|
|
{
|
|
|
|
for (auto iter = m_device_list.begin(); iter != m_device_list.end(); ++iter)
|
|
|
|
{
|
|
|
|
if (iter->first == identifier)
|
|
|
|
{
|
|
|
|
m_device_list.erase(iter);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|