2017-05-20 15:53:17 +00:00
|
|
|
// Copyright 2017 Dolphin Emulator Project
|
2021-07-05 01:22:19 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2018-07-06 22:40:15 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/MappingWindow.h"
|
2018-05-28 01:48:04 +00:00
|
|
|
|
2018-04-01 14:25:34 +00:00
|
|
|
#include <QCheckBox>
|
2017-05-20 15:53:17 +00:00
|
|
|
#include <QComboBox>
|
|
|
|
#include <QDialogButtonBox>
|
|
|
|
#include <QGroupBox>
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
#include <QPushButton>
|
|
|
|
#include <QTabWidget>
|
2020-01-18 17:19:32 +00:00
|
|
|
#include <QTimer>
|
2017-05-20 15:53:17 +00:00
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
2019-03-15 01:27:49 +00:00
|
|
|
#include "Core/Core.h"
|
2021-10-08 01:39:52 +00:00
|
|
|
#include "Core/HotkeyManager.h"
|
2019-03-15 01:27:49 +00:00
|
|
|
|
2021-01-17 18:47:49 +00:00
|
|
|
#include "Common/CommonPaths.h"
|
2017-06-09 20:00:19 +00:00
|
|
|
#include "Common/FileSearch.h"
|
2017-05-20 15:53:17 +00:00
|
|
|
#include "Common/FileUtil.h"
|
|
|
|
#include "Common/IniFile.h"
|
2017-06-09 20:00:19 +00:00
|
|
|
#include "Common/StringUtil.h"
|
2018-05-28 01:48:04 +00:00
|
|
|
|
2020-06-12 05:27:34 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/FreeLookGeneral.h"
|
2021-03-11 06:41:15 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/FreeLookRotation.h"
|
2021-07-04 11:17:32 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/GBAPadEmu.h"
|
2018-07-06 22:40:15 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/GCKeyboardEmu.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/GCMicrophone.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/GCPadEmu.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/Hotkey3D.h"
|
2018-04-14 04:51:32 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyControllerProfile.h"
|
2018-07-06 22:40:15 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyDebugging.h"
|
2021-07-04 11:26:47 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyGBA.h"
|
2018-07-06 22:40:15 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyGeneral.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyGraphics.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyStates.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyStatesOther.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyTAS.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/HotkeyWii.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h"
|
2019-11-04 03:19:33 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionInput.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionSimulation.h"
|
2018-07-06 22:40:15 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/WiimoteEmuGeneral.h"
|
|
|
|
#include "DolphinQt/Config/Mapping/WiimoteEmuMotionControl.h"
|
2019-09-06 15:09:30 +00:00
|
|
|
#include "DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h"
|
2019-03-04 19:49:00 +00:00
|
|
|
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
2022-03-08 07:51:29 +00:00
|
|
|
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
|
2021-10-08 01:39:52 +00:00
|
|
|
#include "DolphinQt/QtUtils/WindowActivationEventFilter.h"
|
2018-07-06 22:40:15 +00:00
|
|
|
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
|
|
|
#include "DolphinQt/Settings.h"
|
2018-05-28 01:48:04 +00:00
|
|
|
|
2017-05-20 15:53:17 +00:00
|
|
|
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
2017-11-03 21:31:17 +00:00
|
|
|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
2020-09-15 11:34:41 +00:00
|
|
|
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
2017-05-20 15:53:17 +00:00
|
|
|
#include "InputCommon/InputConfig.h"
|
|
|
|
|
2018-04-30 07:09:27 +00:00
|
|
|
constexpr const char* PROFILES_DIR = "Profiles/";
|
|
|
|
|
2017-11-03 21:31:17 +00:00
|
|
|
MappingWindow::MappingWindow(QWidget* parent, Type type, int port_num)
|
|
|
|
: QDialog(parent), m_port(port_num)
|
2017-05-20 15:53:17 +00:00
|
|
|
{
|
|
|
|
setWindowTitle(tr("Port %1").arg(port_num + 1));
|
2017-06-30 00:52:53 +00:00
|
|
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
CreateDevicesLayout();
|
|
|
|
CreateProfilesLayout();
|
|
|
|
CreateResetLayout();
|
|
|
|
CreateMainLayout();
|
|
|
|
ConnectWidgets();
|
2017-11-03 21:31:17 +00:00
|
|
|
SetMappingType(type);
|
2019-03-15 01:27:49 +00:00
|
|
|
|
2020-01-18 17:19:32 +00:00
|
|
|
const auto timer = new QTimer(this);
|
|
|
|
connect(timer, &QTimer::timeout, this, [this] {
|
|
|
|
const auto lock = GetController()->GetStateLock();
|
|
|
|
emit Update();
|
|
|
|
});
|
|
|
|
|
|
|
|
timer->start(1000 / INDICATOR_UPDATE_FREQ);
|
|
|
|
|
2020-02-23 15:34:31 +00:00
|
|
|
const auto lock = GetController()->GetStateLock();
|
2019-03-15 01:27:49 +00:00
|
|
|
emit ConfigChanged();
|
2021-10-08 01:39:52 +00:00
|
|
|
|
|
|
|
auto* filter = new WindowActivationEventFilter(this);
|
|
|
|
installEventFilter(filter);
|
|
|
|
|
|
|
|
filter->connect(filter, &WindowActivationEventFilter::windowDeactivated,
|
|
|
|
[] { HotkeyManagerEmu::Enable(true); });
|
|
|
|
filter->connect(filter, &WindowActivationEventFilter::windowActivated,
|
|
|
|
[] { HotkeyManagerEmu::Enable(false); });
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::CreateDevicesLayout()
|
|
|
|
{
|
|
|
|
m_devices_layout = new QHBoxLayout();
|
2017-07-23 10:58:32 +00:00
|
|
|
m_devices_box = new QGroupBox(tr("Device"));
|
2017-05-20 15:53:17 +00:00
|
|
|
m_devices_combo = new QComboBox();
|
2022-03-08 07:51:29 +00:00
|
|
|
m_devices_refresh = new NonDefaultQPushButton(tr("Refresh"));
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2021-04-27 02:24:36 +00:00
|
|
|
m_devices_combo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
2017-05-20 15:53:17 +00:00
|
|
|
m_devices_refresh->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
2019-03-03 22:09:24 +00:00
|
|
|
|
2017-05-20 15:53:17 +00:00
|
|
|
m_devices_layout->addWidget(m_devices_combo);
|
|
|
|
m_devices_layout->addWidget(m_devices_refresh);
|
|
|
|
|
|
|
|
m_devices_box->setLayout(m_devices_layout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::CreateProfilesLayout()
|
|
|
|
{
|
2017-06-14 04:24:24 +00:00
|
|
|
m_profiles_layout = new QHBoxLayout();
|
2017-07-23 10:58:32 +00:00
|
|
|
m_profiles_box = new QGroupBox(tr("Profile"));
|
2017-05-20 15:53:17 +00:00
|
|
|
m_profiles_combo = new QComboBox();
|
2022-03-08 07:51:29 +00:00
|
|
|
m_profiles_load = new NonDefaultQPushButton(tr("Load"));
|
|
|
|
m_profiles_save = new NonDefaultQPushButton(tr("Save"));
|
|
|
|
m_profiles_delete = new NonDefaultQPushButton(tr("Delete"));
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
auto* button_layout = new QHBoxLayout();
|
|
|
|
|
2019-03-03 22:09:24 +00:00
|
|
|
m_profiles_combo->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
2019-04-28 19:00:35 +00:00
|
|
|
m_profiles_combo->setMinimumWidth(100);
|
2017-05-20 15:53:17 +00:00
|
|
|
m_profiles_combo->setEditable(true);
|
|
|
|
|
|
|
|
m_profiles_layout->addWidget(m_profiles_combo);
|
|
|
|
button_layout->addWidget(m_profiles_load);
|
|
|
|
button_layout->addWidget(m_profiles_save);
|
|
|
|
button_layout->addWidget(m_profiles_delete);
|
2018-05-08 23:54:47 +00:00
|
|
|
m_profiles_layout->addLayout(button_layout);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
m_profiles_box->setLayout(m_profiles_layout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::CreateResetLayout()
|
|
|
|
{
|
2017-06-14 04:24:24 +00:00
|
|
|
m_reset_layout = new QHBoxLayout();
|
2017-05-20 15:53:17 +00:00
|
|
|
m_reset_box = new QGroupBox(tr("Reset"));
|
2022-03-08 07:51:29 +00:00
|
|
|
m_reset_clear = new NonDefaultQPushButton(tr("Clear"));
|
|
|
|
m_reset_default = new NonDefaultQPushButton(tr("Default"));
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
m_reset_box->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
|
|
|
|
|
|
m_reset_layout->addWidget(m_reset_default);
|
2017-06-14 04:24:24 +00:00
|
|
|
m_reset_layout->addWidget(m_reset_clear);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
m_reset_box->setLayout(m_reset_layout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::CreateMainLayout()
|
|
|
|
{
|
|
|
|
m_main_layout = new QVBoxLayout();
|
|
|
|
m_config_layout = new QHBoxLayout();
|
|
|
|
m_tab_widget = new QTabWidget();
|
2018-03-20 09:12:11 +00:00
|
|
|
m_button_box = new QDialogButtonBox(QDialogButtonBox::Close);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2017-06-14 04:24:24 +00:00
|
|
|
m_config_layout->addWidget(m_devices_box);
|
2017-05-20 15:53:17 +00:00
|
|
|
m_config_layout->addWidget(m_reset_box);
|
2017-06-14 04:24:24 +00:00
|
|
|
m_config_layout->addWidget(m_profiles_box);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2018-05-08 23:54:47 +00:00
|
|
|
m_main_layout->addLayout(m_config_layout);
|
2017-05-20 15:53:17 +00:00
|
|
|
m_main_layout->addWidget(m_tab_widget);
|
|
|
|
m_main_layout->addWidget(m_button_box);
|
|
|
|
|
|
|
|
setLayout(m_main_layout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::ConnectWidgets()
|
|
|
|
{
|
2018-07-05 21:17:47 +00:00
|
|
|
connect(&Settings::Instance(), &Settings::DevicesChanged, this,
|
|
|
|
&MappingWindow::OnGlobalDevicesChanged);
|
2019-03-15 01:27:49 +00:00
|
|
|
connect(this, &MappingWindow::ConfigChanged, this, &MappingWindow::OnGlobalDevicesChanged);
|
2019-07-30 13:35:46 +00:00
|
|
|
connect(m_devices_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
|
|
|
&MappingWindow::OnSelectDevice);
|
2019-03-15 01:27:49 +00:00
|
|
|
|
|
|
|
connect(m_devices_refresh, &QPushButton::clicked, this, &MappingWindow::RefreshDevices);
|
|
|
|
|
2019-02-06 23:40:58 +00:00
|
|
|
connect(m_reset_clear, &QPushButton::clicked, this, &MappingWindow::OnClearFieldsPressed);
|
2017-05-20 15:53:17 +00:00
|
|
|
connect(m_reset_default, &QPushButton::clicked, this, &MappingWindow::OnDefaultFieldsPressed);
|
|
|
|
connect(m_profiles_save, &QPushButton::clicked, this, &MappingWindow::OnSaveProfilePressed);
|
|
|
|
connect(m_profiles_load, &QPushButton::clicked, this, &MappingWindow::OnLoadProfilePressed);
|
|
|
|
connect(m_profiles_delete, &QPushButton::clicked, this, &MappingWindow::OnDeleteProfilePressed);
|
2019-03-15 01:27:49 +00:00
|
|
|
|
2020-09-09 02:26:13 +00:00
|
|
|
connect(m_profiles_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
|
|
|
&MappingWindow::OnSelectProfile);
|
|
|
|
connect(m_profiles_combo, &QComboBox::editTextChanged, this,
|
|
|
|
&MappingWindow::OnProfileTextChanged);
|
|
|
|
|
2019-02-05 00:50:07 +00:00
|
|
|
// We currently use the "Close" button as an "Accept" button so we must save on reject.
|
|
|
|
connect(this, &QDialog::rejected, [this] { emit Save(); });
|
2019-03-15 01:27:49 +00:00
|
|
|
connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
2019-04-28 19:00:35 +00:00
|
|
|
void MappingWindow::UpdateProfileIndex()
|
|
|
|
{
|
|
|
|
// Make sure currentIndex and currentData are accurate when the user manually types a name.
|
|
|
|
|
|
|
|
const auto current_text = m_profiles_combo->currentText();
|
|
|
|
const int text_index = m_profiles_combo->findText(current_text);
|
|
|
|
m_profiles_combo->setCurrentIndex(text_index);
|
|
|
|
|
|
|
|
if (text_index == -1)
|
|
|
|
m_profiles_combo->setCurrentText(current_text);
|
|
|
|
}
|
|
|
|
|
2020-09-09 02:26:13 +00:00
|
|
|
void MappingWindow::UpdateProfileButtonState()
|
|
|
|
{
|
|
|
|
// Make sure save/delete buttons are disabled for built-in profiles
|
|
|
|
|
|
|
|
bool builtin = false;
|
|
|
|
if (m_profiles_combo->findText(m_profiles_combo->currentText()) != -1)
|
|
|
|
{
|
|
|
|
const QString profile_path = m_profiles_combo->currentData().toString();
|
2021-01-17 18:47:49 +00:00
|
|
|
std::string sys_dir = File::GetSysDirectory();
|
|
|
|
sys_dir = ReplaceAll(sys_dir, "\\", DIR_SEP);
|
|
|
|
builtin = profile_path.startsWith(QString::fromStdString(sys_dir));
|
2020-09-09 02:26:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_profiles_save->setEnabled(!builtin);
|
|
|
|
m_profiles_delete->setEnabled(!builtin);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::OnSelectProfile(int)
|
|
|
|
{
|
|
|
|
UpdateProfileButtonState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::OnProfileTextChanged(const QString&)
|
|
|
|
{
|
|
|
|
UpdateProfileButtonState();
|
|
|
|
}
|
|
|
|
|
2017-05-20 15:53:17 +00:00
|
|
|
void MappingWindow::OnDeleteProfilePressed()
|
|
|
|
{
|
2019-04-28 19:00:35 +00:00
|
|
|
UpdateProfileIndex();
|
|
|
|
|
2017-05-20 15:53:17 +00:00
|
|
|
const QString profile_name = m_profiles_combo->currentText();
|
2017-06-09 20:00:19 +00:00
|
|
|
const QString profile_path = m_profiles_combo->currentData().toString();
|
|
|
|
|
2019-04-28 19:00:35 +00:00
|
|
|
if (m_profiles_combo->currentIndex() == -1 || !File::Exists(profile_path.toStdString()))
|
2017-05-20 15:53:17 +00:00
|
|
|
{
|
2019-03-04 19:49:00 +00:00
|
|
|
ModalMessageBox error(this);
|
2017-05-20 15:53:17 +00:00
|
|
|
error.setIcon(QMessageBox::Critical);
|
2018-06-30 03:27:47 +00:00
|
|
|
error.setWindowTitle(tr("Error"));
|
2017-05-20 15:53:17 +00:00
|
|
|
error.setText(tr("The profile '%1' does not exist").arg(profile_name));
|
|
|
|
error.exec();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-04 19:49:00 +00:00
|
|
|
ModalMessageBox confirm(this);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
confirm.setIcon(QMessageBox::Warning);
|
2018-06-30 03:27:47 +00:00
|
|
|
confirm.setWindowTitle(tr("Confirm"));
|
2017-05-20 15:53:17 +00:00
|
|
|
confirm.setText(tr("Are you sure that you want to delete '%1'?").arg(profile_name));
|
|
|
|
confirm.setInformativeText(tr("This cannot be undone!"));
|
|
|
|
confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
|
|
|
|
|
|
|
|
if (confirm.exec() != QMessageBox::Yes)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_profiles_combo->removeItem(m_profiles_combo->currentIndex());
|
2019-04-28 19:00:35 +00:00
|
|
|
m_profiles_combo->setCurrentIndex(-1);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2017-06-09 20:00:19 +00:00
|
|
|
File::Delete(profile_path.toStdString());
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2019-03-04 19:49:00 +00:00
|
|
|
ModalMessageBox result(this);
|
2017-05-20 15:53:17 +00:00
|
|
|
result.setIcon(QMessageBox::Information);
|
2019-03-03 15:19:50 +00:00
|
|
|
result.setWindowModality(Qt::WindowModal);
|
2018-06-30 03:27:47 +00:00
|
|
|
result.setWindowTitle(tr("Success"));
|
2017-05-20 15:53:17 +00:00
|
|
|
result.setText(tr("Successfully deleted '%1'.").arg(profile_name));
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::OnLoadProfilePressed()
|
|
|
|
{
|
2019-04-28 19:00:35 +00:00
|
|
|
UpdateProfileIndex();
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2019-04-28 19:00:35 +00:00
|
|
|
if (m_profiles_combo->currentIndex() == -1)
|
|
|
|
{
|
|
|
|
ModalMessageBox error(this);
|
|
|
|
error.setIcon(QMessageBox::Critical);
|
|
|
|
error.setWindowTitle(tr("Error"));
|
|
|
|
error.setText(tr("The profile '%1' does not exist").arg(m_profiles_combo->currentText()));
|
|
|
|
error.exec();
|
2017-05-20 15:53:17 +00:00
|
|
|
return;
|
2019-04-28 19:00:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const QString profile_path = m_profiles_combo->currentData().toString();
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
IniFile ini;
|
2017-06-09 20:00:19 +00:00
|
|
|
ini.Load(profile_path.toStdString());
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
m_controller->LoadConfig(ini.GetOrCreateSection("Profile"));
|
|
|
|
m_controller->UpdateReferences(g_controller_interface);
|
|
|
|
|
2020-02-23 15:34:31 +00:00
|
|
|
const auto lock = GetController()->GetStateLock();
|
2019-03-15 01:27:49 +00:00
|
|
|
emit ConfigChanged();
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::OnSaveProfilePressed()
|
|
|
|
{
|
|
|
|
const QString profile_name = m_profiles_combo->currentText();
|
|
|
|
|
|
|
|
if (profile_name.isEmpty())
|
|
|
|
return;
|
|
|
|
|
2019-04-28 19:00:35 +00:00
|
|
|
const std::string profile_path = File::GetUserPath(D_CONFIG_IDX) + PROFILES_DIR +
|
|
|
|
m_config->GetProfileName() + "/" + profile_name.toStdString() +
|
|
|
|
".ini";
|
|
|
|
|
2018-04-30 07:09:27 +00:00
|
|
|
File::CreateFullPath(profile_path);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
|
|
|
IniFile ini;
|
|
|
|
|
|
|
|
m_controller->SaveConfig(ini.GetOrCreateSection("Profile"));
|
2018-04-30 07:09:27 +00:00
|
|
|
ini.Save(profile_path);
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2019-04-28 19:00:35 +00:00
|
|
|
if (m_profiles_combo->findText(profile_name) == -1)
|
2020-09-09 02:26:13 +00:00
|
|
|
{
|
|
|
|
PopulateProfileSelection();
|
|
|
|
m_profiles_combo->setCurrentIndex(m_profiles_combo->findText(profile_name));
|
|
|
|
}
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
2019-03-27 00:31:03 +00:00
|
|
|
void MappingWindow::OnSelectDevice(int)
|
2017-05-20 15:53:17 +00:00
|
|
|
{
|
2018-05-23 21:47:42 +00:00
|
|
|
if (IsMappingAllDevices())
|
|
|
|
return;
|
|
|
|
|
2019-03-15 01:27:49 +00:00
|
|
|
// Original string is stored in the "user-data".
|
|
|
|
const auto device = m_devices_combo->currentData().toString().toStdString();
|
|
|
|
|
2017-11-04 21:08:26 +00:00
|
|
|
m_controller->SetDefaultDevice(device);
|
2019-03-15 01:27:49 +00:00
|
|
|
m_controller->UpdateReferences(g_controller_interface);
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
2018-05-23 21:47:42 +00:00
|
|
|
bool MappingWindow::IsMappingAllDevices() const
|
|
|
|
{
|
|
|
|
return m_devices_combo->currentIndex() == m_devices_combo->count() - 1;
|
|
|
|
}
|
|
|
|
|
2017-05-20 15:53:17 +00:00
|
|
|
void MappingWindow::RefreshDevices()
|
2018-07-05 21:17:47 +00:00
|
|
|
{
|
2021-05-20 22:33:38 +00:00
|
|
|
g_controller_interface.RefreshDevices();
|
2018-07-05 21:17:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::OnGlobalDevicesChanged()
|
2017-05-20 15:53:17 +00:00
|
|
|
{
|
2019-04-27 15:51:57 +00:00
|
|
|
const QSignalBlocker blocker(m_devices_combo);
|
2019-03-15 01:27:49 +00:00
|
|
|
|
2017-05-20 15:53:17 +00:00
|
|
|
m_devices_combo->clear();
|
|
|
|
|
2019-03-15 01:27:49 +00:00
|
|
|
for (const auto& name : g_controller_interface.GetAllDeviceStrings())
|
|
|
|
{
|
|
|
|
const auto qname = QString::fromStdString(name);
|
|
|
|
m_devices_combo->addItem(qname, qname);
|
|
|
|
}
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2019-03-15 01:27:49 +00:00
|
|
|
m_devices_combo->insertSeparator(m_devices_combo->count());
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2019-03-15 01:27:49 +00:00
|
|
|
const auto default_device = m_controller->GetDefaultDevice().ToString();
|
2017-05-20 15:53:17 +00:00
|
|
|
|
2019-03-15 01:27:49 +00:00
|
|
|
if (!default_device.empty())
|
|
|
|
{
|
|
|
|
const auto default_device_index =
|
|
|
|
m_devices_combo->findText(QString::fromStdString(default_device));
|
|
|
|
|
|
|
|
if (default_device_index != -1)
|
|
|
|
{
|
|
|
|
m_devices_combo->setCurrentIndex(default_device_index);
|
|
|
|
}
|
|
|
|
else
|
2017-07-21 06:06:02 +00:00
|
|
|
{
|
2019-03-15 01:27:49 +00:00
|
|
|
// Selected device is not currently attached.
|
|
|
|
const auto qname = QString::fromStdString(default_device);
|
2019-07-30 11:57:06 +00:00
|
|
|
m_devices_combo->addItem(QLatin1Char{'['} + tr("disconnected") + QStringLiteral("] ") + qname,
|
|
|
|
qname);
|
2019-03-15 01:27:49 +00:00
|
|
|
m_devices_combo->setCurrentIndex(m_devices_combo->count() - 1);
|
2017-07-21 06:06:02 +00:00
|
|
|
}
|
2019-03-15 01:27:49 +00:00
|
|
|
}
|
2017-05-21 12:47:35 +00:00
|
|
|
|
2019-03-15 01:27:49 +00:00
|
|
|
m_devices_combo->addItem(tr("All devices"));
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 21:31:17 +00:00
|
|
|
void MappingWindow::SetMappingType(MappingWindow::Type type)
|
2017-05-20 15:53:17 +00:00
|
|
|
{
|
|
|
|
MappingWidget* widget;
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
2021-07-04 11:17:32 +00:00
|
|
|
case Type::MAPPING_GC_GBA:
|
|
|
|
widget = new GBAPadEmu(this);
|
2021-10-05 13:53:23 +00:00
|
|
|
setWindowTitle(tr("Game Boy Advance at Port %1").arg(GetPort() + 1));
|
|
|
|
AddWidget(tr("Game Boy Advance"), widget);
|
2021-07-04 11:17:32 +00:00
|
|
|
break;
|
2017-05-20 15:53:17 +00:00
|
|
|
case Type::MAPPING_GC_KEYBOARD:
|
|
|
|
widget = new GCKeyboardEmu(this);
|
|
|
|
setWindowTitle(tr("GameCube Keyboard at Port %1").arg(GetPort() + 1));
|
2021-07-04 11:17:32 +00:00
|
|
|
AddWidget(tr("GameCube Keyboard"), widget);
|
2017-05-20 15:53:17 +00:00
|
|
|
break;
|
|
|
|
case Type::MAPPING_GC_BONGOS:
|
|
|
|
case Type::MAPPING_GC_STEERINGWHEEL:
|
|
|
|
case Type::MAPPING_GC_DANCEMAT:
|
|
|
|
case Type::MAPPING_GCPAD:
|
|
|
|
widget = new GCPadEmu(this);
|
|
|
|
setWindowTitle(tr("GameCube Controller at Port %1").arg(GetPort() + 1));
|
|
|
|
AddWidget(tr("GameCube Controller"), widget);
|
|
|
|
break;
|
2018-01-04 18:38:09 +00:00
|
|
|
case Type::MAPPING_GC_MICROPHONE:
|
|
|
|
widget = new GCMicrophone(this);
|
|
|
|
setWindowTitle(tr("GameCube Microphone Slot %1")
|
2019-07-30 11:57:06 +00:00
|
|
|
.arg(GetPort() == 0 ? QLatin1Char{'A'} : QLatin1Char{'B'}));
|
2018-01-04 18:38:09 +00:00
|
|
|
AddWidget(tr("Microphone"), widget);
|
|
|
|
break;
|
2017-05-20 15:53:17 +00:00
|
|
|
case Type::MAPPING_WIIMOTE_EMU:
|
|
|
|
{
|
|
|
|
auto* extension = new WiimoteEmuExtension(this);
|
2019-11-04 03:19:33 +00:00
|
|
|
auto* extension_motion_input = new WiimoteEmuExtensionMotionInput(this);
|
|
|
|
auto* extension_motion_simulation = new WiimoteEmuExtensionMotionSimulation(this);
|
2017-05-20 15:53:17 +00:00
|
|
|
widget = new WiimoteEmuGeneral(this, extension);
|
2017-07-23 10:58:32 +00:00
|
|
|
setWindowTitle(tr("Wii Remote %1").arg(GetPort() + 1));
|
2017-05-20 15:53:17 +00:00
|
|
|
AddWidget(tr("General and Options"), widget);
|
2019-09-06 15:09:30 +00:00
|
|
|
AddWidget(tr("Motion Simulation"), new WiimoteEmuMotionControl(this));
|
|
|
|
AddWidget(tr("Motion Input"), new WiimoteEmuMotionControlIMU(this));
|
2017-05-20 15:53:17 +00:00
|
|
|
AddWidget(tr("Extension"), extension);
|
2019-11-04 03:19:33 +00:00
|
|
|
m_extension_motion_simulation_tab =
|
|
|
|
AddWidget(EXTENSION_MOTION_SIMULATION_TAB_NAME, extension_motion_simulation);
|
|
|
|
m_extension_motion_input_tab =
|
|
|
|
AddWidget(EXTENSION_MOTION_INPUT_TAB_NAME, extension_motion_input);
|
2020-01-14 00:29:37 +00:00
|
|
|
// Hide tabs by default. "Nunchuk" selection triggers an event to show them.
|
|
|
|
ShowExtensionMotionTabs(false);
|
2017-05-20 15:53:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-06-06 11:49:49 +00:00
|
|
|
case Type::MAPPING_HOTKEYS:
|
|
|
|
{
|
|
|
|
widget = new HotkeyGeneral(this);
|
|
|
|
AddWidget(tr("General"), widget);
|
2018-09-08 13:58:15 +00:00
|
|
|
// i18n: TAS is short for tool-assisted speedrun. Read http://tasvideos.org/ for details.
|
|
|
|
// Frame advance is an example of a typical TAS tool.
|
2017-06-06 11:49:49 +00:00
|
|
|
AddWidget(tr("TAS Tools"), new HotkeyTAS(this));
|
2018-05-13 22:12:31 +00:00
|
|
|
|
2018-06-03 11:40:57 +00:00
|
|
|
AddWidget(tr("Debugging"), new HotkeyDebugging(this));
|
2018-05-13 22:12:31 +00:00
|
|
|
|
2017-07-23 10:58:32 +00:00
|
|
|
AddWidget(tr("Wii and Wii Remote"), new HotkeyWii(this));
|
2018-05-24 03:20:58 +00:00
|
|
|
AddWidget(tr("Controller Profile"), new HotkeyControllerProfile(this));
|
2017-06-06 11:49:49 +00:00
|
|
|
AddWidget(tr("Graphics"), new HotkeyGraphics(this));
|
2018-09-08 13:58:15 +00:00
|
|
|
// i18n: Stereoscopic 3D
|
2017-06-06 11:49:49 +00:00
|
|
|
AddWidget(tr("3D"), new Hotkey3D(this));
|
2017-07-23 10:58:32 +00:00
|
|
|
AddWidget(tr("Save and Load State"), new HotkeyStates(this));
|
2018-06-03 10:41:58 +00:00
|
|
|
AddWidget(tr("Other State Management"), new HotkeyStatesOther(this));
|
2021-10-05 13:53:23 +00:00
|
|
|
AddWidget(tr("Game Boy Advance"), new HotkeyGBA(this));
|
2017-06-06 11:49:49 +00:00
|
|
|
setWindowTitle(tr("Hotkey Settings"));
|
|
|
|
break;
|
|
|
|
}
|
2020-06-12 05:27:34 +00:00
|
|
|
case Type::MAPPING_FREELOOK:
|
|
|
|
{
|
|
|
|
widget = new FreeLookGeneral(this);
|
|
|
|
AddWidget(tr("General"), widget);
|
2021-03-11 06:41:15 +00:00
|
|
|
AddWidget(tr("Rotation"), new FreeLookRotation(this));
|
2020-06-12 05:27:34 +00:00
|
|
|
setWindowTitle(tr("Free Look Controller %1").arg(GetPort() + 1));
|
|
|
|
}
|
|
|
|
break;
|
2017-05-20 15:53:17 +00:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
widget->LoadSettings();
|
|
|
|
|
|
|
|
m_config = widget->GetConfig();
|
|
|
|
|
2017-11-03 21:47:36 +00:00
|
|
|
m_controller = m_config->GetController(GetPort());
|
2017-06-09 20:00:19 +00:00
|
|
|
|
2020-09-09 02:26:13 +00:00
|
|
|
PopulateProfileSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MappingWindow::PopulateProfileSelection()
|
|
|
|
{
|
|
|
|
m_profiles_combo->clear();
|
|
|
|
|
2017-11-03 21:47:36 +00:00
|
|
|
const std::string profiles_path =
|
2018-04-30 07:09:27 +00:00
|
|
|
File::GetUserPath(D_CONFIG_IDX) + PROFILES_DIR + m_config->GetProfileName();
|
2017-11-03 21:47:36 +00:00
|
|
|
for (const auto& filename : Common::DoFileSearch({profiles_path}, {".ini"}))
|
|
|
|
{
|
|
|
|
std::string basename;
|
|
|
|
SplitPath(filename, nullptr, &basename, nullptr);
|
2021-01-17 18:47:49 +00:00
|
|
|
if (!basename.empty()) // Ignore files with an empty name to avoid multiple problems
|
|
|
|
m_profiles_combo->addItem(QString::fromStdString(basename), QString::fromStdString(filename));
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
2020-09-09 02:26:13 +00:00
|
|
|
|
|
|
|
m_profiles_combo->insertSeparator(m_profiles_combo->count());
|
|
|
|
|
|
|
|
const std::string builtin_profiles_path =
|
|
|
|
File::GetSysDirectory() + PROFILES_DIR + m_config->GetProfileName();
|
|
|
|
for (const auto& filename : Common::DoFileSearch({builtin_profiles_path}, {".ini"}))
|
|
|
|
{
|
|
|
|
std::string basename;
|
|
|
|
SplitPath(filename, nullptr, &basename, nullptr);
|
2021-01-17 18:47:49 +00:00
|
|
|
if (!basename.empty())
|
|
|
|
{
|
|
|
|
// i18n: "Stock" refers to input profiles included with Dolphin
|
|
|
|
m_profiles_combo->addItem(tr("%1 (Stock)").arg(QString::fromStdString(basename)),
|
|
|
|
QString::fromStdString(filename));
|
|
|
|
}
|
2020-09-09 02:26:13 +00:00
|
|
|
}
|
|
|
|
|
2019-04-28 19:00:35 +00:00
|
|
|
m_profiles_combo->setCurrentIndex(-1);
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
2019-11-04 03:19:33 +00:00
|
|
|
QWidget* MappingWindow::AddWidget(const QString& name, QWidget* widget)
|
2017-05-20 15:53:17 +00:00
|
|
|
{
|
2019-11-04 03:19:33 +00:00
|
|
|
QWidget* wrapper = GetWrappedWidget(widget, this, 150, 210);
|
|
|
|
m_tab_widget->addTab(wrapper, name);
|
|
|
|
return wrapper;
|
2017-05-20 15:53:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int MappingWindow::GetPort() const
|
|
|
|
{
|
|
|
|
return m_port;
|
|
|
|
}
|
|
|
|
|
2017-06-13 15:16:41 +00:00
|
|
|
ControllerEmu::EmulatedController* MappingWindow::GetController() const
|
|
|
|
{
|
|
|
|
return m_controller;
|
|
|
|
}
|
|
|
|
|
2017-06-13 23:57:05 +00:00
|
|
|
void MappingWindow::OnDefaultFieldsPressed()
|
|
|
|
{
|
|
|
|
m_controller->LoadDefaults(g_controller_interface);
|
|
|
|
m_controller->UpdateReferences(g_controller_interface);
|
2020-01-18 17:19:32 +00:00
|
|
|
|
2020-02-23 15:34:31 +00:00
|
|
|
const auto lock = GetController()->GetStateLock();
|
2019-03-15 01:27:49 +00:00
|
|
|
emit ConfigChanged();
|
2018-05-02 14:55:42 +00:00
|
|
|
emit Save();
|
2017-06-13 23:57:05 +00:00
|
|
|
}
|
2018-04-01 14:25:34 +00:00
|
|
|
|
2019-02-06 23:40:58 +00:00
|
|
|
void MappingWindow::OnClearFieldsPressed()
|
|
|
|
{
|
|
|
|
// Loading an empty inifile section clears everything.
|
|
|
|
IniFile::Section sec;
|
2019-03-15 01:27:49 +00:00
|
|
|
|
|
|
|
// Keep the currently selected device.
|
|
|
|
const auto default_device = m_controller->GetDefaultDevice();
|
2019-02-06 23:40:58 +00:00
|
|
|
m_controller->LoadConfig(&sec);
|
2019-03-15 01:27:49 +00:00
|
|
|
m_controller->SetDefaultDevice(default_device);
|
|
|
|
|
2019-02-06 23:40:58 +00:00
|
|
|
m_controller->UpdateReferences(g_controller_interface);
|
2020-01-18 17:19:32 +00:00
|
|
|
|
2020-02-23 15:34:31 +00:00
|
|
|
const auto lock = GetController()->GetStateLock();
|
2019-03-15 01:27:49 +00:00
|
|
|
emit ConfigChanged();
|
2019-02-06 23:40:58 +00:00
|
|
|
emit Save();
|
|
|
|
}
|
2019-11-04 03:19:33 +00:00
|
|
|
|
|
|
|
void MappingWindow::ShowExtensionMotionTabs(bool show)
|
|
|
|
{
|
|
|
|
if (show)
|
|
|
|
{
|
|
|
|
m_tab_widget->addTab(m_extension_motion_simulation_tab, EXTENSION_MOTION_SIMULATION_TAB_NAME);
|
|
|
|
m_tab_widget->addTab(m_extension_motion_input_tab, EXTENSION_MOTION_INPUT_TAB_NAME);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_tab_widget->removeTab(5);
|
|
|
|
m_tab_widget->removeTab(4);
|
|
|
|
}
|
|
|
|
}
|