diff options
Diffstat (limited to 'src/settingsdialog.cpp')
| -rw-r--r-- | src/settingsdialog.cpp | 649 |
1 files changed, 68 insertions, 581 deletions
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 95e4ceb0..daccfba9 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -18,115 +18,85 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ #include "settingsdialog.h" - #include "ui_settingsdialog.h" -#include "ui_nexusmanualkey.h" -#include "categoriesdialog.h" -#include "helper.h" -#include "noeditdelegate.h" -#include "iplugingame.h" -#include "settings.h" -#include "instancemanager.h" -#include "nexusinterface.h" -#include "nxmaccessmanager.h" -#include "plugincontainer.h" +#include "settingsdialogdiagnostics.h" +#include "settingsdialoggeneral.h" +#include "settingsdialognexus.h" +#include "settingsdialogpaths.h" +#include "settingsdialogplugins.h" +#include "settingsdialogsteam.h" +#include "settingsdialogworkarounds.h" -#include <boost/uuid/uuid_generators.hpp> -#include <boost/uuid/uuid_io.hpp> +using namespace MOBase; -#include <QDirIterator> -#include <QFileDialog> -#include <QMessageBox> -#include <QShortcut> -#include <QColorDialog> -#include <QInputDialog> -#include <QJsonDocument> -#include <QDesktopServices> +SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& settings, QWidget *parent) + : TutorableDialog("SettingsDialog", parent) + , ui(new Ui::SettingsDialog) + , m_settings(settings) + , m_exit(Exit::None) + , m_pluginContainer(pluginContainer) +{ + ui->setupUi(this); -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> + m_tabs.push_back(std::unique_ptr<SettingsTab>(new GeneralSettingsTab(settings, *this))); + m_tabs.push_back(std::unique_ptr<SettingsTab>(new PathsSettingsTab(settings, *this))); + m_tabs.push_back(std::unique_ptr<SettingsTab>(new DiagnosticsSettingsTab(settings, *this))); + m_tabs.push_back(std::unique_ptr<SettingsTab>(new NexusSettingsTab(settings, *this))); + m_tabs.push_back(std::unique_ptr<SettingsTab>(new SteamSettingsTab(settings, *this))); + m_tabs.push_back(std::unique_ptr<SettingsTab>(new PluginsSettingsTab(settings, *this))); + m_tabs.push_back(std::unique_ptr<SettingsTab>(new WorkaroundsSettingsTab(settings, *this))); +} +PluginContainer* SettingsDialog::pluginContainer() +{ + return m_pluginContainer; +} -using namespace MOBase; +QWidget* SettingsDialog::parentWidgetForDialogs() +{ + if (isVisible()) { + return this; + } else { + return parentWidget(); + } +} +void SettingsDialog::setExitNeeded(ExitFlags e) +{ + m_exit = e; +} -class NexusManualKeyDialog : public QDialog +ExitFlags SettingsDialog::exitNeeded() const { -public: - NexusManualKeyDialog(QWidget* parent) - : QDialog(parent), ui(new Ui::NexusManualKeyDialog) - { - ui->setupUi(this); + return m_exit; +} - connect(ui->openBrowser, &QPushButton::clicked, [&]{ openBrowser(); }); - connect(ui->paste, &QPushButton::clicked, [&]{ paste(); }); - connect(ui->clear, &QPushButton::clicked, [&]{ clear(); }); - } +int SettingsDialog::exec() +{ + GeometrySaver gs(m_settings, this); - void accept() override - { - m_key = ui->key->toPlainText(); - QDialog::accept(); - } + m_settings.widgets().restoreIndex(ui->tabWidget); - const QString& key() const - { - return m_key; - } + auto ret = TutorableDialog::exec(); - void openBrowser() - { - shell::OpenLink(QUrl("https://www.nexusmods.com/users/myaccount?tab=api")); - } + m_settings.widgets().saveIndex(ui->tabWidget); - void paste() - { - const auto text = QApplication::clipboard()->text(); - if (!text.isEmpty()) { - ui->key->setPlainText(text); + if (ret == QDialog::Accepted) { + for (auto&& tab : m_tabs) { + tab->closing(); } - } - void clear() - { - ui->key->clear(); + // update settings for each tab + for (std::unique_ptr<SettingsTab> const &tab: m_tabs) { + tab->update(); + } } -private: - std::unique_ptr<Ui::NexusManualKeyDialog> ui; - QString m_key; -}; - - -SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings* settings, QWidget *parent) - : TutorableDialog("SettingsDialog", parent) - , ui(new Ui::SettingsDialog) - , m_settings(settings) - , m_PluginContainer(pluginContainer) - , m_nexusLogin(new QWebSocket) - , m_KeyReceived(false) - , m_KeyCleared(false) - , m_GeometriesReset(false) -{ - ui->setupUi(this); - ui->pluginSettingsList->setStyleSheet("QTreeWidget::item {padding-right: 10px;}"); - - QShortcut *delShortcut - = new QShortcut(QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); - connect(delShortcut, SIGNAL(activated()), this, SLOT(deleteBlacklistItem())); - connect(m_nexusLogin, SIGNAL(connected()), this, SLOT(dispatchLogin())); - connect(m_nexusLogin, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(authError(QAbstractSocket::SocketError))); - connect(m_nexusLogin, SIGNAL(textMessageReceived(const QString &)), this, SLOT(receiveApiKey(const QString &))); - connect(m_nexusLogin, SIGNAL(disconnected()), this, SLOT(completeApiConnection())); - m_loginTimer.callOnTimeout(this, &SettingsDialog::loginPing); - - updateNexusButtons(); + return ret; } SettingsDialog::~SettingsDialog() { - m_loginTimer.stop(); - m_nexusLogin->close(); disconnect(this); delete ui; } @@ -141,23 +111,6 @@ QString SettingsDialog::getColoredButtonStyleSheet() const "}"); } -void SettingsDialog::setButtonColor(QPushButton *button, const QColor &color) -{ - button->setStyleSheet( - QString("QPushButton {" - "background-color: rgba(%1, %2, %3, %4);" - "color: %5;" - "border: 1px solid;" - "padding: 3px;" - "}") - .arg(color.red()) - .arg(color.green()) - .arg(color.blue()) - .arg(color.alpha()) - .arg(Settings::getIdealTextColor(color).name()) - ); -}; - void SettingsDialog::accept() { QString newModPath = ui->modDirEdit->text(); @@ -165,9 +118,9 @@ void SettingsDialog::accept() if ((QDir::fromNativeSeparators(newModPath) != QDir::fromNativeSeparators( - Settings::instance().getModDirectory(true))) && + Settings::instance().paths().mods(true))) && (QMessageBox::question( - nullptr, tr("Confirm"), + parentWidgetForDialogs(), tr("Confirm"), tr("Changing the mod directory affects all your profiles! " "Mods not present (or named differently) in the new location " "will be disabled in all profiles. " @@ -177,494 +130,28 @@ void SettingsDialog::accept() return; } - storeSettings(ui->pluginsList->currentItem()); TutorableDialog::accept(); } -bool SettingsDialog::getResetGeometries() -{ - return ui->resetGeometryBtn->isChecked(); -} - -bool SettingsDialog::getApiKeyChanged() -{ - return m_KeyReceived || m_KeyCleared; -} - -void SettingsDialog::on_categoriesBtn_clicked() -{ - CategoriesDialog dialog(this); - if (dialog.exec() == QDialog::Accepted) { - dialog.commitChanges(); - } -} - -void SettingsDialog::on_execBlacklistBtn_clicked() -{ - bool ok = false; - QString result = QInputDialog::getMultiLineText( - this, - tr("Executables Blacklist"), - tr("Enter one executable per line to be blacklisted from the virtual file system.\n" - "Mods and other virtualized files will not be visible to these executables and\n" - "any executables launched by them.\n\n" - "Example:\n" - " Chrome.exe\n" - " Firefox.exe"), - m_ExecutableBlacklist.split(";").join("\n"), - &ok - ); - if (ok) { - QStringList blacklist; - for (auto exec : result.split("\n")) { - if (exec.trimmed().endsWith(".exe", Qt::CaseInsensitive)) { - blacklist << exec.trimmed(); - } - } - m_ExecutableBlacklist = blacklist.join(";"); - } -} - -void SettingsDialog::on_bsaDateBtn_clicked() -{ - IPluginGame const *game - = qApp->property("managed_game").value<IPluginGame *>(); - QDir dir = game->dataDirectory(); - - Helper::backdateBSAs(qApp->applicationDirPath().toStdWString(), - dir.absolutePath().toStdWString()); -} - -void SettingsDialog::on_browseBaseDirBtn_clicked() -{ - QString temp = QFileDialog::getExistingDirectory( - this, tr("Select base directory"), ui->baseDirEdit->text()); - if (!temp.isEmpty()) { - ui->baseDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseDownloadDirBtn_clicked() -{ - QString searchPath = ui->downloadDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - - QString temp = QFileDialog::getExistingDirectory(this, tr("Select download directory"), searchPath); - if (!temp.isEmpty()) { - ui->downloadDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseModDirBtn_clicked() -{ - QString searchPath = ui->modDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - - QString temp = QFileDialog::getExistingDirectory(this, tr("Select mod directory"), searchPath); - if (!temp.isEmpty()) { - ui->modDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseCacheDirBtn_clicked() -{ - QString searchPath = ui->cacheDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - - QString temp = QFileDialog::getExistingDirectory(this, tr("Select cache directory"), searchPath); - if (!temp.isEmpty()) { - ui->cacheDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseProfilesDirBtn_clicked() -{ - QString searchPath = ui->profilesDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - - QString temp = QFileDialog::getExistingDirectory(this, tr("Select profiles directory"), searchPath); - if (!temp.isEmpty()) { - ui->profilesDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseOverwriteDirBtn_clicked() -{ - QString searchPath = ui->overwriteDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - - QString temp = QFileDialog::getExistingDirectory(this, tr("Select overwrite directory"), searchPath); - if (!temp.isEmpty()) { - ui->overwriteDirEdit->setText(temp); - } -} - -void SettingsDialog::on_browseGameDirBtn_clicked() -{ - QFileInfo oldGameExe(ui->managedGameDirEdit->text()); - - QString temp = QFileDialog::getOpenFileName(this, tr("Select game executable"), oldGameExe.absolutePath(), oldGameExe.fileName()); - if (!temp.isEmpty()) { - ui->managedGameDirEdit->setText(temp); - } -} - -void SettingsDialog::on_containsBtn_clicked() -{ - QColor result = QColorDialog::getColor(m_ContainsColor, this, "Color Picker: Mod contains selected plugin", QColorDialog::ShowAlphaChannel); - if (result.isValid()) { - m_ContainsColor = result; - setButtonColor(ui->containsBtn, result); - } -} - -void SettingsDialog::on_containedBtn_clicked() -{ - QColor result = QColorDialog::getColor(m_ContainedColor, this, "ColorPicker: Plugin is Contained in selected Mod", QColorDialog::ShowAlphaChannel); - if (result.isValid()) { - m_ContainedColor = result; - setButtonColor(ui->containedBtn, result); - } -} - -void SettingsDialog::on_overwrittenBtn_clicked() -{ - QColor result = QColorDialog::getColor(m_OverwrittenColor, this, "ColorPicker: Is overwritten (loose files)", QColorDialog::ShowAlphaChannel); - if (result.isValid()) { - m_OverwrittenColor = result; - setButtonColor(ui->overwrittenBtn, result); - } -} - -void SettingsDialog::on_overwritingBtn_clicked() -{ - QColor result = QColorDialog::getColor(m_OverwritingColor, this, "ColorPicker: Is overwriting (loose files)", QColorDialog::ShowAlphaChannel); - if (result.isValid()) { - m_OverwritingColor = result; - setButtonColor(ui->overwritingBtn, result); - } -} - -void SettingsDialog::on_overwrittenArchiveBtn_clicked() -{ - QColor result = QColorDialog::getColor(m_OverwrittenArchiveColor, this, "ColorPicker: Is overwritten (archive files)", QColorDialog::ShowAlphaChannel); - if (result.isValid()) { - m_OverwrittenArchiveColor = result; - setButtonColor(ui->overwrittenArchiveBtn, result); - } -} - -void SettingsDialog::on_overwritingArchiveBtn_clicked() -{ - QColor result = QColorDialog::getColor(m_OverwritingArchiveColor, this, "ColorPicker: Is overwriting (archive files)", QColorDialog::ShowAlphaChannel); - if (result.isValid()) { - m_OverwritingArchiveColor = result; - setButtonColor(ui->overwritingArchiveBtn, result); - } -} - -void SettingsDialog::on_resetColorsBtn_clicked() -{ - m_OverwritingColor = QColor(255, 0, 0, 64); - m_OverwrittenColor = QColor(0, 255, 0, 64); - m_OverwritingArchiveColor = QColor(255, 0, 255, 64); - m_OverwrittenArchiveColor = QColor(0, 255, 255, 64); - m_ContainsColor = QColor(0, 0, 255, 64); - m_ContainedColor = QColor(0, 0, 255, 64); - - setButtonColor(ui->overwritingBtn, m_OverwritingColor); - setButtonColor(ui->overwrittenBtn, m_OverwrittenColor); - setButtonColor(ui->overwritingArchiveBtn, m_OverwritingArchiveColor); - setButtonColor(ui->overwrittenArchiveBtn, m_OverwrittenArchiveColor); - setButtonColor(ui->containsBtn, m_ContainsColor); - setButtonColor(ui->containedBtn, m_ContainedColor); -} - -void SettingsDialog::on_resetDialogsButton_clicked() -{ - if (QMessageBox::question(this, tr("Confirm?"), - tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - emit resetDialogs(); - } -} - -void SettingsDialog::on_nexusConnect_clicked() -{ - fetchNexusApiKey(); -} - -void SettingsDialog::on_nexusManualKey_clicked() -{ - NexusManualKeyDialog dialog(this); - - if (dialog.exec() != QDialog::Accepted) { - return; - } - - const auto key = dialog.key(); - - if (key.isEmpty()) { - clearKey(); - } else { - if (setKey(key)) { - testApiKey(); - } - } -} - -void SettingsDialog::fetchNexusApiKey() -{ - QUrl url = QUrl("wss://sso.nexusmods.com"); - m_nexusLogin->open(url); - updateNexusButtons(); -} - -void SettingsDialog::dispatchLogin() -{ - m_KeyReceived = false; - QJsonObject login; - if (m_UUID.isEmpty()) { - boost::uuids::random_generator generator; - boost::uuids::uuid sessionId = generator(); - m_UUID = boost::uuids::to_string(sessionId).c_str(); - } - login.insert(QString("id"), QJsonValue(m_UUID)); - login.insert(QString("token"), QJsonValue(m_AuthToken)); - login.insert(QString("protocol"), 2); - QJsonDocument loginDoc(login); - QString finalMessage(loginDoc.toJson()); - m_nexusLogin->sendTextMessage(finalMessage); - QDesktopServices::openUrl(QUrl(QString("https://www.nexusmods.com/sso?id=%1&application=%2").arg(m_UUID).arg("modorganizer2"))); - m_loginTimer.start(30000); -} -void SettingsDialog::loginPing() +SettingsTab::SettingsTab(Settings& s, SettingsDialog& d) + : ui(d.ui), m_settings(s), m_dialog(d) { - if (m_nexusLogin->isValid()) { - m_nexusLogin->ping(); - m_totalPings++; - } - if (m_totalPings >= 60) { - m_loginTimer.stop(); - m_totalPings = 0; - m_nexusLogin->close(QWebSocketProtocol::CloseCodeGoingAway, "Timeout: No response received after thirty minutes. Cancelling request."); - } -} - -void SettingsDialog::authError(QAbstractSocket::SocketError error) -{ - auto errorInfo = m_nexusLogin->errorString(); - qCritical() << "An error occurred: " << errorInfo; -} - -void SettingsDialog::receiveApiKey(const QString &response) -{ - QJsonDocument responseDoc = QJsonDocument::fromJson(response.toUtf8()); - QVariantMap responseData = responseDoc.object().toVariantMap(); - if (responseData["success"].toBool()) { - QVariantMap data = responseData["data"].toMap(); - if (data.contains("connection_token")) { - m_AuthToken = data["connection_token"].toString(); - } else { - const auto key = data["api_key"].toString(); - - m_nexusLogin->close(); - m_loginTimer.stop(); - m_totalPings = 0; - - if (key.isEmpty()) { - clearKey(); - } else { - setKey(key); - } - } - } else { - QString error("There was a problem with SSO initialization: %1"); - qCritical() << error.arg(responseData["error"].toString()); - m_nexusLogin->close(); - } -} - -void SettingsDialog::completeApiConnection() -{ - if (!m_KeyReceived && !m_loginTimer.isActive()) { - QMessageBox::warning(qApp->activeWindow(), tr("Error"), - tr("Failed to retrieve a Nexus API key! Please try again. " - "A browser window should open asking you to authorize.")); - - // try again - fetchNexusApiKey(); - } -} - -bool SettingsDialog::setKey(const QString& key) -{ - m_KeyReceived = true; - const bool ret = m_settings->setNexusApiKey(key); - updateNexusButtons(); - return ret; -} - -bool SettingsDialog::clearKey() -{ - m_KeyCleared = true; - const auto ret = m_settings->clearNexusApiKey(); - updateNexusButtons(); - - NexusInterface::instance(m_PluginContainer)->getAccessManager()->clearApiKey(); - - return ret; } -void SettingsDialog::testApiKey() -{ - QString key; - if (!m_settings->getNexusApiKey(key)) { - qWarning().nospace() << "can't test API key, nothing stored"; - return; - } - - NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(key, true); -} - -void SettingsDialog::updateNexusButtons() -{ - if (m_nexusLogin->state() != QAbstractSocket::UnconnectedState) { - // api key is in the process of being retrieved - ui->nexusConnect->setText("Connecting the API. Please login within the browser and accept the request. This will time out after 30 minutes."); - ui->nexusConnect->setEnabled(false); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setEnabled(false); - } - else if (m_settings->hasNexusApiKey()) { - // api key is present - ui->nexusConnect->setText("Nexus API Key Stored"); - ui->nexusConnect->setEnabled(false); - ui->nexusDisconnect->setEnabled(true); - ui->nexusManualKey->setEnabled(false); - } else { - // api key not present - ui->nexusConnect->setText("Connect to Nexus"); - ui->nexusConnect->setEnabled(true); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setEnabled(true); - } -} - -void SettingsDialog::storeSettings(QListWidgetItem *pluginItem) -{ - if (pluginItem != nullptr) { - QVariantMap settings = pluginItem->data(Qt::UserRole + 1).toMap(); - - for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) { - const QTreeWidgetItem *item = ui->pluginSettingsList->topLevelItem(i); - settings[item->text(0)] = item->data(1, Qt::DisplayRole); - } - - pluginItem->setData(Qt::UserRole + 1, settings); - } -} - -void SettingsDialog::on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) -{ - storeSettings(previous); - - ui->pluginSettingsList->clear(); - IPlugin *plugin = static_cast<IPlugin*>(current->data(Qt::UserRole).value<void*>()); - ui->authorLabel->setText(plugin->author()); - ui->versionLabel->setText(plugin->version().canonicalString()); - ui->descriptionLabel->setText(plugin->description()); - - QVariantMap settings = current->data(Qt::UserRole + 1).toMap(); - QVariantMap descriptions = current->data(Qt::UserRole + 2).toMap(); - ui->pluginSettingsList->setEnabled(settings.count() != 0); - for (auto iter = settings.begin(); iter != settings.end(); ++iter) { - QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(iter.key())); - QVariant value = *iter; - QString description; - { - auto descriptionIter = descriptions.find(iter.key()); - if (descriptionIter != descriptions.end()) { - description = descriptionIter->toString(); - } - } - - ui->pluginSettingsList->setItemDelegateForColumn(0, new NoEditDelegate()); - newItem->setData(1, Qt::DisplayRole, value); - newItem->setData(1, Qt::EditRole, value); - newItem->setToolTip(1, description); - - newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); - ui->pluginSettingsList->addTopLevelItem(newItem); - } - - ui->pluginSettingsList->resizeColumnToContents(0); - ui->pluginSettingsList->resizeColumnToContents(1); -} - -void SettingsDialog::deleteBlacklistItem() -{ - ui->pluginBlacklist->takeItem(ui->pluginBlacklist->currentIndex().row()); -} - -void SettingsDialog::on_associateButton_clicked() -{ - Settings::instance().registerAsNXMHandler(true); -} - -void SettingsDialog::on_clearCacheButton_clicked() -{ - QDir(Settings::instance().getCacheDirectory()).removeRecursively(); - NexusInterface::instance(m_PluginContainer)->clearCache(); -} - -void SettingsDialog::on_nexusDisconnect_clicked() -{ - clearKey(); -} - -void SettingsDialog::normalizePath(QLineEdit *lineEdit) -{ - QString text = lineEdit->text(); - while (text.endsWith('/') || text.endsWith('\\')) { - text.chop(1); - } - lineEdit->setText(text); -} - -void SettingsDialog::on_baseDirEdit_editingFinished() -{ - normalizePath(ui->baseDirEdit); -} - -void SettingsDialog::on_downloadDirEdit_editingFinished() -{ - normalizePath(ui->downloadDirEdit); -} - -void SettingsDialog::on_modDirEdit_editingFinished() -{ - normalizePath(ui->modDirEdit); -} - -void SettingsDialog::on_cacheDirEdit_editingFinished() -{ - normalizePath(ui->cacheDirEdit); -} +SettingsTab::~SettingsTab() = default; -void SettingsDialog::on_profilesDirEdit_editingFinished() +Settings& SettingsTab::settings() { - normalizePath(ui->profilesDirEdit); + return m_settings; } -void SettingsDialog::on_overwriteDirEdit_editingFinished() +SettingsDialog& SettingsTab::dialog() { - normalizePath(ui->overwriteDirEdit); + return m_dialog; } -void SettingsDialog::on_resetGeometryBtn_clicked() +QWidget* SettingsTab::parentWidget() { - m_GeometriesReset = true; - ui->resetGeometryBtn->setChecked(true); + return m_dialog.parentWidgetForDialogs(); } |
