/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 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 .
*/
#include "PrecompiledHeader.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/StringUtil.h"
#include "pcsx2/Frontend/GameList.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/INISettingsInterface.h"
#include "MainWindow.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "SettingsDialog.h"
#include "AdvancedSystemSettingsWidget.h"
#include "AudioSettingsWidget.h"
#include "BIOSSettingsWidget.h"
#include "EmulationSettingsWidget.h"
#include "GameSummaryWidget.h"
#include "GameFixSettingsWidget.h"
#include "GameListSettingsWidget.h"
#include "GraphicsSettingsWidget.h"
#include "DEV9SettingsWidget.h"
#include "FolderSettingsWidget.h"
#include "HotkeySettingsWidget.h"
#include "InterfaceSettingsWidget.h"
#include "MemoryCardSettingsWidget.h"
#include "SystemSettingsWidget.h"
#ifdef ENABLE_ACHIEVEMENTS
#include "AchievementSettingsWidget.h"
#include "pcsx2/Frontend/Achievements.h"
#endif
#include
#include
static QList s_open_game_properties_dialogs;
SettingsDialog::SettingsDialog(QWidget* parent)
: QDialog(parent)
, m_game_crc(0)
{
setupUi(nullptr);
}
SettingsDialog::SettingsDialog(QWidget* parent, std::unique_ptr sif, const GameList::Entry* game, u32 game_crc)
: QDialog(parent)
, m_sif(std::move(sif))
, m_game_crc(game_crc)
{
setupUi(game);
s_open_game_properties_dialogs.push_back(this);
}
void SettingsDialog::setupUi(const GameList::Entry* game)
{
const bool show_advanced_settings = true;
m_ui.setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
if (isPerGameSettings())
{
if (game)
{
addWidget(new GameSummaryWidget(game, this, m_ui.settingsContainer), tr("Summary"), QStringLiteral("file-list-line"),
tr("SummaryEventually this will be where we can see patches and compute hashes/verify dumps/etc."));
}
m_ui.restoreDefaultsButton->setVisible(false);
}
addWidget(m_interface_settings = new InterfaceSettingsWidget(this, m_ui.settingsContainer), tr("Interface"),
QStringLiteral("settings-3-line"),
tr("Interface SettingsThese options control how the software looks and behaves.
Mouse over an option for "
"additional information."));
// We don't include game list/bios settings in per-game settings.
if (!isPerGameSettings())
{
addWidget(m_game_list_settings = new GameListSettingsWidget(this, m_ui.settingsContainer), tr("Game List"),
QStringLiteral("folder-settings-line"),
tr("Game List SettingsThe list above shows the directories which will be searched by PCSX2 to populate the game "
"list. Search directories can be added, removed, and switched to recursive/non-recursive."));
addWidget(m_bios_settings = new BIOSSettingsWidget(this, m_ui.settingsContainer), tr("BIOS"), QStringLiteral("hard-drive-2-line"),
tr("BIOS SettingsConfigure your BIOS here.
Mouse over an option for additional information."));
}
// Common to both per-game and global settings.
addWidget(m_emulation_settings = new EmulationSettingsWidget(this, m_ui.settingsContainer), tr("Emulation"), QStringLiteral("dashboard-line"),
tr("Emulation SettingsThese options determine the configuration of frame pacing and game settings.
Mouse over an option for additional information."));
addWidget(m_system_settings = new SystemSettingsWidget(this, m_ui.settingsContainer), tr("System"), QStringLiteral("artboard-2-line"),
tr("System SettingsThese options determine the configuration of the simulated console.
Mouse over an option for additional information."));
if (show_advanced_settings)
{
addWidget(m_advanced_system_settings = new AdvancedSystemSettingsWidget(this, m_ui.settingsContainer), tr("Advanced System"),
QStringLiteral("artboard-2-line"), tr("Advanced System SettingsThese are Advanced options to determine the configuration of the simulated console.
Mouse over an option for additional information."));
// Only show the game fixes for per-game settings, there's really no reason to be setting them globally.
if (isPerGameSettings())
{
addWidget(m_game_fix_settings_widget = new GameFixSettingsWidget(this, m_ui.settingsContainer), tr("Game Fix"),
QStringLiteral("close-line"), tr("Game Fix SettingsGamefixes can work around incorrect emulation in some titles however they can also cause problems in games if used incorrectly. It is best to leave them all disabled unless advised otherwise."));
}
}
addWidget(m_graphics_settings = new GraphicsSettingsWidget(this, m_ui.settingsContainer), tr("Graphics"), QStringLiteral("brush-line"),
tr("Graphics SettingsThese options determine the configuration of the graphical output.
Mouse over an option for additional information."));
addWidget(m_audio_settings = new AudioSettingsWidget(this, m_ui.settingsContainer), tr("Audio"), QStringLiteral("volume-up-line"),
tr("Audio SettingsThese options control the audio output of the console.
Mouse over an option for additional information."));
// for now, memory cards aren't settable per-game
if (!isPerGameSettings())
{
addWidget(m_memory_card_settings = new MemoryCardSettingsWidget(this, m_ui.settingsContainer), tr("Memory Cards"),
QStringLiteral("sd-card-line"), tr("Memory Card SettingsCreate and configure Memory Cards here.
Mouse over an option for additional information."));
}
addWidget(m_dev9_settings = new DEV9SettingsWidget(this, m_ui.settingsContainer), tr("Network & HDD"), QStringLiteral("dashboard-line"),
tr("Network & HDD SettingsThese options control the network connectivity and internal HDD storage of the console.
"
"Mouse over an option for additional information."));
if (!isPerGameSettings())
{
addWidget(m_folder_settings = new FolderSettingsWidget(this, m_ui.settingsContainer), tr("Folders"), QStringLiteral("folder-open-line"),
tr("Folder SettingsThese options control where PCSX2 will save runtime data files."));
}
{
QString title = tr("Achievements");
QString icon_text(QStringLiteral("trophy-line"));
QString help_text = tr(
"Achievements Settings"
"These options control the RetroAchievements implementation in PCSX2, allowing you to earn achievements in your games.");
#ifdef ENABLE_ACHIEVEMENTS
if (Achievements::IsUsingRAIntegration())
{
QLabel* placeholder_label =
new QLabel(tr("RAIntegration is being used, built-in RetroAchievements support is disabled."),
m_ui.settingsContainer);
placeholder_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
addWidget(placeholder_label, std::move(title), std::move(icon_text), std::move(help_text));
}
else
{
addWidget((m_achievement_settings = new AchievementSettingsWidget(this, m_ui.settingsContainer)),
std::move(title), std::move(icon_text), std::move(help_text));
}
#else
QLabel* placeholder_label =
new QLabel(tr("This PCSX2 build was not compiled with RetroAchievements support."), m_ui.settingsContainer);
placeholder_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
addWidget(placeholder_label, std::move(title), std::move(icon_text), std::move(help_text));
#endif
}
m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
m_ui.settingsCategory->setCurrentRow(0);
m_ui.settingsContainer->setCurrentIndex(0);
m_ui.helpText->setText(m_category_help_text[0]);
connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &SettingsDialog::onCategoryCurrentRowChanged);
connect(m_ui.closeButton, &QPushButton::clicked, this, &SettingsDialog::accept);
connect(m_ui.restoreDefaultsButton, &QPushButton::clicked, this, &SettingsDialog::onRestoreDefaultsClicked);
}
SettingsDialog::~SettingsDialog()
{
if (isPerGameSettings())
s_open_game_properties_dialogs.removeOne(this);
}
void SettingsDialog::closeEvent(QCloseEvent*)
{
// we need to clean up ourselves, since we're not modal
if (isPerGameSettings())
deleteLater();
}
QString SettingsDialog::getCategory() const
{
return m_ui.settingsCategory->item(m_ui.settingsCategory->currentRow())->text();
}
void SettingsDialog::setCategory(const char* category)
{
// the titles in the category list will be translated.
const QString translated_category(qApp->translate("SettingsDialog", category));
for (int i = 0; i < m_ui.settingsCategory->count(); i++)
{
if (translated_category == m_ui.settingsCategory->item(i)->text())
{
// will also update the visible widget
m_ui.settingsCategory->setCurrentRow(i);
break;
}
}
}
void SettingsDialog::onCategoryCurrentRowChanged(int row)
{
m_ui.settingsContainer->setCurrentIndex(row);
m_ui.helpText->setText(m_category_help_text[row]);
}
void SettingsDialog::onRestoreDefaultsClicked()
{
QMessageBox msgbox(this);
msgbox.setIcon(QMessageBox::Question);
msgbox.setWindowTitle(tr("Confirm Restore Defaults"));
msgbox.setText(tr("Are you sure you want to restore the default settings? Any preferences will be lost."));
QCheckBox* ui_cb = new QCheckBox(tr("Reset UI Settings"), &msgbox);
msgbox.setCheckBox(ui_cb);
msgbox.addButton(QMessageBox::Yes);
msgbox.addButton(QMessageBox::No);
msgbox.setDefaultButton(QMessageBox::Yes);
if (msgbox.exec() != QMessageBox::Yes)
return;
g_main_window->resetSettings(ui_cb->isChecked());
}
void SettingsDialog::addWidget(QWidget* widget, QString title, QString icon, QString help_text)
{
const int index = m_ui.settingsCategory->count();
QListWidgetItem* item = new QListWidgetItem(m_ui.settingsCategory);
item->setText(title);
if (!icon.isEmpty())
item->setIcon(QIcon::fromTheme(icon));
m_ui.settingsContainer->addWidget(widget);
m_category_help_text[index] = std::move(help_text);
}
void SettingsDialog::registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text)
{
if (!object)
return;
// construct rich text with formatted description
QString full_text;
full_text += "