diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-08-03 09:31:19 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-03 09:31:19 -0400 |
| commit | a7df11a31d684cfc341fc401dc3a9449f2538084 (patch) | |
| tree | 04005ddcf6b364c0b1132e47c0490314c14726ae | |
| parent | 80dd9222f325959a26580ce2fdd1ebc4b7979517 (diff) | |
| parent | 712c8687c208629e22ef7b4d8015c899c2ea053a (diff) | |
Merge pull request #808 from isanae/settings-rework
Settings rework
| -rw-r--r-- | src/CMakeLists.txt | 21 | ||||
| -rw-r--r-- | src/loadmechanism.cpp | 274 | ||||
| -rw-r--r-- | src/loadmechanism.h | 65 | ||||
| -rw-r--r-- | src/loglist.cpp | 3 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 5 | ||||
| -rw-r--r-- | src/settings.cpp | 675 | ||||
| -rw-r--r-- | src/settings.h | 213 | ||||
| -rw-r--r-- | src/settingsdialog.cpp | 700 | ||||
| -rw-r--r-- | src/settingsdialog.h | 131 | ||||
| -rw-r--r-- | src/settingsdialog.ui | 67 | ||||
| -rw-r--r-- | src/settingsdialogdiagnostics.cpp | 48 | ||||
| -rw-r--r-- | src/settingsdialogdiagnostics.h | 18 | ||||
| -rw-r--r-- | src/settingsdialoggeneral.cpp | 250 | ||||
| -rw-r--r-- | src/settingsdialoggeneral.h | 53 | ||||
| -rw-r--r-- | src/settingsdialognexus.cpp | 363 | ||||
| -rw-r--r-- | src/settingsdialognexus.h | 41 | ||||
| -rw-r--r-- | src/settingsdialogpaths.cpp | 205 | ||||
| -rw-r--r-- | src/settingsdialogpaths.h | 33 | ||||
| -rw-r--r-- | src/settingsdialogplugins.cpp | 121 | ||||
| -rw-r--r-- | src/settingsdialogplugins.h | 21 | ||||
| -rw-r--r-- | src/settingsdialogsteam.cpp | 17 | ||||
| -rw-r--r-- | src/settingsdialogsteam.h | 17 | ||||
| -rw-r--r-- | src/settingsdialogworkarounds.cpp | 94 | ||||
| -rw-r--r-- | src/settingsdialogworkarounds.h | 25 |
24 files changed, 1515 insertions, 1945 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9785dc3d..d8316e7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,6 +37,13 @@ SET(organizer_SRCS spawn.cpp singleinstance.cpp settingsdialog.cpp + settingsdialogdiagnostics.cpp + settingsdialoggeneral.cpp + settingsdialognexus.cpp + settingsdialogpaths.cpp + settingsdialogplugins.cpp + settingsdialogsteam.cpp + settingsdialogworkarounds.cpp settings.cpp selfupdater.cpp selectiondialog.cpp @@ -151,6 +158,13 @@ SET(organizer_HDRS spawn.h singleinstance.h settingsdialog.h + settingsdialogdiagnostics.h + settingsdialoggeneral.h + settingsdialognexus.h + settingsdialogpaths.h + settingsdialogplugins.h + settingsdialogsteam.h + settingsdialogworkarounds.h settings.h selfupdater.h selectiondialog.h @@ -431,6 +445,13 @@ set(profiles set(settings settings settingsdialog + settingsdialogdiagnostics + settingsdialoggeneral + settingsdialognexus + settingsdialogpaths + settingsdialogplugins + settingsdialogsteam + settingsdialogworkarounds ) set(utilities diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp index 4d6cebd4..06e9f201 100644 --- a/src/loadmechanism.cpp +++ b/src/loadmechanism.cpp @@ -32,286 +32,20 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QCryptographicHash>
#include <QCoreApplication>
-
using namespace MOBase;
using namespace MOShared;
-
LoadMechanism::LoadMechanism()
: m_SelectedMechanism(LOAD_MODORGANIZER)
{
}
-void LoadMechanism::writeHintFile(const QDir &targetDirectory)
-{
- QString hintFilePath = targetDirectory.absoluteFilePath("mo_path.txt");
- QFile hintFile(hintFilePath);
- if (hintFile.exists()) {
- hintFile.remove();
- }
- if (!hintFile.open(QIODevice::WriteOnly)) {
- throw MyException(QObject::tr("failed to open %1: %2").arg(hintFilePath).arg(hintFile.errorString()));
- }
- hintFile.write(qApp->applicationDirPath().toUtf8().constData());
- hintFile.close();
-}
-
-
-void LoadMechanism::removeHintFile(QDir targetDirectory)
-{
- targetDirectory.remove("mo_path.txt");
-}
-
-
-bool LoadMechanism::isDirectLoadingSupported()
-{
- //FIXME: Seriously? isn't there a 'do i need steam' thing?
- IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
- if (game->gameName().compare("oblivion", Qt::CaseInsensitive) == 0) {
- // oblivion can be loaded directly if it's not the steam variant
- return !game->gameDirectory().exists("steam_api.dll");
- } else {
- // all other games work afaik
- return true;
- }
-}
-
-bool LoadMechanism::isScriptExtenderSupported()
-{
- IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
- ScriptExtender *extender = game->feature<ScriptExtender>();
-
- // test if there even is an extender for the managed game and if so whether it's installed
- return extender != nullptr && extender->isInstalled();
-}
-
-bool LoadMechanism::isProxyDLLSupported()
-{
- // using steam_api.dll as the proxy is way too game specific as many games will have different
- // versions of that dll.
- // plus: the proxy dll hasn't been working for at least the whole 1.12.x versions of MO and
- // noone reported it so why maintain an unused feature?
- return false;
-/* IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
- return game->gameDirectory().exists(QString::fromStdWString(AppConfig::proxyDLLTarget()));*/
-}
-
-
-bool LoadMechanism::hashIdentical(const QString &fileNameLHS, const QString &fileNameRHS)
+bool LoadMechanism::isDirectLoadingSupported() const
{
- QFile fileLHS(fileNameLHS);
- if (!fileLHS.open(QIODevice::ReadOnly)) {
- throw MyException(QObject::tr("file not found: %1").arg(qUtf8Printable(fileNameLHS)));
- }
- QByteArray dataLHS = fileLHS.readAll();
- QByteArray hashLHS = QCryptographicHash::hash(dataLHS, QCryptographicHash::Md5);
-
- fileLHS.close();
-
- QFile fileRHS(fileNameRHS);
- if (!fileRHS.open(QIODevice::ReadOnly)) {
- throw MyException(QObject::tr("file not found: %1").arg(qUtf8Printable(fileNameRHS)));
- }
- QByteArray dataRHS = fileRHS.readAll();
- QByteArray hashRHS = QCryptographicHash::hash(dataRHS, QCryptographicHash::Md5);
-
- fileRHS.close();
-
- return hashLHS == hashRHS;
+ return true;
}
-
-void LoadMechanism::deactivateScriptExtender()
+void LoadMechanism::activate(EMechanism)
{
- try {
- IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
- ScriptExtender *extender = game->feature<ScriptExtender>();
- if (extender == nullptr) {
- return;
- }
-
- QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->PluginPath());
-
-#pragma message("implement this for usvfs")
-
- QString vfsDLLName = "";
- if (extender->getArch() == IMAGE_FILE_MACHINE_I386) {
- vfsDLLName = ToQString(AppConfig::vfs32DLLName());
- }
- else if (extender->getArch() == IMAGE_FILE_MACHINE_AMD64)
- {
- vfsDLLName = ToQString(AppConfig::vfs64DLLName());
- }
- log::debug("USVFS DLL Name: {}", vfsDLLName);
- if (vfsDLLName != "") {
- if (QFile(pluginsDir.absoluteFilePath(vfsDLLName)).exists()) {
- // remove dll from SE plugins directory
- if (!pluginsDir.remove(vfsDLLName)) {
- throw MyException(QObject::tr("Failed to delete %1").arg(pluginsDir.absoluteFilePath(vfsDLLName)));
- }
- }
- }
-
- removeHintFile(pluginsDir);
- } catch (const std::exception &e) {
- QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate script extender loading"), e.what());
- }
+ // no-op
}
-
-
-void LoadMechanism::deactivateProxyDLL()
-{
- try {
- IPluginGame const *game = qApp->property("managed_game").value<IPluginGame *>();
-
- QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget()));
-
- QFile targetDLL(targetPath);
- if (targetDLL.exists()) {
- QString origFile = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLOrig()));
- // determine if a proxy-dll is installed
- // this is a very crude way of making this decision but it should be good enough
- if ((targetDLL.size() < 24576) && (QFile(origFile).exists())) {
- // remove proxy-dll
- if (!targetDLL.remove()) {
- throw MyException(QObject::tr("Failed to remove %1: %2").arg(targetPath).arg(targetDLL.errorString()));
- } else if (!QFile::rename(origFile, targetPath)) {
- throw MyException(QObject::tr("Failed to rename %1 to %2").arg(origFile, targetPath));
- }
- }
- }
-
- removeHintFile(game->gameDirectory());
- } catch (const std::exception &e) {
- QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate proxy-dll loading"), e.what());
- }
-}
-
-
-void LoadMechanism::activateScriptExtender()
-{
- try {
- IPluginGame const *game = qApp->property("managed_game").value<IPluginGame *>();
- ScriptExtender *extender = game->feature<ScriptExtender>();
- if (extender == nullptr) {
- return;
- }
-
- QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->PluginPath());
-
- if (!pluginsDir.exists()) {
- pluginsDir.mkpath(".");
- }
-
-#pragma message("implement this for usvfs")
- std::wstring vfsDLL = L"";
- if (extender->getArch() == IMAGE_FILE_MACHINE_I386) {
- vfsDLL = AppConfig::vfs32DLLName();
- }
- else if (extender->getArch() == IMAGE_FILE_MACHINE_AMD64)
- {
- vfsDLL = AppConfig::vfs64DLLName();
- }
- if (vfsDLL != L"") {
- QString targetPath = pluginsDir.absoluteFilePath(ToQString(vfsDLL));
- QString vfsDLLPath = qApp->applicationDirPath() + "/" + QString::fromStdWString(vfsDLL);
-
- log::debug("DLL USVFS Target Path: {}", targetPath);
- log::debug("DLL USVFS VFS DLL Path: {}", vfsDLLPath);
-
- QFile dllFile(targetPath);
-
- if (dllFile.exists()) {
- // may be outdated
- if (!hashIdentical(targetPath, vfsDLLPath)) {
- dllFile.remove();
- }
- }
-
- if (!dllFile.exists()) {
- // install dll to SE plugins
- if (!QFile::copy(vfsDLLPath, targetPath)) {
- throw MyException(QObject::tr("Failed to copy %1 to %2").arg(vfsDLLPath, targetPath));
- }
- }
- }
- writeHintFile(pluginsDir);
- } catch (const std::exception &e) {
- QMessageBox::critical(nullptr, QObject::tr("Failed to set up script extender loading"), e.what());
- }
-}
-
-
-void LoadMechanism::activateProxyDLL()
-{
- try {
- IPluginGame const *game = qApp->property("managed_game").value<IPluginGame *>();
-
- QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget()));
-
- QFile targetDLL(targetPath);
- if (!targetDLL.exists()) {
- return;
- }
-
- QString sourcePath = qApp->applicationDirPath() + "/" + ToQString(AppConfig::proxyDLLSource());
-
- // this is a very crude way of making this decision but it should be good enough
- if (targetDLL.size() < 24576) {
- // determine if a proxy-dll is already installed and if so, if it's the right one
- if (!hashIdentical(targetPath, sourcePath)) {
- // wrong proxy dll, probably outdated. delete and install the new one
- if (!QFile::remove(targetPath)) {
- throw MyException(QObject::tr("Failed to delete old proxy-dll %1").arg(targetPath));
- }
- if (!QFile::copy(sourcePath, targetPath)) {
- throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
- }
- } // otherwise the proxy-dll is already the right one
- } else {
- // no proxy dll installed yet. move the original and insert proxy-dll
-
- QString origFile = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLOrig()));
-
- if (QFile(origFile).exists()) {
- // orig-file exists. this may happen if the steam-api was updated or the user messed with the
- // dlls.
- if (!QFile::remove(origFile)) {
- throw MyException(QObject::tr("Failed to overwrite %1").arg(origFile));
- }
- }
- if (!QFile::rename(targetPath, origFile)) {
- throw MyException(QObject::tr("Failed to rename %1 to %2").arg(targetPath).arg(origFile));
- }
- if (!QFile::copy(sourcePath, targetPath)) {
- throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
- }
- }
- writeHintFile(game->gameDirectory());
- } catch (const std::exception &e) {
- QMessageBox::critical(nullptr, QObject::tr("Failed to set up proxy-dll loading"), e.what());
- }
-}
-
-
-void LoadMechanism::activate(EMechanism mechanism)
-{
- switch (mechanism) {
- case LOAD_MODORGANIZER: {
- log::debug("Load Mechanism: Mod Organizer");
- deactivateProxyDLL();
- deactivateScriptExtender();
- } break;
- case LOAD_SCRIPTEXTENDER: {
- log::debug("Load Mechanism: ScriptExtender");
- deactivateProxyDLL();
- activateScriptExtender();
- } break;
- case LOAD_PROXYDLL: {
- log::debug("Load Mechanism: Proxy DLL");
- deactivateScriptExtender();
- activateProxyDLL();
- } break;
- }
-}
-
diff --git a/src/loadmechanism.h b/src/loadmechanism.h index c04473ab..49eb0c52 100644 --- a/src/loadmechanism.h +++ b/src/loadmechanism.h @@ -27,33 +27,14 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. /**
* @brief manages the various load mechanisms supported by Mod Organizer
- * the load mechanisms is the means by which the mo-dll is injected into the target
- * process. The default mode "mod organizer" requires the target process to be started
- * from inside mod organizer. In certain cases (oblivion steam edition) this is not
- * possible since the game can then only be started from steam.
- * "Script Extender" is an alternative load mechanism that uses a script extender (obse,
- * fose, nvse or skse) to load MO. This is reliable but prevents se plugins installed
- * through MO from working.
- * "Proxy DLL" replaces a dll belonging to the game by a proxy that will load MO and then
- * chain-load the original dll. This currently only works with steam-versions of games and
- * is intended as a last resort solution.
**/
class LoadMechanism
{
public:
-
enum EMechanism {
LOAD_MODORGANIZER = 0,
- LOAD_SCRIPTEXTENDER,
- LOAD_PROXYDLL
};
-public:
-
- /**
- * @brief constructor
- *
- **/
LoadMechanism();
/**
@@ -66,54 +47,12 @@ public: /**
* @brief test whether the "Mod Organizer" load mechanism is supported for the current game
*
- * @return true if the load mechanism is supported
+ * @return true
**/
- bool isDirectLoadingSupported();
-
- /**
- * @brief test whether the "Script Extender" load mechanism is supported for the current game
- *
- * @return true if the load mechanism is supported
- **/
- bool isScriptExtenderSupported();
-
- /**
- * @brief test whether the "Proxy DLL" load mechanism is supported for the current game
- *
- * @return true if the load mechanism is supported
- **/
- bool isProxyDLLSupported();
-
-private:
-
- // write a hint file that is required for certain loading mechanisms for the dll to find
- // the mod organizer installation
- void writeHintFile(const QDir &targetDirectory);
-
- // remove the hint file if it exists. does nothing if the file doesn't exist
- void removeHintFile(QDir targetDirectory);
-
- // compare the two files by md5-hash, returns true if they are identical
- bool hashIdentical(const QString &fileNameLHS, const QString &fileNameRHS);
-
- // deactivate loading through script extender. does nothing if se-loading wasn't active
- void deactivateScriptExtender();
-
- // deactivate loading through proxy-dll. does nothing if se-loading wasn't active
- void deactivateProxyDLL();
-
- // activate loading through script extender. does nothing if already active. updates
- // the dll if necessary
- void activateScriptExtender();
-
- // activate loading through proxy-dll. does nothing if already active. updates
- // the dll if necessary
- void activateProxyDLL();
+ bool isDirectLoadingSupported() const;
private:
-
EMechanism m_SelectedMechanism;
-
};
diff --git a/src/loglist.cpp b/src/loglist.cpp index 192913b6..c884be49 100644 --- a/src/loglist.cpp +++ b/src/loglist.cpp @@ -162,7 +162,8 @@ LogList::LogList(QWidget* parent) {
setModel(&LogModel::instance());
- const int timestampWidth = QFontMetrics(font()).width("00:00:00.000");
+ const QFontMetrics fm(font());
+ const int timestampWidth = fm.horizontalAdvance("00:00:00.000");
header()->setMinimumSectionSize(0);
header()->resizeSection(0, 20);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7c73bc8a..28405819 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -72,6 +72,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "previewdialog.h" #include "browserdialog.h" #include "aboutdialog.h" +#include "settingsdialog.h" #include <safewritefile.h> #include "nxmaccessmanager.h" #include "appconfig.h" @@ -5217,7 +5218,9 @@ void MainWindow::on_actionSettings_triggered() bool proxy = settings.useProxy(); DownloadManager *dlManager = m_OrganizerCore.downloadManager(); - settings.query(&m_PluginContainer, this); + + SettingsDialog dialog(&m_PluginContainer, &settings, this); + dialog.exec(); if (oldManagedGameDirectory != settings.getManagedGameDirectory()) { QMessageBox::about(this, tr("Restarting MO"), diff --git a/src/settings.cpp b/src/settings.cpp index 5ad066b2..77db6918 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,72 +18,16 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */ #include "settings.h" - -#include "pluginsetting.h" #include "serverinfo.h" -#include "settingsdialog.h" -#include "versioninfo.h" #include "appconfig.h" -#include "organizercore.h" #include <utility.h> -#include <iplugin.h> #include <iplugingame.h> -#include <questionboxmemory.h> #include <usvfsparameters.h> -#include <QCheckBox> -#include <QCoreApplication> -#include <QComboBox> -#include <QDate> -#include <QDialog> -#include <QDir> -#include <QDirIterator> -#include <QFileInfo> -#include <QLineEdit> -#include <QSpinBox> -#include <QListWidgetItem> -#include <QLocale> -#include <QMessageBox> -#include <QApplication> -#include <QRegExp> -#include <QDir> -#include <QStringList> -#include <QVariantMap> -#include <QLabel> -#include <QPushButton> -#include <QPalette> - -#include <Qt> // for Qt::UserRole, etc - -#include <Windows.h> // For ShellExecuteW, HINSTANCE, etc -#include <wincred.h> // For storage - -#include <algorithm> // for sort -#include <memory> -#include <stdexcept> // for runtime_error -#include <string> -#include <utility> // for pair, make_pair - - using namespace MOBase; -template <typename T> -class QListWidgetItemEx : public QListWidgetItem { -public: - QListWidgetItemEx(const QString &text, int sortRole = Qt::DisplayRole, QListWidget *parent = 0, int type = Type) - : QListWidgetItem(text, parent, type), m_SortRole(sortRole) {} - - virtual bool operator< ( const QListWidgetItem & other ) const { - return this->data(m_SortRole).value<T>() < other.data(m_SortRole).value<T>(); - } -private: - int m_SortRole; -}; - - Settings *Settings::s_Instance = nullptr; - Settings::Settings(const QSettings &settingsSource) : m_Settings(settingsSource.fileName(), settingsSource.format()) { @@ -94,13 +38,11 @@ Settings::Settings(const QSettings &settingsSource) } } - Settings::~Settings() { s_Instance = nullptr; } - Settings &Settings::instance() { if (s_Instance == nullptr) { @@ -384,15 +326,10 @@ bool Settings::hasNexusApiKey() const bool Settings::getSteamLogin(QString &username, QString &password) const { - if (m_Settings.contains("Settings/steam_username")) { - QString tempPass = deObfuscate("steam_password"); - if (!tempPass.isEmpty()) { - username = m_Settings.value("Settings/steam_username").toString(); - password = tempPass; - return true; - } - } - return false; + username = m_Settings.value("Settings/steam_username", "").toString(); + password = deObfuscate("steam_password"); + + return !username.isEmpty() && !password.isEmpty(); } bool Settings::compactDownloads() const { @@ -492,12 +429,21 @@ void Settings::setSteamLogin(QString username, QString password) LoadMechanism::EMechanism Settings::getLoadMechanism() const { - switch (m_Settings.value("Settings/load_mechanism").toInt()) { - case LoadMechanism::LOAD_MODORGANIZER: return LoadMechanism::LOAD_MODORGANIZER; - case LoadMechanism::LOAD_SCRIPTEXTENDER: return LoadMechanism::LOAD_SCRIPTEXTENDER; - case LoadMechanism::LOAD_PROXYDLL: return LoadMechanism::LOAD_PROXYDLL; + const auto i = m_Settings.value("Settings/load_mechanism").toInt(); + + switch (i) + { + case LoadMechanism::LOAD_MODORGANIZER: + return LoadMechanism::LOAD_MODORGANIZER; + + default: + qCritical().nospace().noquote() + << "invalid load mechanism " << i << ", reverting to modorganizer"; + + m_Settings.setValue("Settings/load_mechanism", LoadMechanism::LOAD_MODORGANIZER); + + return LoadMechanism::LOAD_MODORGANIZER; } - throw std::runtime_error("invalid load mechanism"); } @@ -662,588 +608,3 @@ void Settings::writePluginBlacklist() m_Settings.endArray(); } - -void Settings::addLanguages(QComboBox *languageBox) -{ - std::vector<std::pair<QString, QString>> languages; - - QDirIterator langIter(QCoreApplication::applicationDirPath() + "/translations", QDir::Files); - QString pattern = ToQString(AppConfig::translationPrefix()) + "_([a-z]{2,3}(_[A-Z]{2,2})?).qm"; - QRegExp exp(pattern); - while (langIter.hasNext()) { - langIter.next(); - QString file = langIter.fileName(); - if (exp.exactMatch(file)) { - QString languageCode = exp.cap(1); - QLocale locale(languageCode); - QString languageString = QString("%1 (%2)").arg(locale.nativeLanguageName()).arg(locale.nativeCountryName()); //QLocale::languageToString(locale.language()); - if (locale.language() == QLocale::Chinese) { - if (languageCode == "zh_TW") { - languageString = "Chinese (traditional)"; - } else { - languageString = "Chinese (simplified)"; - } - } - languages.push_back(std::make_pair(QString("%1").arg(languageString), exp.cap(1))); - //languageBox->addItem(QString("%1").arg(languageString), exp.cap(1)); - } - } - if (!languageBox->findText("English")) { - languages.push_back(std::make_pair(QString("English"), QString("en_US"))); - //languageBox->addItem("English", "en_US"); - } - std::sort(languages.begin(), languages.end()); - for (const auto &lang : languages) { - languageBox->addItem(lang.first, lang.second); - } -} - -void Settings::addStyles(QComboBox *styleBox) -{ - styleBox->addItem("None", ""); - styleBox->addItem("Fusion", "Fusion"); - - QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files); - while (langIter.hasNext()) { - langIter.next(); - QString style = langIter.fileName(); - styleBox->addItem(style, style); - } -} - -void Settings::resetDialogs() -{ - QuestionBoxMemory::resetDialogs(); -} - -void Settings::query(PluginContainer *pluginContainer, QWidget *parent) -{ - SettingsDialog dialog(pluginContainer, this, parent); - connect(&dialog, SIGNAL(resetDialogs()), this, SLOT(resetDialogs())); - - std::vector<std::unique_ptr<SettingsTab>> tabs; - - tabs.push_back(std::unique_ptr<SettingsTab>(new GeneralTab(this, dialog))); - tabs.push_back(std::unique_ptr<SettingsTab>(new PathsTab(this, dialog))); - tabs.push_back(std::unique_ptr<SettingsTab>(new DiagnosticsTab(this, dialog))); - tabs.push_back(std::unique_ptr<SettingsTab>(new NexusTab(this, dialog))); - tabs.push_back(std::unique_ptr<SettingsTab>(new SteamTab(this, dialog))); - tabs.push_back(std::unique_ptr<SettingsTab>(new PluginsTab(this, dialog))); - tabs.push_back(std::unique_ptr<SettingsTab>(new WorkaroundsTab(this, dialog))); - - - QString key = QString("geometry/%1").arg(dialog.objectName()); - if (m_Settings.contains(key)) { - dialog.restoreGeometry(m_Settings.value(key).toByteArray()); - } - - if (dialog.exec() == QDialog::Accepted) { - // remember settings before change - QMap<QString, QString> before; - m_Settings.beginGroup("Settings"); - for (auto k : m_Settings.allKeys()) - before[k] = m_Settings.value(k).toString(); - m_Settings.endGroup(); - - // transfer modified settings to configuration file - for (std::unique_ptr<SettingsTab> const &tab: tabs) { - tab->update(); - } - - // print "changed" settings - m_Settings.beginGroup("Settings"); - bool first_update = true; - for (auto k : m_Settings.allKeys()) - if (m_Settings.value(k).toString() != before[k] && !k.contains("username") && !k.contains("password")) - { - if (first_update) { - log::debug("Changed settings:"); - first_update = false; - } - log::debug(" {}={}", k, m_Settings.value(k).toString()); - } - m_Settings.endGroup(); - } - m_Settings.setValue(key, dialog.saveGeometry()); - - // These changes happen regardless of accepted or rejected - bool restartNeeded = false; - if (dialog.getApiKeyChanged()) { - restartNeeded = true; - } - if (dialog.getResetGeometries()) { - restartNeeded = true; - m_Settings.setValue("reset_geometry", true); - } - if (restartNeeded) { - if (QMessageBox::question(nullptr, - tr("Restart Mod Organizer?"), - tr("In order to finish configuration changes, MO must be restarted.\n" - "Restart it now?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - qApp->exit(INT_MAX); - } - } - -} - -Settings::SettingsTab::SettingsTab(Settings *m_parent, SettingsDialog &m_dialog) - : m_parent(m_parent) - , m_Settings(m_parent->m_Settings) - , m_dialog(m_dialog) -{ -} - -Settings::SettingsTab::~SettingsTab() -{} - -Settings::GeneralTab::GeneralTab(Settings *m_parent, SettingsDialog &m_dialog) - : Settings::SettingsTab(m_parent, m_dialog) - , m_languageBox(m_dialog.findChild<QComboBox *>("languageBox")) - , m_styleBox(m_dialog.findChild<QComboBox *>("styleBox")) - , m_compactBox(m_dialog.findChild<QCheckBox *>("compactBox")) - , m_showMetaBox(m_dialog.findChild<QCheckBox *>("showMetaBox")) - , m_usePrereleaseBox(m_dialog.findChild<QCheckBox *>("usePrereleaseBox")) - , m_overwritingBtn(m_dialog.findChild<QPushButton *>("overwritingBtn")) - , m_overwrittenBtn(m_dialog.findChild<QPushButton *>("overwrittenBtn")) - , m_overwritingArchiveBtn(m_dialog.findChild<QPushButton *>("overwritingArchiveBtn")) - , m_overwrittenArchiveBtn(m_dialog.findChild<QPushButton *>("overwrittenArchiveBtn")) - , m_containsBtn(m_dialog.findChild<QPushButton *>("containsBtn")) - , m_containedBtn(m_dialog.findChild<QPushButton *>("containedBtn")) - , m_colorSeparatorsBox(m_dialog.findChild<QCheckBox *>("colorSeparatorsBox")) -{ - // FIXME I think 'addLanguages' lives in here not in parent - m_parent->addLanguages(m_languageBox); - { - QString languageCode = m_parent->language(); - int currentID = m_languageBox->findData(languageCode); - // I made a mess. :( Most languages are stored with only the iso country - // code (2 characters like "de") but chinese - // with the exact language variant (zh_TW) so I have to search for both - // variants - if (currentID == -1) { - currentID = m_languageBox->findData(languageCode.mid(0, 2)); - } - if (currentID != -1) { - m_languageBox->setCurrentIndex(currentID); - } - } - - // FIXME I think addStyles lives in here not in parent - m_parent->addStyles(m_styleBox); - { - int currentID = m_styleBox->findData( - m_Settings.value("Settings/style", "").toString()); - if (currentID != -1) { - m_styleBox->setCurrentIndex(currentID); - } - } - /* verision using palette only works with fusion theme for some stupid reason... - m_overwritingBtn->setAutoFillBackground(true); - m_overwrittenBtn->setAutoFillBackground(true); - m_containsBtn->setAutoFillBackground(true); - m_containedBtn->setAutoFillBackground(true); - m_overwritingBtn->setPalette(QPalette(m_parent->modlistOverwritingLooseColor())); - m_overwrittenBtn->setPalette(QPalette(m_parent->modlistOverwrittenLooseColor())); - m_containsBtn->setPalette(QPalette(m_parent->modlistContainsPluginColor())); - m_containedBtn->setPalette(QPalette(m_parent->pluginListContainedColor())); - QPalette palette1 = m_overwritingBtn->palette(); - QPalette palette2 = m_overwrittenBtn->palette(); - QPalette palette3 = m_containsBtn->palette(); - QPalette palette4 = m_containedBtn->palette(); - palette1.setColor(QPalette::Background, m_parent->modlistOverwritingLooseColor()); - palette2.setColor(QPalette::Background, m_parent->modlistOverwrittenLooseColor()); - palette3.setColor(QPalette::Background, m_parent->modlistContainsPluginColor()); - palette4.setColor(QPalette::Background, m_parent->pluginListContainedColor()); - m_overwritingBtn->setPalette(palette1); - m_overwrittenBtn->setPalette(palette2); - m_containsBtn->setPalette(palette3); - m_containedBtn->setPalette(palette4); - */ - - //version with stylesheet - m_dialog.setButtonColor(m_overwritingBtn, m_parent->modlistOverwritingLooseColor()); - m_dialog.setButtonColor(m_overwrittenBtn, m_parent->modlistOverwrittenLooseColor()); - m_dialog.setButtonColor(m_overwritingArchiveBtn, m_parent->modlistOverwritingArchiveColor()); - m_dialog.setButtonColor(m_overwrittenArchiveBtn, m_parent->modlistOverwrittenArchiveColor()); - m_dialog.setButtonColor(m_containsBtn, m_parent->modlistContainsPluginColor()); - m_dialog.setButtonColor(m_containedBtn, m_parent->pluginListContainedColor()); - - m_dialog.setOverwritingColor(m_parent->modlistOverwritingLooseColor()); - m_dialog.setOverwrittenColor(m_parent->modlistOverwrittenLooseColor()); - m_dialog.setOverwritingArchiveColor(m_parent->modlistOverwritingArchiveColor()); - m_dialog.setOverwrittenArchiveColor(m_parent->modlistOverwrittenArchiveColor()); - m_dialog.setContainsColor(m_parent->modlistContainsPluginColor()); - m_dialog.setContainedColor(m_parent->pluginListContainedColor()); - - m_compactBox->setChecked(m_parent->compactDownloads()); - m_showMetaBox->setChecked(m_parent->metaDownloads()); - m_usePrereleaseBox->setChecked(m_parent->usePrereleases()); - m_colorSeparatorsBox->setChecked(m_parent->colorSeparatorScrollbar()); -} - -void Settings::GeneralTab::update() -{ - QString oldLanguage = m_parent->language(); - QString newLanguage = m_languageBox->itemData(m_languageBox->currentIndex()).toString(); - if (newLanguage != oldLanguage) { - m_Settings.setValue("Settings/language", newLanguage); - emit m_parent->languageChanged(newLanguage); - } - - QString oldStyle = m_Settings.value("Settings/style", "").toString(); - QString newStyle = m_styleBox->itemData(m_styleBox->currentIndex()).toString(); - if (oldStyle != newStyle) { - m_Settings.setValue("Settings/style", newStyle); - emit m_parent->styleChanged(newStyle); - } - - m_Settings.setValue("Settings/overwritingLooseFilesColor", m_dialog.getOverwritingColor()); - m_Settings.setValue("Settings/overwrittenLooseFilesColor", m_dialog.getOverwrittenColor()); - m_Settings.setValue("Settings/overwritingArchiveFilesColor", m_dialog.getOverwritingArchiveColor()); - m_Settings.setValue("Settings/overwrittenArchiveFilesColor", m_dialog.getOverwrittenArchiveColor()); - m_Settings.setValue("Settings/containsPluginColor", m_dialog.getContainsColor()); - m_Settings.setValue("Settings/containedColor", m_dialog.getContainedColor()); - m_Settings.setValue("Settings/compact_downloads", m_compactBox->isChecked()); - m_Settings.setValue("Settings/meta_downloads", m_showMetaBox->isChecked()); - m_Settings.setValue("Settings/use_prereleases", m_usePrereleaseBox->isChecked()); - m_Settings.setValue("Settings/colorSeparatorScrollbars", m_colorSeparatorsBox->isChecked()); -} - -Settings::PathsTab::PathsTab(Settings *parent, SettingsDialog &dialog) - : SettingsTab(parent, dialog) - , m_baseDirEdit(m_dialog.findChild<QLineEdit *>("baseDirEdit")) - , m_downloadDirEdit(m_dialog.findChild<QLineEdit *>("downloadDirEdit")) - , m_modDirEdit(m_dialog.findChild<QLineEdit *>("modDirEdit")) - , m_cacheDirEdit(m_dialog.findChild<QLineEdit *>("cacheDirEdit")) - , m_profilesDirEdit(m_dialog.findChild<QLineEdit *>("profilesDirEdit")) - , m_overwriteDirEdit(m_dialog.findChild<QLineEdit *>("overwriteDirEdit")) - , m_managedGameDirEdit(m_dialog.findChild<QLineEdit *>("managedGameDirEdit")) -{ - m_baseDirEdit->setText(m_parent->getBaseDirectory()); - m_managedGameDirEdit->setText(m_parent->m_GamePlugin->gameDirectory().absoluteFilePath(m_parent->m_GamePlugin->binaryName())); - QString basePath = parent->getBaseDirectory(); - QDir baseDir(basePath); - for (const auto &dir : { - std::make_pair(m_downloadDirEdit, m_parent->getDownloadDirectory(false)), - std::make_pair(m_modDirEdit, m_parent->getModDirectory(false)), - std::make_pair(m_cacheDirEdit, m_parent->getCacheDirectory(false)), - std::make_pair(m_profilesDirEdit, m_parent->getProfileDirectory(false)), - std::make_pair(m_overwriteDirEdit, m_parent->getOverwriteDirectory(false)) - }) { - QString storePath = baseDir.relativeFilePath(dir.second); - storePath = dir.second; - dir.first->setText(storePath); - } -} - -void Settings::PathsTab::update() -{ - typedef std::tuple<QString, QString, std::wstring> Directory; - - QString basePath = m_parent->getBaseDirectory(); - - for (const Directory &dir :{ - Directory{m_downloadDirEdit->text(), "download_directory", AppConfig::downloadPath()}, - Directory{m_cacheDirEdit->text(), "cache_directory", AppConfig::cachePath()}, - Directory{m_modDirEdit->text(), "mod_directory", AppConfig::modsPath()}, - Directory{m_overwriteDirEdit->text(), "overwrite_directory", AppConfig::overwritePath()}, - Directory{m_profilesDirEdit->text(), "profiles_directory", AppConfig::profilesPath()} - }) { - QString path, settingsKey; - std::wstring defaultName; - std::tie(path, settingsKey, defaultName) = dir; - - settingsKey = QString("Settings/%1").arg(settingsKey); - - QString realPath = path; - realPath.replace("%BASE_DIR%", m_baseDirEdit->text()); - - if (!QDir(realPath).exists()) { - if (!QDir().mkpath(realPath)) { - QMessageBox::warning(qApp->activeWindow(), tr("Error"), - tr("Failed to create \"%1\", you may not have the " - "necessary permission. path remains unchanged.") - .arg(realPath)); - } - } - - if (QFileInfo(realPath) - != QFileInfo(basePath + "/" + QString::fromStdWString(defaultName))) { - m_Settings.setValue(settingsKey, path); - } else { - m_Settings.remove(settingsKey); - } - } - - if (QFileInfo(m_baseDirEdit->text()) != - QFileInfo(qApp->property("dataPath").toString())) { - m_Settings.setValue("Settings/base_directory", m_baseDirEdit->text()); - } else { - m_Settings.remove("Settings/base_directory"); - } - - QFileInfo oldGameExe(m_parent->m_GamePlugin->gameDirectory().absoluteFilePath(m_parent->m_GamePlugin->binaryName())); - QFileInfo newGameExe(m_managedGameDirEdit->text()); - if (oldGameExe != newGameExe) { - m_Settings.setValue("gamePath", newGameExe.absolutePath()); - } -} - -Settings::DiagnosticsTab::DiagnosticsTab(Settings *m_parent, SettingsDialog &m_dialog) - : Settings::SettingsTab(m_parent, m_dialog) - , m_logLevelBox(m_dialog.findChild<QComboBox *>("logLevelBox")) - , m_dumpsTypeBox(m_dialog.findChild<QComboBox *>("dumpsTypeBox")) - , m_dumpsMaxEdit(m_dialog.findChild<QSpinBox *>("dumpsMaxEdit")) - , m_diagnosticsExplainedLabel(m_dialog.findChild<QLabel *>("diagnosticsExplainedLabel")) -{ - setLevelsBox(); - m_dumpsTypeBox->setCurrentIndex(m_parent->crashDumpsType()); - m_dumpsMaxEdit->setValue(m_parent->crashDumpsMax()); - QString logsPath = qApp->property("dataPath").toString() - + "/" + QString::fromStdWString(AppConfig::logPath()); - m_diagnosticsExplainedLabel->setText( - m_diagnosticsExplainedLabel->text() - .replace("LOGS_FULL_PATH", logsPath) - .replace("LOGS_DIR", QString::fromStdWString(AppConfig::logPath())) - .replace("DUMPS_FULL_PATH", QString::fromStdWString(OrganizerCore::crashDumpsPath())) - .replace("DUMPS_DIR", QString::fromStdWString(AppConfig::dumpsDir())) - ); -} - -void Settings::DiagnosticsTab::update() -{ - m_Settings.setValue("Settings/log_level", m_logLevelBox->currentData().toInt()); - m_Settings.setValue("Settings/crash_dumps_type", m_dumpsTypeBox->currentIndex()); - m_Settings.setValue("Settings/crash_dumps_max", m_dumpsMaxEdit->value()); -} - -void Settings::DiagnosticsTab::setLevelsBox() -{ - m_logLevelBox->clear(); - - m_logLevelBox->addItem(tr("Debug"), log::Debug); - m_logLevelBox->addItem(tr("Info (recommended)"), log::Info); - m_logLevelBox->addItem(tr("Warning"), log::Warning); - m_logLevelBox->addItem(tr("Error"), log::Error); - - for (int i=0; i<m_logLevelBox->count(); ++i) { - if (m_logLevelBox->itemData(i) == m_parent->logLevel()) { - m_logLevelBox->setCurrentIndex(i); - break; - } - } -} - -Settings::NexusTab::NexusTab(Settings *parent, SettingsDialog &dialog) - : Settings::SettingsTab(parent, dialog) - , m_offlineBox(dialog.findChild<QCheckBox *>("offlineBox")) - , m_proxyBox(dialog.findChild<QCheckBox *>("proxyBox")) - , m_knownServersList(dialog.findChild<QListWidget *>("knownServersList")) - , m_preferredServersList( - dialog.findChild<QListWidget *>("preferredServersList")) - , m_endorsementBox(dialog.findChild<QCheckBox *>("endorsementBox")) - , m_hideAPICounterBox(dialog.findChild<QCheckBox *>("hideAPICounterBox")) -{ - m_offlineBox->setChecked(parent->offlineMode()); - m_proxyBox->setChecked(parent->useProxy()); - m_endorsementBox->setChecked(parent->endorsementIntegration()); - m_hideAPICounterBox->setChecked(parent->hideAPICounter()); - - // display server preferences - m_Settings.beginGroup("Servers"); - for (const QString &key : m_Settings.childKeys()) { - QVariantMap val = m_Settings.value(key).toMap(); - QString descriptor = key; - if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { - descriptor += QStringLiteral(" (automatic)"); - } - if (val.contains("downloadSpeed") && val.contains("downloadCount") && (val["downloadCount"].toInt() > 0)) { - int bps = static_cast<int>(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt()); - descriptor += QString(" (%1 kbps)").arg(bps / 1024); - } - - QListWidgetItem *newItem = new QListWidgetItemEx<int>(descriptor, Qt::UserRole + 1); - - newItem->setData(Qt::UserRole, key); - newItem->setData(Qt::UserRole + 1, val["preferred"].toInt()); - if (val["preferred"].toInt() > 0) { - m_preferredServersList->addItem(newItem); - } else { - m_knownServersList->addItem(newItem); - } - m_preferredServersList->sortItems(Qt::DescendingOrder); - } - m_Settings.endGroup(); -} - -void Settings::NexusTab::update() -{ - /* - if (m_loginCheckBox->isChecked()) { - m_Settings.setValue("Settings/nexus_login", true); - m_Settings.setValue("Settings/nexus_username", m_usernameEdit->text()); - m_Settings.setValue("Settings/nexus_password", obfuscate(m_passwordEdit->text())); - } else { - m_Settings.setValue("Settings/nexus_login", false); - m_Settings.remove("Settings/nexus_username"); - m_Settings.remove("Settings/nexus_password"); - } - */ - m_Settings.setValue("Settings/offline_mode", m_offlineBox->isChecked()); - m_Settings.setValue("Settings/use_proxy", m_proxyBox->isChecked()); - m_Settings.setValue("Settings/endorsement_integration", m_endorsementBox->isChecked()); - m_Settings.setValue("Settings/hide_api_counter", m_hideAPICounterBox->isChecked()); - - // store server preference - m_Settings.beginGroup("Servers"); - for (int i = 0; i < m_knownServersList->count(); ++i) { - QString key = m_knownServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = m_Settings.value(key).toMap(); - val["preferred"] = 0; - m_Settings.setValue(key, val); - } - int count = m_preferredServersList->count(); - for (int i = 0; i < count; ++i) { - QString key = m_preferredServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = m_Settings.value(key).toMap(); - val["preferred"] = count - i; - m_Settings.setValue(key, val); - } - m_Settings.endGroup(); -} - -Settings::SteamTab::SteamTab(Settings *m_parent, SettingsDialog &m_dialog) - : Settings::SettingsTab(m_parent, m_dialog) - , m_steamUserEdit(m_dialog.findChild<QLineEdit *>("steamUserEdit")) - , m_steamPassEdit(m_dialog.findChild<QLineEdit *>("steamPassEdit")) -{ - if (m_Settings.contains("Settings/steam_username")) { - m_steamUserEdit->setText(m_Settings.value("Settings/steam_username", "").toString()); - QString password = deObfuscate("steam_password"); - if (!password.isEmpty()) { - m_steamPassEdit->setText(password); - } - } -} - -void Settings::SteamTab::update() -{ - //FIXME this should be inlined here? - m_parent->setSteamLogin(m_steamUserEdit->text(), m_steamPassEdit->text()); -} - -Settings::PluginsTab::PluginsTab(Settings *m_parent, SettingsDialog &m_dialog) - : Settings::SettingsTab(m_parent, m_dialog) - , m_pluginsList(m_dialog.findChild<QListWidget *>("pluginsList")) - , m_pluginBlacklistList(m_dialog.findChild<QListWidget *>("pluginBlacklist")) -{ - // display plugin settings - QSet<QString> handledNames; - for (IPlugin *plugin : m_parent->m_Plugins) { - if (handledNames.contains(plugin->name())) - continue; - QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), m_pluginsList); - listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin)); - listItem->setData(Qt::UserRole + 1, m_parent->m_PluginSettings[plugin->name()]); - listItem->setData(Qt::UserRole + 2, m_parent->m_PluginDescriptions[plugin->name()]); - m_pluginsList->addItem(listItem); - handledNames.insert(plugin->name()); - } - - // display plugin blacklist - for (const QString &pluginName : m_parent->m_PluginBlacklist) { - m_pluginBlacklistList->addItem(pluginName); - } -} - -void Settings::PluginsTab::update() -{ - // transfer plugin settings to in-memory structure - for (int i = 0; i < m_pluginsList->count(); ++i) { - QListWidgetItem *item = m_pluginsList->item(i); - m_parent->m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap(); - } - // store plugin settings on disc - for (auto iterPlugins = m_parent->m_PluginSettings.begin(); iterPlugins != m_parent->m_PluginSettings.end(); ++iterPlugins) { - for (auto iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) { - m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value()); - } - } - - // store plugin blacklist - m_parent->m_PluginBlacklist.clear(); - for (QListWidgetItem *item : m_pluginBlacklistList->findItems("*", Qt::MatchWildcard)) { - m_parent->m_PluginBlacklist.insert(item->text()); - } - m_parent->writePluginBlacklist(); -} - -Settings::WorkaroundsTab::WorkaroundsTab(Settings *m_parent, - SettingsDialog &m_dialog) - : Settings::SettingsTab(m_parent, m_dialog) - , m_appIDEdit(m_dialog.findChild<QLineEdit *>("appIDEdit")) - , m_mechanismBox(m_dialog.findChild<QComboBox *>("mechanismBox")) - , m_hideUncheckedBox(m_dialog.findChild<QCheckBox *>("hideUncheckedBox")) - , m_forceEnableBox(m_dialog.findChild<QCheckBox *>("forceEnableBox")) - , m_displayForeignBox(m_dialog.findChild<QCheckBox *>("displayForeignBox")) - , m_lockGUIBox(m_dialog.findChild<QCheckBox *>("lockGUIBox")) - , m_enableArchiveParsingBox(m_dialog.findChild<QCheckBox *>("enableArchiveParsingBox")) - , m_resetGeometriesBtn(m_dialog.findChild<QPushButton *>("resetGeometryBtn")) -{ - m_appIDEdit->setText(m_parent->getSteamAppID()); - - LoadMechanism::EMechanism mechanismID = m_parent->getLoadMechanism(); - int index = 0; - - if (m_parent->m_LoadMechanism.isDirectLoadingSupported()) { - m_mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER); - if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) { - index = m_mechanismBox->count() - 1; - } - } - - if (m_parent->m_LoadMechanism.isScriptExtenderSupported()) { - m_mechanismBox->addItem(QObject::tr("Script Extender"), LoadMechanism::LOAD_SCRIPTEXTENDER); - if (mechanismID == LoadMechanism::LOAD_SCRIPTEXTENDER) { - index = m_mechanismBox->count() - 1; - } - } - - if (m_parent->m_LoadMechanism.isProxyDLLSupported()) { - m_mechanismBox->addItem(QObject::tr("Proxy DLL"), LoadMechanism::LOAD_PROXYDLL); - if (mechanismID == LoadMechanism::LOAD_PROXYDLL) { - index = m_mechanismBox->count() - 1; - } - } - - m_mechanismBox->setCurrentIndex(index); - - m_hideUncheckedBox->setChecked(m_parent->hideUncheckedPlugins()); - m_forceEnableBox->setChecked(m_parent->forceEnableCoreFiles()); - m_displayForeignBox->setChecked(m_parent->displayForeign()); - m_lockGUIBox->setChecked(m_parent->lockGUI()); - m_enableArchiveParsingBox->setChecked(m_parent->archiveParsing()); - - m_resetGeometriesBtn->setChecked(m_parent->directInterface().value("reset_geometry", false).toBool()); - - m_dialog.setExecutableBlacklist(m_parent->executablesBlacklist()); - -} - -void Settings::WorkaroundsTab::update() -{ - if (m_appIDEdit->text() != m_parent->m_GamePlugin->steamAPPId()) { - m_Settings.setValue("Settings/app_id", m_appIDEdit->text()); - } else { - m_Settings.remove("Settings/app_id"); - } - m_Settings.setValue("Settings/load_mechanism", m_mechanismBox->itemData(m_mechanismBox->currentIndex()).toInt()); - m_Settings.setValue("Settings/hide_unchecked_plugins", m_hideUncheckedBox->isChecked()); - m_Settings.setValue("Settings/force_enable_core_files", m_forceEnableBox->isChecked()); - m_Settings.setValue("Settings/display_foreign", m_displayForeignBox->isChecked()); - m_Settings.setValue("Settings/lock_gui", m_lockGUIBox->isChecked()); - m_Settings.setValue("Settings/archive_parsing_experimental", m_enableArchiveParsingBox->isChecked()); - - m_Settings.setValue("Settings/executable_blacklist", m_dialog.getExecutableBlacklist()); -} diff --git a/src/settings.h b/src/settings.h index c66eb94c..63718089 100644 --- a/src/settings.h +++ b/src/settings.h @@ -23,40 +23,13 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "loadmechanism.h" #include <log.h> -#include <QList> -#include <QMap> -#include <QObject> -#include <QPushButton> -#include <QSet> -#include <QSettings> -#include <QString> -#include <QVariant> -#include <QColor> -#include <QMetaType> - -#include <QtGlobal> //for uint - -#include <map> -#include <vector> - -class QCheckBox; -class QComboBox; -class QLineEdit; -class QSpinBox; -class QListWidget; -class QWidget; -class QLabel; -class QPushButton; - -struct ServerInfo; - namespace MOBase { class IPlugin; class IPluginGame; } -class SettingsDialog; class PluginContainer; +struct ServerInfo; /** * manages the settings for Mod Organizer. The settings are not cached @@ -64,17 +37,11 @@ class PluginContainer; **/ class Settings : public QObject { - Q_OBJECT public: - - /** - * @brief constructor - **/ Settings(const QSettings &settingsSource); - - virtual ~Settings(); + ~Settings(); static Settings &instance(); @@ -91,12 +58,6 @@ public: void registerPlugin(MOBase::IPlugin *plugin); /** - * displays a SettingsDialog that allows the user to change settings. If the - * user accepts the changes, the settings are immediately written - **/ - void query(PluginContainer *pluginContainer, QWidget *parent); - - /** * set up the settings for the specified plugins **/ void addPluginSettings(const std::vector<MOBase::IPlugin*> &plugins); @@ -404,180 +365,36 @@ public: */ bool colorSeparatorScrollbar() const; -public slots: - - void managedGameChanged(MOBase::IPluginGame const *gamePlugin); -public: static QColor getIdealTextColor(const QColor& rBackgroundColor); -private: - static bool obfuscate(const QString key, const QString data); - static QString deObfuscate(const QString key); + MOBase::IPluginGame const *gamePlugin() { return m_GamePlugin; } + const LoadMechanism& loadMechanism() const { return m_LoadMechanism; } - void addLanguages(QComboBox *languageBox); - void addStyles(QComboBox *styleBox); - void readPluginBlacklist(); + // temp + QMap<QString, QVariantMap> m_PluginSettings; + QMap<QString, QVariantMap> m_PluginDescriptions; + QSet<QString> m_PluginBlacklist; void writePluginBlacklist(); - QString getConfigurablePath(const QString &key, const QString &def, bool resolve) const; - - class SettingsTab - { - public: - SettingsTab(Settings *m_parent, SettingsDialog &m_dialog); - virtual ~SettingsTab(); - - virtual void update() = 0; - - protected: - Settings *m_parent; - QSettings &m_Settings; - SettingsDialog &m_dialog; - - }; - - /** Display/store the configuration in the 'general' tab of the settings dialogue */ - class GeneralTab : public SettingsTab - { - public: - GeneralTab(Settings *m_parent, SettingsDialog &m_dialog); - - void update(); - - private: - QComboBox *m_languageBox; - QComboBox *m_styleBox; - QCheckBox *m_compactBox; - QCheckBox *m_showMetaBox; - QCheckBox *m_usePrereleaseBox; - QPushButton *m_overwritingBtn; - QPushButton *m_overwrittenBtn; - QPushButton *m_overwritingArchiveBtn; - QPushButton *m_overwrittenArchiveBtn; - QPushButton *m_containsBtn; - QPushButton *m_containedBtn; - QCheckBox *m_colorSeparatorsBox; - }; - - class PathsTab : public SettingsTab - { - public: - PathsTab(Settings *parent, SettingsDialog &dialog); - void update(); - - private: - QLineEdit *m_baseDirEdit; - QLineEdit *m_downloadDirEdit; - QLineEdit *m_modDirEdit; - QLineEdit *m_cacheDirEdit; - QLineEdit *m_profilesDirEdit; - QLineEdit *m_overwriteDirEdit; - QLineEdit *m_managedGameDirEdit; - }; - - class DiagnosticsTab : public SettingsTab - { - public: - DiagnosticsTab(Settings *parent, SettingsDialog &dialog); - - void update(); - - private: - QComboBox *m_logLevelBox; - QComboBox *m_dumpsTypeBox; - QSpinBox *m_dumpsMaxEdit; - QLabel *m_diagnosticsExplainedLabel; - - void setLevelsBox(); - }; - - /** Display/store the configuration in the 'nexus' tab of the settings dialogue */ - class NexusTab : public SettingsTab - { - public: - NexusTab(Settings *m_parent, SettingsDialog &m_dialog); - void update(); - - private: - QCheckBox *m_offlineBox; - QCheckBox *m_proxyBox; - QListWidget *m_knownServersList; - QListWidget *m_preferredServersList; - QCheckBox *m_endorsementBox; - QCheckBox *m_hideAPICounterBox; - }; - - /** Display/store the configuration in the 'steam' tab of the settings dialogue */ - class SteamTab : public SettingsTab - { - public: - SteamTab(Settings *m_parent, SettingsDialog &m_dialog); - - void update(); - - private: - QLineEdit *m_steamUserEdit; - QLineEdit *m_steamPassEdit; - }; - - /** Display/store the configuration in the 'plugins' tab of the settings dialogue */ - class PluginsTab : public SettingsTab - { - public: - PluginsTab(Settings *m_parent, SettingsDialog &m_dialog); - - void update(); - - private: - QListWidget *m_pluginsList; - QListWidget *m_pluginBlacklistList; - }; - - /** Display/store the configuration in the 'workarounds' tab of the settings dialogue */ - class WorkaroundsTab : public SettingsTab - { - public: - WorkaroundsTab(Settings *m_parent, SettingsDialog &m_dialog); - - void update(); - - private: - QLineEdit *m_appIDEdit; - QComboBox *m_mechanismBox; - QCheckBox *m_hideUncheckedBox; - QCheckBox *m_forceEnableBox; - QCheckBox *m_displayForeignBox; - QCheckBox *m_lockGUIBox; - QCheckBox *m_enableArchiveParsingBox; - QPushButton *m_resetGeometriesBtn; - }; - -private slots: - - void resetDialogs(); +public slots: + void managedGameChanged(MOBase::IPluginGame const *gamePlugin); signals: - void languageChanged(const QString &newLanguage); void styleChanged(const QString &newStyle); private: - static Settings *s_Instance; - MOBase::IPluginGame const *m_GamePlugin; - - QSettings m_Settings; - + mutable QSettings m_Settings; LoadMechanism m_LoadMechanism; - std::vector<MOBase::IPlugin*> m_Plugins; - QMap<QString, QVariantMap> m_PluginSettings; - QMap<QString, QVariantMap> m_PluginDescriptions; - - QSet<QString> m_PluginBlacklist; + static bool obfuscate(const QString key, const QString data); + static QString deObfuscate(const QString key); + void readPluginBlacklist(); + QString getConfigurablePath(const QString &key, const QString &def, bool resolve) const; }; #endif // SETTINGS_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 0dae31ac..fbd9ecd1 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -18,101 +18,104 @@ 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 "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_PluginContainer(pluginContainer) + , m_GeometriesReset(false) + , m_keyChanged(false) +{ + 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))); + auto& qsettings = settings->directInterface(); -using namespace MOBase; + QString key = QString("geometry/%1").arg(objectName()); + if (qsettings.contains(key)) { + restoreGeometry(qsettings.value(key).toByteArray()); + } +} -class NexusManualKeyDialog : public QDialog +int SettingsDialog::exec() { -public: - NexusManualKeyDialog(QWidget* parent) - : QDialog(parent), ui(new Ui::NexusManualKeyDialog) - { - ui->setupUi(this); - ui->key->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - - connect(ui->openBrowser, &QPushButton::clicked, [&]{ openBrowser(); }); - connect(ui->paste, &QPushButton::clicked, [&]{ paste(); }); - connect(ui->clear, &QPushButton::clicked, [&]{ clear(); }); - } + auto& qsettings = m_settings->directInterface(); + auto ret = TutorableDialog::exec(); - void accept() override - { - m_key = ui->key->toPlainText(); - QDialog::accept(); - } + if (ret == QDialog::Accepted) { - const QString& key() const - { - return m_key; - } + for (auto&& tab : m_tabs) { + tab->closing(); + } - void openBrowser() - { - shell::OpenLink(QUrl("https://www.nexusmods.com/users/myaccount?tab=api")); - } + // remember settings before change + QMap<QString, QString> before; + qsettings.beginGroup("Settings"); + for (auto k : qsettings.allKeys()) + before[k] = qsettings.value(k).toString(); + qsettings.endGroup(); - void paste() - { - const auto text = QApplication::clipboard()->text(); - if (!text.isEmpty()) { - ui->key->setPlainText(text); + // transfer modified settings to configuration file + for (std::unique_ptr<SettingsTab> const &tab: m_tabs) { + tab->update(); } - } - void clear() - { - ui->key->clear(); + // print "changed" settings + qsettings.beginGroup("Settings"); + bool first_update = true; + for (auto k : qsettings.allKeys()) + if (qsettings.value(k).toString() != before[k] && !k.contains("username") && !k.contains("password")) + { + if (first_update) { + log::debug("Changed settings:"); + first_update = false; + } + log::debug(" {}={}", k, qsettings.value(k).toString()); + } + qsettings.endGroup(); } -private: - std::unique_ptr<Ui::NexusManualKeyDialog> ui; - QString m_key; -}; + QString key = QString("geometry/%1").arg(objectName()); + qsettings.setValue(key, saveGeometry()); + // These changes happen regardless of accepted or rejected + bool restartNeeded = false; + if (getApiKeyChanged()) { + restartNeeded = true; + } + if (getResetGeometries()) { + restartNeeded = true; + qsettings.setValue("reset_geometry", true); + } + if (restartNeeded) { + if (QMessageBox::question(nullptr, + tr("Restart Mod Organizer?"), + tr("In order to finish configuration changes, MO must be restarted.\n" + "Restart it now?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + qApp->exit(INT_MAX); + } + } -SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings* settings, QWidget *parent) - : TutorableDialog("SettingsDialog", parent) - , ui(new Ui::SettingsDialog) - , m_settings(settings) - , m_PluginContainer(pluginContainer) - , m_keyChanged(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())); - - updateNexusState(); + return ret; } SettingsDialog::~SettingsDialog() @@ -131,23 +134,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(); @@ -167,7 +153,6 @@ void SettingsDialog::accept() return; } - storeSettings(ui->pluginsList->currentItem()); TutorableDialog::accept(); } @@ -181,526 +166,19 @@ bool SettingsDialog::getApiKeyChanged() return m_keyChanged; } -void SettingsDialog::on_categoriesBtn_clicked() -{ - CategoriesDialog dialog(this); - if (dialog.exec() == QDialog::Accepted) { - dialog.commitChanges(); - } -} -void SettingsDialog::on_execBlacklistBtn_clicked() +SettingsTab::SettingsTab(Settings *m_parent, SettingsDialog &m_dialog) + : m_parent(m_parent) + , m_Settings(m_parent->directInterface()) + , m_dialog(m_dialog) + , ui(m_dialog.ui) { - 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() -{ - if (m_nexusLogin && m_nexusLogin->isActive()) { - m_nexusLogin->cancel(); - return; - } - - if (!m_nexusLogin) { - m_nexusLogin.reset(new NexusSSOLogin); - - m_nexusLogin->keyChanged = [&](auto&& s){ - onSSOKeyChanged(s); - }; - - m_nexusLogin->stateChanged = [&](auto&& s, auto&& e){ - onSSOStateChanged(s, e); - }; - } - - ui->nexusLog->clear(); - m_nexusLogin->start(); - updateNexusState(); -} - -void SettingsDialog::on_nexusManualKey_clicked() -{ - if (m_nexusValidator && m_nexusValidator->isActive()) { - m_nexusValidator->cancel(); - return; - } - - NexusManualKeyDialog dialog(this); - if (dialog.exec() != QDialog::Accepted) { - return; - } - - const auto key = dialog.key(); - if (key.isEmpty()) { - clearKey(); - return; - } - - ui->nexusLog->clear(); - validateKey(key); -} - -void SettingsDialog::on_nexusDisconnect_clicked() -{ - clearKey(); - ui->nexusLog->clear(); - addNexusLog(tr("Disconnected.")); -} - -void SettingsDialog::validateKey(const QString& key) -{ - if (!m_nexusValidator) { - m_nexusValidator.reset(new NexusKeyValidator( - *NexusInterface::instance(m_PluginContainer)->getAccessManager())); - - m_nexusValidator->stateChanged = [&](auto&& s, auto&& e){ - onValidatorStateChanged(s, e); - }; - - m_nexusValidator->finished = [&](auto&& user) { - onValidatorFinished(user); - }; - } - - addNexusLog(tr("Checking API key...")); - m_nexusValidator->start(key); -} - -void SettingsDialog::onSSOKeyChanged(const QString& key) -{ - if (key.isEmpty()) { - clearKey(); - } else { - addNexusLog(tr("Received API key.")); - validateKey(key); - } -} - -void SettingsDialog::onSSOStateChanged(NexusSSOLogin::States s, const QString& e) -{ - if (s != NexusSSOLogin::Finished) { - // finished state is handled in onSSOKeyChanged() - const auto log = NexusSSOLogin::stateToString(s, e); - - for (auto&& line : log.split("\n")) { - addNexusLog(line); - } - } - - updateNexusState(); -} - -void SettingsDialog::onValidatorStateChanged( - NexusKeyValidator::States s, const QString& e) -{ - if (s != NexusKeyValidator::Finished) { - // finished state is handled in onValidatorFinished() - const auto log = NexusKeyValidator::stateToString(s, e); - - for (auto&& line : log.split("\n")) { - addNexusLog(line); - } - } - - updateNexusState(); -} - -void SettingsDialog::onValidatorFinished(const APIUserAccount& user) -{ - NexusInterface::instance(m_PluginContainer)->setUserAccount(user); - - if (!user.apiKey().isEmpty()) { - if (setKey(user.apiKey())) { - addNexusLog(tr("Linked with Nexus successfully.")); - } - } -} - -void SettingsDialog::addNexusLog(const QString& s) -{ - ui->nexusLog->addItem(s); - ui->nexusLog->scrollToBottom(); -} - -bool SettingsDialog::setKey(const QString& key) -{ - m_keyChanged = true; - const bool ret = m_settings->setNexusApiKey(key); - updateNexusState(); - return ret; -} - -bool SettingsDialog::clearKey() -{ - m_keyChanged = true; - const auto ret = m_settings->clearNexusApiKey(); - - NexusInterface::instance(m_PluginContainer)->getAccessManager()->clearApiKey(); - updateNexusState(); - - return ret; -} - -void SettingsDialog::updateNexusState() -{ - updateNexusButtons(); - updateNexusData(); -} - -void SettingsDialog::updateNexusButtons() -{ - if (m_nexusLogin && m_nexusLogin->isActive()) { - // api key is in the process of being retrieved - ui->nexusConnect->setText(tr("Cancel")); - ui->nexusConnect->setEnabled(true); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setText(tr("Enter API Key Manually")); - ui->nexusManualKey->setEnabled(false); - } - else if (m_nexusValidator && m_nexusValidator->isActive()) { - // api key is in the process of being tested - ui->nexusConnect->setText(tr("Connect to Nexus")); - ui->nexusConnect->setEnabled(false); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setText(tr("Cancel")); - ui->nexusManualKey->setEnabled(true); - } - else if (m_settings->hasNexusApiKey()) { - // api key is present - ui->nexusConnect->setText(tr("Connect to Nexus")); - ui->nexusConnect->setEnabled(false); - ui->nexusDisconnect->setEnabled(true); - ui->nexusManualKey->setText(tr("Enter API Key Manually")); - ui->nexusManualKey->setEnabled(false); - } else { - // api key not present - ui->nexusConnect->setText(tr("Connect to Nexus")); - ui->nexusConnect->setEnabled(true); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setText(tr("Enter API Key Manually")); - ui->nexusManualKey->setEnabled(true); - } -} - -void SettingsDialog::updateNexusData() -{ - const auto user = NexusInterface::instance(m_PluginContainer) - ->getAPIUserAccount(); - - if (user.isValid()) { - ui->nexusUserID->setText(user.id()); - ui->nexusName->setText(user.name()); - ui->nexusAccount->setText(localizedUserAccountType(user.type())); - - ui->nexusDailyRequests->setText(QString("%1/%2") - .arg(user.limits().remainingDailyRequests) - .arg(user.limits().maxDailyRequests)); - - ui->nexusHourlyRequests->setText(QString("%1/%2") - .arg(user.limits().remainingHourlyRequests) - .arg(user.limits().maxHourlyRequests)); - } else { - ui->nexusUserID->setText(tr("N/A")); - ui->nexusName->setText(tr("N/A")); - ui->nexusAccount->setText(tr("N/A")); - ui->nexusDailyRequests->setText(tr("N/A")); - ui->nexusHourlyRequests->setText(tr("N/A")); - } -} - -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::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); -} - -void SettingsDialog::on_profilesDirEdit_editingFinished() -{ - normalizePath(ui->profilesDirEdit); -} - -void SettingsDialog::on_overwriteDirEdit_editingFinished() -{ - normalizePath(ui->overwriteDirEdit); -} +SettingsTab::~SettingsTab() +{} -void SettingsDialog::on_resetGeometryBtn_clicked() +QWidget* SettingsTab::parentWidget() { - m_GeometriesReset = true; - ui->resetGeometryBtn->setChecked(true); + return &m_dialog; } diff --git a/src/settingsdialog.h b/src/settingsdialog.h index c5f487fd..03bba7cf 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -21,16 +21,30 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #define SETTINGSDIALOG_H #include "tutorabledialog.h" -#include "nxmaccessmanager.h" -#include <iplugin.h> -#include <QListWidgetItem> class PluginContainer; class Settings; +class SettingsDialog; +namespace Ui { class SettingsDialog; } -namespace Ui { - class SettingsDialog; -} + +class SettingsTab +{ +public: + SettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + virtual ~SettingsTab(); + + virtual void update() = 0; + virtual void closing() {} + +protected: + Settings *m_parent; + QSettings &m_Settings; + SettingsDialog &m_dialog; + Ui::SettingsDialog* ui; + + QWidget* parentWidget(); +}; /** @@ -54,112 +68,25 @@ public: */ QString getColoredButtonStyleSheet() const; - void setButtonColor(QPushButton *button, const QColor &color); + // temp + Ui::SettingsDialog *ui; + bool m_keyChanged; + bool m_GeometriesReset; + PluginContainer *m_PluginContainer; + + int exec() override; public slots: - virtual void accept(); -signals: - - void resetDialogs(); - void retryApiConnection(); - -private: - - void storeSettings(QListWidgetItem *pluginItem); - void normalizePath(QLineEdit *lineEdit); - public: - - QColor getOverwritingColor() { return m_OverwritingColor; } - QColor getOverwrittenColor() { return m_OverwrittenColor; } - QColor getOverwritingArchiveColor() { return m_OverwritingArchiveColor; } - QColor getOverwrittenArchiveColor() { return m_OverwrittenArchiveColor; } - QColor getContainsColor() { return m_ContainsColor; } - QColor getContainedColor() { return m_ContainedColor; } - QString getExecutableBlacklist() { return m_ExecutableBlacklist; } - bool getResetGeometries(); bool getApiKeyChanged(); - - void setOverwritingColor(QColor col) { m_OverwritingColor = col; } - void setOverwrittenColor(QColor col) { m_OverwrittenColor = col; } - void setOverwritingArchiveColor(QColor col) { m_OverwritingArchiveColor = col; } - void setOverwrittenArchiveColor(QColor col) { m_OverwrittenArchiveColor = col; } - void setContainsColor(QColor col) { m_ContainsColor = col; } - void setContainedColor(QColor col) { m_ContainedColor = col; } - void setExecutableBlacklist(QString blacklist) { m_ExecutableBlacklist = blacklist; } - - -private slots: - void on_categoriesBtn_clicked(); - void on_execBlacklistBtn_clicked(); - void on_bsaDateBtn_clicked(); - void on_browseDownloadDirBtn_clicked(); - void on_browseModDirBtn_clicked(); - void on_browseCacheDirBtn_clicked(); - void on_resetDialogsButton_clicked(); - void on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); - void on_associateButton_clicked(); - void on_clearCacheButton_clicked(); - void on_nexusDisconnect_clicked(); - void on_browseBaseDirBtn_clicked(); - void on_browseOverwriteDirBtn_clicked(); - void on_browseProfilesDirBtn_clicked(); - void on_browseGameDirBtn_clicked(); - void on_overwritingBtn_clicked(); - void on_overwrittenBtn_clicked(); - void on_overwritingArchiveBtn_clicked(); - void on_overwrittenArchiveBtn_clicked(); - void on_containsBtn_clicked(); - void on_containedBtn_clicked(); - void on_resetColorsBtn_clicked(); - void on_baseDirEdit_editingFinished(); - void on_downloadDirEdit_editingFinished(); - void on_modDirEdit_editingFinished(); - void on_cacheDirEdit_editingFinished(); - void on_profilesDirEdit_editingFinished(); - void on_overwriteDirEdit_editingFinished(); - void on_nexusConnect_clicked(); - void on_nexusManualKey_clicked(); - void on_resetGeometryBtn_clicked(); - - void deleteBlacklistItem(); + bool getResetGeometries(); private: - Ui::SettingsDialog *ui; Settings* m_settings; - PluginContainer *m_PluginContainer; - - QColor m_OverwritingColor; - QColor m_OverwrittenColor; - QColor m_OverwritingArchiveColor; - QColor m_OverwrittenArchiveColor; - QColor m_ContainsColor; - QColor m_ContainedColor; - - bool m_GeometriesReset; - bool m_keyChanged; - - QString m_ExecutableBlacklist; - std::unique_ptr<NexusSSOLogin> m_nexusLogin; - std::unique_ptr<NexusKeyValidator> m_nexusValidator; - - void validateKey(const QString& key); - bool setKey(const QString& key); - bool clearKey(); - - void updateNexusState(); - void updateNexusButtons(); - void updateNexusData(); - - void onSSOKeyChanged(const QString& key); - void onSSOStateChanged(NexusSSOLogin::States s, const QString& e); - - void onValidatorStateChanged(NexusKeyValidator::States s, const QString& e); - void onValidatorFinished(const APIUserAccount& user); + std::vector<std::unique_ptr<SettingsTab>> m_tabs; - void addNexusLog(const QString& s); }; #endif // SETTINGSDIALOG_H diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 1e94bcde..e011542e 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -76,11 +76,7 @@ p, li { white-space: pre-wrap; } <string>Update to non-stable releases.</string> </property> <property name="whatsThis"> - <string>If this is enabled, the integrated update mechanism will notify of all releases, including pre-releases (alphas, betas). - -Please use this only if you're sufficiently tech-savvy to investigate issues, look for known problems in the issue tracker and create meaningful reports. - -If you use pre-releases, never contact me directly by e-mail or via private messages!</string> + <string>If this is enabled, the integrated update mechanism will notify of all releases, including pre-releases (alphas, betas). Please use this only if you're sufficiently tech-savvy to investigate issues, look for known problems in the issue tracker and create meaningful reports.</string> </property> <property name="text"> <string>Install Pre-releases (Betas)</string> @@ -828,71 +824,55 @@ If you use pre-releases, never contact me directly by e-mail or via private mess <string>Steam</string> </attribute> <layout class="QGridLayout" name="gridLayout_3"> - <item row="0" column="0"> + <item row="1" column="0"> <widget class="QLabel" name="label_19"> <property name="text"> <string>Username</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="steamUserEdit"/> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_20"> - <property name="text"> - <string>Password</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="steamPassEdit"> - <property name="echoMode"> - <enum>QLineEdit::Password</enum> - </property> - </widget> - </item> - <item row="2" column="0"> - <spacer name="verticalSpacer_5"> + <item row="4" column="0"> + <spacer name="verticalSpacer_4"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeType"> - <enum>QSizePolicy::Minimum</enum> + <enum>QSizePolicy::Expanding</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> - <height>40</height> + <height>232</height> </size> </property> </spacer> </item> - <item row="3" column="0" colspan="2"> + <item row="1" column="1"> + <widget class="QLineEdit" name="steamUserEdit"/> + </item> + <item row="0" column="0" colspan="2"> <widget class="QLabel" name="label_21"> <property name="text"> - <string>If you save your steam user ID and password here, they will be used when logging into steam. Note, however, your password will be stored unencrypted, so make sure your computer is secure.</string> + <string><html><head/><body><p>If you save your steam user ID and password here, they will be used when logging into steam.</p></body></html></string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> - <item row="4" column="0"> - <spacer name="verticalSpacer_4"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> + <item row="2" column="0"> + <widget class="QLabel" name="label_20"> + <property name="text"> + <string>Password</string> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>232</height> - </size> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="steamPassEdit"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> </property> - </spacer> + </widget> </item> </layout> </widget> @@ -1341,6 +1321,9 @@ programs you are intentionally running.</string> <property name="wordWrap"> <bool>true</bool> </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> </widget> </item> <item row="1" column="0"> diff --git a/src/settingsdialogdiagnostics.cpp b/src/settingsdialogdiagnostics.cpp new file mode 100644 index 00000000..daf81d5c --- /dev/null +++ b/src/settingsdialogdiagnostics.cpp @@ -0,0 +1,48 @@ +#include "settingsdialogdiagnostics.h" +#include "ui_settingsdialog.h" +#include "appconfig.h" +#include "organizercore.h" +#include <log.h> + +using namespace MOBase; + +DiagnosticsSettingsTab::DiagnosticsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) + : SettingsTab(m_parent, m_dialog) +{ + setLevelsBox(); + ui->dumpsTypeBox->setCurrentIndex(m_parent->crashDumpsType()); + ui->dumpsMaxEdit->setValue(m_parent->crashDumpsMax()); + QString logsPath = qApp->property("dataPath").toString() + + "/" + QString::fromStdWString(AppConfig::logPath()); + ui->diagnosticsExplainedLabel->setText( + ui->diagnosticsExplainedLabel->text() + .replace("LOGS_FULL_PATH", logsPath) + .replace("LOGS_DIR", QString::fromStdWString(AppConfig::logPath())) + .replace("DUMPS_FULL_PATH", QString::fromStdWString(OrganizerCore::crashDumpsPath())) + .replace("DUMPS_DIR", QString::fromStdWString(AppConfig::dumpsDir())) + ); +} + +void DiagnosticsSettingsTab::setLevelsBox() +{ + ui->logLevelBox->clear(); + + ui->logLevelBox->addItem(QObject::tr("Debug"), log::Debug); + ui->logLevelBox->addItem(QObject::tr("Info (recommended)"), log::Info); + ui->logLevelBox->addItem(QObject::tr("Warning"), log::Warning); + ui->logLevelBox->addItem(QObject::tr("Error"), log::Error); + + for (int i=0; i<ui->logLevelBox->count(); ++i) { + if (ui->logLevelBox->itemData(i) == m_parent->logLevel()) { + ui->logLevelBox->setCurrentIndex(i); + break; + } + } +} + +void DiagnosticsSettingsTab::update() +{ + m_Settings.setValue("Settings/log_level", ui->logLevelBox->currentData().toInt()); + m_Settings.setValue("Settings/crash_dumps_type", ui->dumpsTypeBox->currentIndex()); + m_Settings.setValue("Settings/crash_dumps_max", ui->dumpsMaxEdit->value()); +} diff --git a/src/settingsdialogdiagnostics.h b/src/settingsdialogdiagnostics.h new file mode 100644 index 00000000..4c1805e2 --- /dev/null +++ b/src/settingsdialogdiagnostics.h @@ -0,0 +1,18 @@ +#ifndef SETTINGSDIALOGDIAGNOSTICS_H +#define SETTINGSDIALOGDIAGNOSTICS_H + +#include "settings.h" +#include "settingsdialog.h" + +class DiagnosticsSettingsTab : public SettingsTab +{ +public: + DiagnosticsSettingsTab(Settings *parent, SettingsDialog &dialog); + + void update(); + +private: + void setLevelsBox(); +}; + +#endif // SETTINGSDIALOGDIAGNOSTICS_H diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp new file mode 100644 index 00000000..324dc4f4 --- /dev/null +++ b/src/settingsdialoggeneral.cpp @@ -0,0 +1,250 @@ +#include "settingsdialoggeneral.h" +#include "ui_settingsdialog.h" +#include "appconfig.h" +#include "categoriesdialog.h" +#include <questionboxmemory.h> + +using MOBase::QuestionBoxMemory; + +GeneralSettingsTab::GeneralSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) + : SettingsTab(m_parent, m_dialog) +{ + addLanguages(); + { + QString languageCode = m_parent->language(); + int currentID = ui->languageBox->findData(languageCode); + // I made a mess. :( Most languages are stored with only the iso country + // code (2 characters like "de") but chinese + // with the exact language variant (zh_TW) so I have to search for both + // variants + if (currentID == -1) { + currentID = ui->languageBox->findData(languageCode.mid(0, 2)); + } + if (currentID != -1) { + ui->languageBox->setCurrentIndex(currentID); + } + } + + addStyles(); + { + int currentID = ui->styleBox->findData( + m_Settings.value("Settings/style", "").toString()); + if (currentID != -1) { + ui->styleBox->setCurrentIndex(currentID); + } + } + + //version with stylesheet + setButtonColor(ui->overwritingBtn, m_parent->modlistOverwritingLooseColor()); + setButtonColor(ui->overwrittenBtn, m_parent->modlistOverwrittenLooseColor()); + setButtonColor(ui->overwritingArchiveBtn, m_parent->modlistOverwritingArchiveColor()); + setButtonColor(ui->overwrittenArchiveBtn, m_parent->modlistOverwrittenArchiveColor()); + setButtonColor(ui->containsBtn, m_parent->modlistContainsPluginColor()); + setButtonColor(ui->containedBtn, m_parent->pluginListContainedColor()); + + setOverwritingColor(m_parent->modlistOverwritingLooseColor()); + setOverwrittenColor(m_parent->modlistOverwrittenLooseColor()); + setOverwritingArchiveColor(m_parent->modlistOverwritingArchiveColor()); + setOverwrittenArchiveColor(m_parent->modlistOverwrittenArchiveColor()); + setContainsColor(m_parent->modlistContainsPluginColor()); + setContainedColor(m_parent->pluginListContainedColor()); + + ui->compactBox->setChecked(m_parent->compactDownloads()); + ui->showMetaBox->setChecked(m_parent->metaDownloads()); + ui->usePrereleaseBox->setChecked(m_parent->usePrereleases()); + ui->colorSeparatorsBox->setChecked(m_parent->colorSeparatorScrollbar()); + + QObject::connect(ui->overwritingArchiveBtn, &QPushButton::clicked, [&]{ on_overwritingArchiveBtn_clicked(); }); + QObject::connect(ui->overwritingBtn, &QPushButton::clicked, [&]{ on_overwritingBtn_clicked(); }); + QObject::connect(ui->overwrittenArchiveBtn, &QPushButton::clicked, [&]{ on_overwrittenArchiveBtn_clicked(); }); + QObject::connect(ui->overwrittenBtn, &QPushButton::clicked, [&]{ on_overwrittenBtn_clicked(); }); + QObject::connect(ui->containedBtn, &QPushButton::clicked, [&]{ on_containedBtn_clicked(); }); + QObject::connect(ui->containsBtn, &QPushButton::clicked, [&]{ on_containsBtn_clicked(); }); + QObject::connect(ui->categoriesBtn, &QPushButton::clicked, [&]{ on_categoriesBtn_clicked(); }); + QObject::connect(ui->resetColorsBtn, &QPushButton::clicked, [&]{ on_resetColorsBtn_clicked(); }); + QObject::connect(ui->resetDialogsButton, &QPushButton::clicked, [&]{ on_resetDialogsButton_clicked(); }); +} + +void GeneralSettingsTab::update() +{ + QString oldLanguage = m_parent->language(); + QString newLanguage = ui->languageBox->itemData(ui->languageBox->currentIndex()).toString(); + if (newLanguage != oldLanguage) { + m_Settings.setValue("Settings/language", newLanguage); + emit m_parent->languageChanged(newLanguage); + } + + QString oldStyle = m_Settings.value("Settings/style", "").toString(); + QString newStyle = ui->styleBox->itemData(ui->styleBox->currentIndex()).toString(); + if (oldStyle != newStyle) { + m_Settings.setValue("Settings/style", newStyle); + emit m_parent->styleChanged(newStyle); + } + + m_Settings.setValue("Settings/overwritingLooseFilesColor", getOverwritingColor()); + m_Settings.setValue("Settings/overwrittenLooseFilesColor", getOverwrittenColor()); + m_Settings.setValue("Settings/overwritingArchiveFilesColor", getOverwritingArchiveColor()); + m_Settings.setValue("Settings/overwrittenArchiveFilesColor", getOverwrittenArchiveColor()); + m_Settings.setValue("Settings/containsPluginColor", getContainsColor()); + m_Settings.setValue("Settings/containedColor", getContainedColor()); + m_Settings.setValue("Settings/compact_downloads", ui->compactBox->isChecked()); + m_Settings.setValue("Settings/meta_downloads", ui->showMetaBox->isChecked()); + m_Settings.setValue("Settings/use_prereleases", ui->usePrereleaseBox->isChecked()); + m_Settings.setValue("Settings/colorSeparatorScrollbars", ui->colorSeparatorsBox->isChecked()); +} + +void GeneralSettingsTab::addLanguages() +{ + std::vector<std::pair<QString, QString>> languages; + + QDirIterator langIter(QCoreApplication::applicationDirPath() + "/translations", QDir::Files); + QString pattern = QString::fromStdWString(AppConfig::translationPrefix()) + "_([a-z]{2,3}(_[A-Z]{2,2})?).qm"; + QRegExp exp(pattern); + while (langIter.hasNext()) { + langIter.next(); + QString file = langIter.fileName(); + if (exp.exactMatch(file)) { + QString languageCode = exp.cap(1); + QLocale locale(languageCode); + QString languageString = QString("%1 (%2)").arg(locale.nativeLanguageName()).arg(locale.nativeCountryName()); //QLocale::languageToString(locale.language()); + if (locale.language() == QLocale::Chinese) { + if (languageCode == "zh_TW") { + languageString = "Chinese (traditional)"; + } else { + languageString = "Chinese (simplified)"; + } + } + languages.push_back(std::make_pair(QString("%1").arg(languageString), exp.cap(1))); + } + } + if (!ui->languageBox->findText("English")) { + languages.push_back(std::make_pair(QString("English"), QString("en_US"))); + } + std::sort(languages.begin(), languages.end()); + for (const auto &lang : languages) { + ui->languageBox->addItem(lang.first, lang.second); + } +} + +void GeneralSettingsTab::addStyles() +{ + ui->styleBox->addItem("None", ""); + ui->styleBox->addItem("Fusion", "Fusion"); + + QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + QString::fromStdWString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files); + while (langIter.hasNext()) { + langIter.next(); + QString style = langIter.fileName(); + ui->styleBox->addItem(style, style); + } +} + +void GeneralSettingsTab::resetDialogs() +{ + QuestionBoxMemory::resetDialogs(); +} + +void GeneralSettingsTab::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 GeneralSettingsTab::on_containsBtn_clicked() +{ + QColor result = QColorDialog::getColor(m_ContainsColor, parentWidget(), "Color Picker: Mod contains selected plugin", QColorDialog::ShowAlphaChannel); + if (result.isValid()) { + m_ContainsColor = result; + setButtonColor(ui->containsBtn, result); + } +} + +void GeneralSettingsTab::on_containedBtn_clicked() +{ + QColor result = QColorDialog::getColor(m_ContainedColor, parentWidget(), "ColorPicker: Plugin is Contained in selected Mod", QColorDialog::ShowAlphaChannel); + if (result.isValid()) { + m_ContainedColor = result; + setButtonColor(ui->containedBtn, result); + } +} + +void GeneralSettingsTab::on_overwrittenBtn_clicked() +{ + QColor result = QColorDialog::getColor(m_OverwrittenColor, parentWidget(), "ColorPicker: Is overwritten (loose files)", QColorDialog::ShowAlphaChannel); + if (result.isValid()) { + m_OverwrittenColor = result; + setButtonColor(ui->overwrittenBtn, result); + } +} + +void GeneralSettingsTab::on_overwritingBtn_clicked() +{ + QColor result = QColorDialog::getColor(m_OverwritingColor, parentWidget(), "ColorPicker: Is overwriting (loose files)", QColorDialog::ShowAlphaChannel); + if (result.isValid()) { + m_OverwritingColor = result; + setButtonColor(ui->overwritingBtn, result); + } +} + +void GeneralSettingsTab::on_overwrittenArchiveBtn_clicked() +{ + QColor result = QColorDialog::getColor(m_OverwrittenArchiveColor, parentWidget(), "ColorPicker: Is overwritten (archive files)", QColorDialog::ShowAlphaChannel); + if (result.isValid()) { + m_OverwrittenArchiveColor = result; + setButtonColor(ui->overwrittenArchiveBtn, result); + } +} + +void GeneralSettingsTab::on_overwritingArchiveBtn_clicked() +{ + QColor result = QColorDialog::getColor(m_OverwritingArchiveColor, parentWidget(), "ColorPicker: Is overwriting (archive files)", QColorDialog::ShowAlphaChannel); + if (result.isValid()) { + m_OverwritingArchiveColor = result; + setButtonColor(ui->overwritingArchiveBtn, result); + } +} + +void GeneralSettingsTab::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 GeneralSettingsTab::on_resetDialogsButton_clicked() +{ + if (QMessageBox::question(parentWidget(), QObject::tr("Confirm?"), + QObject::tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + resetDialogs(); + } +} + +void GeneralSettingsTab::on_categoriesBtn_clicked() +{ + CategoriesDialog dialog(parentWidget()); + if (dialog.exec() == QDialog::Accepted) { + dialog.commitChanges(); + } +} diff --git a/src/settingsdialoggeneral.h b/src/settingsdialoggeneral.h new file mode 100644 index 00000000..c7fcae36 --- /dev/null +++ b/src/settingsdialoggeneral.h @@ -0,0 +1,53 @@ +#ifndef SETTINGSDIALOGGENERAL_H +#define SETTINGSDIALOGGENERAL_H + +#include "settingsdialog.h" +#include "settings.h" + +class GeneralSettingsTab : public SettingsTab +{ +public: + GeneralSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + + void update(); + +private: + QColor m_OverwritingColor; + QColor m_OverwrittenColor; + QColor m_OverwritingArchiveColor; + QColor m_OverwrittenArchiveColor; + QColor m_ContainsColor; + QColor m_ContainedColor; + + void addLanguages(); + void addStyles(); + void resetDialogs(); + void setButtonColor(QPushButton *button, const QColor &color); + + QColor getOverwritingColor() { return m_OverwritingColor; } + QColor getOverwrittenColor() { return m_OverwrittenColor; } + QColor getOverwritingArchiveColor() { return m_OverwritingArchiveColor; } + QColor getOverwrittenArchiveColor() { return m_OverwrittenArchiveColor; } + QColor getContainsColor() { return m_ContainsColor; } + QColor getContainedColor() { return m_ContainedColor; } + + void setOverwritingColor(QColor col) { m_OverwritingColor = col; } + void setOverwrittenColor(QColor col) { m_OverwrittenColor = col; } + void setOverwritingArchiveColor(QColor col) { m_OverwritingArchiveColor = col; } + void setOverwrittenArchiveColor(QColor col) { m_OverwrittenArchiveColor = col; } + void setContainsColor(QColor col) { m_ContainsColor = col; } + void setContainedColor(QColor col) { m_ContainedColor = col; } + + void on_overwritingArchiveBtn_clicked(); + void on_overwritingBtn_clicked(); + void on_overwrittenArchiveBtn_clicked(); + void on_overwrittenBtn_clicked(); + void on_containedBtn_clicked(); + void on_containsBtn_clicked(); + + void on_categoriesBtn_clicked(); + void on_resetColorsBtn_clicked(); + void on_resetDialogsButton_clicked(); +}; + +#endif // SETTINGSDIALOGGENERAL_H diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp new file mode 100644 index 00000000..575f54d0 --- /dev/null +++ b/src/settingsdialognexus.cpp @@ -0,0 +1,363 @@ +#include "settingsdialognexus.h" +#include "ui_settingsdialog.h" +#include "ui_nexusmanualkey.h" +#include "nexusinterface.h" +#include <utility.h> + +namespace shell = MOBase::shell; + +template <typename T> +class ServerItem : public QListWidgetItem { +public: + ServerItem(const QString &text, int sortRole = Qt::DisplayRole, QListWidget *parent = 0, int type = Type) + : QListWidgetItem(text, parent, type), m_SortRole(sortRole) {} + + virtual bool operator< ( const QListWidgetItem & other ) const { + return this->data(m_SortRole).value<T>() < other.data(m_SortRole).value<T>(); + } +private: + int m_SortRole; +}; + + +class NexusManualKeyDialog : public QDialog +{ +public: + NexusManualKeyDialog(QWidget* parent) + : QDialog(parent), ui(new Ui::NexusManualKeyDialog) + { + ui->setupUi(this); + ui->key->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + + connect(ui->openBrowser, &QPushButton::clicked, [&]{ openBrowser(); }); + connect(ui->paste, &QPushButton::clicked, [&]{ paste(); }); + connect(ui->clear, &QPushButton::clicked, [&]{ clear(); }); + } + + void accept() override + { + m_key = ui->key->toPlainText(); + QDialog::accept(); + } + + const QString& key() const + { + return m_key; + } + + void openBrowser() + { + shell::OpenLink(QUrl("https://www.nexusmods.com/users/myaccount?tab=api")); + } + + void paste() + { + const auto text = QApplication::clipboard()->text(); + if (!text.isEmpty()) { + ui->key->setPlainText(text); + } + } + + void clear() + { + ui->key->clear(); + } + +private: + std::unique_ptr<Ui::NexusManualKeyDialog> ui; + QString m_key; +}; + + +NexusSettingsTab::NexusSettingsTab(Settings *parent, SettingsDialog &dialog) + : SettingsTab(parent, dialog) +{ + ui->offlineBox->setChecked(parent->offlineMode()); + ui->proxyBox->setChecked(parent->useProxy()); + ui->endorsementBox->setChecked(parent->endorsementIntegration()); + ui->hideAPICounterBox->setChecked(parent->hideAPICounter()); + + // display server preferences + m_Settings.beginGroup("Servers"); + for (const QString &key : m_Settings.childKeys()) { + QVariantMap val = m_Settings.value(key).toMap(); + QString descriptor = key; + if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { + descriptor += QStringLiteral(" (automatic)"); + } + if (val.contains("downloadSpeed") && val.contains("downloadCount") && (val["downloadCount"].toInt() > 0)) { + int bps = static_cast<int>(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt()); + descriptor += QString(" (%1 kbps)").arg(bps / 1024); + } + + QListWidgetItem *newItem = new ServerItem<int>(descriptor, Qt::UserRole + 1); + + newItem->setData(Qt::UserRole, key); + newItem->setData(Qt::UserRole + 1, val["preferred"].toInt()); + if (val["preferred"].toInt() > 0) { + ui->preferredServersList->addItem(newItem); + } else { + ui->knownServersList->addItem(newItem); + } + ui->preferredServersList->sortItems(Qt::DescendingOrder); + } + m_Settings.endGroup(); + + QObject::connect(ui->nexusConnect, &QPushButton::clicked, [&]{ on_nexusConnect_clicked(); }); + QObject::connect(ui->nexusManualKey, &QPushButton::clicked, [&]{ on_nexusManualKey_clicked(); }); + QObject::connect(ui->nexusDisconnect, &QPushButton::clicked, [&]{ on_nexusDisconnect_clicked(); }); + QObject::connect(ui->clearCacheButton, &QPushButton::clicked, [&]{ on_clearCacheButton_clicked(); }); + QObject::connect(ui->associateButton, &QPushButton::clicked, [&]{ on_associateButton_clicked(); }); + + updateNexusState(); +} + +void NexusSettingsTab::update() +{ + m_Settings.setValue("Settings/offline_mode", ui->offlineBox->isChecked()); + m_Settings.setValue("Settings/use_proxy", ui->proxyBox->isChecked()); + m_Settings.setValue("Settings/endorsement_integration", ui->endorsementBox->isChecked()); + m_Settings.setValue("Settings/hide_api_counter", ui->hideAPICounterBox->isChecked()); + + // store server preference + m_Settings.beginGroup("Servers"); + for (int i = 0; i < ui->knownServersList->count(); ++i) { + QString key = ui->knownServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = 0; + m_Settings.setValue(key, val); + } + int count = ui->preferredServersList->count(); + for (int i = 0; i < count; ++i) { + QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = count - i; + m_Settings.setValue(key, val); + } + m_Settings.endGroup(); +} + +void NexusSettingsTab::on_nexusConnect_clicked() +{ + if (m_nexusLogin && m_nexusLogin->isActive()) { + m_nexusLogin->cancel(); + return; + } + + if (!m_nexusLogin) { + m_nexusLogin.reset(new NexusSSOLogin); + + m_nexusLogin->keyChanged = [&](auto&& s){ + onSSOKeyChanged(s); + }; + + m_nexusLogin->stateChanged = [&](auto&& s, auto&& e){ + onSSOStateChanged(s, e); + }; + } + + ui->nexusLog->clear(); + m_nexusLogin->start(); + updateNexusState(); +} + +void NexusSettingsTab::on_nexusManualKey_clicked() +{ + if (m_nexusValidator && m_nexusValidator->isActive()) { + m_nexusValidator->cancel(); + return; + } + + NexusManualKeyDialog dialog(parentWidget()); + if (dialog.exec() != QDialog::Accepted) { + return; + } + + const auto key = dialog.key(); + if (key.isEmpty()) { + clearKey(); + return; + } + + ui->nexusLog->clear(); + validateKey(key); +} + +void NexusSettingsTab::on_nexusDisconnect_clicked() +{ + clearKey(); + ui->nexusLog->clear(); + addNexusLog(QObject::tr("Disconnected.")); +} + +void NexusSettingsTab::on_clearCacheButton_clicked() +{ + QDir(Settings::instance().getCacheDirectory()).removeRecursively(); + NexusInterface::instance(m_dialog.m_PluginContainer)->clearCache(); +} + +void NexusSettingsTab::on_associateButton_clicked() +{ + Settings::instance().registerAsNXMHandler(true); +} + +void NexusSettingsTab::validateKey(const QString& key) +{ + if (!m_nexusValidator) { + m_nexusValidator.reset(new NexusKeyValidator( + *NexusInterface::instance(m_dialog.m_PluginContainer)->getAccessManager())); + + m_nexusValidator->stateChanged = [&](auto&& s, auto&& e){ + onValidatorStateChanged(s, e); + }; + + m_nexusValidator->finished = [&](auto&& user) { + onValidatorFinished(user); + }; + } + + addNexusLog(QObject::tr("Checking API key...")); + m_nexusValidator->start(key); +} + +void NexusSettingsTab::onSSOKeyChanged(const QString& key) +{ + if (key.isEmpty()) { + clearKey(); + } else { + addNexusLog(QObject::tr("Received API key.")); + validateKey(key); + } +} + +void NexusSettingsTab::onSSOStateChanged(NexusSSOLogin::States s, const QString& e) +{ + if (s != NexusSSOLogin::Finished) { + // finished state is handled in onSSOKeyChanged() + const auto log = NexusSSOLogin::stateToString(s, e); + + for (auto&& line : log.split("\n")) { + addNexusLog(line); + } + } + + updateNexusState(); +} + +void NexusSettingsTab::onValidatorStateChanged( + NexusKeyValidator::States s, const QString& e) +{ + if (s != NexusKeyValidator::Finished) { + // finished state is handled in onValidatorFinished() + const auto log = NexusKeyValidator::stateToString(s, e); + + for (auto&& line : log.split("\n")) { + addNexusLog(line); + } + } + + updateNexusState(); +} + +void NexusSettingsTab::onValidatorFinished(const APIUserAccount& user) +{ + NexusInterface::instance(m_dialog.m_PluginContainer)->setUserAccount(user); + + if (!user.apiKey().isEmpty()) { + if (setKey(user.apiKey())) { + addNexusLog(QObject::tr("Linked with Nexus successfully.")); + } + } +} + +void NexusSettingsTab::addNexusLog(const QString& s) +{ + ui->nexusLog->addItem(s); + ui->nexusLog->scrollToBottom(); +} + +bool NexusSettingsTab::setKey(const QString& key) +{ + m_dialog.m_keyChanged = true; + const bool ret = m_parent->setNexusApiKey(key); + updateNexusState(); + return ret; +} + +bool NexusSettingsTab::clearKey() +{ + m_dialog.m_keyChanged = true; + const auto ret = m_parent->clearNexusApiKey(); + + NexusInterface::instance(m_dialog.m_PluginContainer)->getAccessManager()->clearApiKey(); + updateNexusState(); + + return ret; +} + +void NexusSettingsTab::updateNexusState() +{ + updateNexusButtons(); + updateNexusData(); +} + +void NexusSettingsTab::updateNexusButtons() +{ + if (m_nexusLogin && m_nexusLogin->isActive()) { + // api key is in the process of being retrieved + ui->nexusConnect->setText(QObject::tr("Cancel")); + ui->nexusConnect->setEnabled(true); + ui->nexusDisconnect->setEnabled(false); + ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); + ui->nexusManualKey->setEnabled(false); + } + else if (m_nexusValidator && m_nexusValidator->isActive()) { + // api key is in the process of being tested + ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); + ui->nexusConnect->setEnabled(false); + ui->nexusDisconnect->setEnabled(false); + ui->nexusManualKey->setText(QObject::tr("Cancel")); + ui->nexusManualKey->setEnabled(true); + } + else if (m_parent->hasNexusApiKey()) { + // api key is present + ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); + ui->nexusConnect->setEnabled(false); + ui->nexusDisconnect->setEnabled(true); + ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); + ui->nexusManualKey->setEnabled(false); + } else { + // api key not present + ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); + ui->nexusConnect->setEnabled(true); + ui->nexusDisconnect->setEnabled(false); + ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); + ui->nexusManualKey->setEnabled(true); + } +} + +void NexusSettingsTab::updateNexusData() +{ + const auto user = NexusInterface::instance(m_dialog.m_PluginContainer) + ->getAPIUserAccount(); + + if (user.isValid()) { + ui->nexusUserID->setText(user.id()); + ui->nexusName->setText(user.name()); + ui->nexusAccount->setText(localizedUserAccountType(user.type())); + + ui->nexusDailyRequests->setText(QString("%1/%2") + .arg(user.limits().remainingDailyRequests) + .arg(user.limits().maxDailyRequests)); + + ui->nexusHourlyRequests->setText(QString("%1/%2") + .arg(user.limits().remainingHourlyRequests) + .arg(user.limits().maxHourlyRequests)); + } else { + ui->nexusUserID->setText(QObject::tr("N/A")); + ui->nexusName->setText(QObject::tr("N/A")); + ui->nexusAccount->setText(QObject::tr("N/A")); + ui->nexusDailyRequests->setText(QObject::tr("N/A")); + ui->nexusHourlyRequests->setText(QObject::tr("N/A")); + } +} diff --git a/src/settingsdialognexus.h b/src/settingsdialognexus.h new file mode 100644 index 00000000..cca2e1b5 --- /dev/null +++ b/src/settingsdialognexus.h @@ -0,0 +1,41 @@ +#ifndef SETTINGSDIALOGNEXUS_H +#define SETTINGSDIALOGNEXUS_H + +#include "settings.h" +#include "settingsdialog.h" +#include "nxmaccessmanager.h" + +class NexusSettingsTab : public SettingsTab +{ +public: + NexusSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + void update(); + +private: + std::unique_ptr<NexusSSOLogin> m_nexusLogin; + std::unique_ptr<NexusKeyValidator> m_nexusValidator; + + void on_nexusConnect_clicked(); + void on_nexusManualKey_clicked(); + void on_nexusDisconnect_clicked(); + void on_clearCacheButton_clicked(); + void on_associateButton_clicked(); + + void validateKey(const QString& key); + bool setKey(const QString& key); + bool clearKey(); + + void updateNexusState(); + void updateNexusButtons(); + void updateNexusData(); + + void onSSOKeyChanged(const QString& key); + void onSSOStateChanged(NexusSSOLogin::States s, const QString& e); + + void onValidatorStateChanged(NexusKeyValidator::States s, const QString& e); + void onValidatorFinished(const APIUserAccount& user); + + void addNexusLog(const QString& s); +}; + +#endif // SETTINGSDIALOGNEXUS_H diff --git a/src/settingsdialogpaths.cpp b/src/settingsdialogpaths.cpp new file mode 100644 index 00000000..6e8fe994 --- /dev/null +++ b/src/settingsdialogpaths.cpp @@ -0,0 +1,205 @@ +#include "settingsdialogpaths.h" +#include "ui_settingsdialog.h" +#include "appconfig.h" +#include <iplugingame.h> + +PathsSettingsTab::PathsSettingsTab(Settings *parent, SettingsDialog &dialog) + : SettingsTab(parent, dialog) +{ + ui->baseDirEdit->setText(m_parent->getBaseDirectory()); + ui->managedGameDirEdit->setText(m_parent->gamePlugin()->gameDirectory().absoluteFilePath(m_parent->gamePlugin()->binaryName())); + QString basePath = parent->getBaseDirectory(); + QDir baseDir(basePath); + for (const auto &dir : { + std::make_pair(ui->downloadDirEdit, m_parent->getDownloadDirectory(false)), + std::make_pair(ui->modDirEdit, m_parent->getModDirectory(false)), + std::make_pair(ui->cacheDirEdit, m_parent->getCacheDirectory(false)), + std::make_pair(ui->profilesDirEdit, m_parent->getProfileDirectory(false)), + std::make_pair(ui->overwriteDirEdit, m_parent->getOverwriteDirectory(false)) + }) { + QString storePath = baseDir.relativeFilePath(dir.second); + storePath = dir.second; + dir.first->setText(storePath); + } + + QObject::connect(ui->browseBaseDirBtn, &QPushButton::clicked, [&]{ on_browseBaseDirBtn_clicked(); }); + QObject::connect(ui->browseCacheDirBtn, &QPushButton::clicked, [&]{ on_browseCacheDirBtn_clicked(); }); + QObject::connect(ui->browseDownloadDirBtn, &QPushButton::clicked, [&]{ on_browseDownloadDirBtn_clicked(); }); + QObject::connect(ui->browseGameDirBtn, &QPushButton::clicked, [&]{ on_browseGameDirBtn_clicked(); }); + QObject::connect(ui->browseModDirBtn, &QPushButton::clicked, [&]{ on_browseModDirBtn_clicked(); }); + QObject::connect(ui->browseOverwriteDirBtn, &QPushButton::clicked, [&]{ on_browseOverwriteDirBtn_clicked(); }); + QObject::connect(ui->browseProfilesDirBtn, &QPushButton::clicked, [&]{ on_browseProfilesDirBtn_clicked(); }); + + QObject::connect(ui->baseDirEdit, &QLineEdit::editingFinished, [&]{ on_baseDirEdit_editingFinished(); }); + QObject::connect(ui->cacheDirEdit, &QLineEdit::editingFinished, [&]{ on_cacheDirEdit_editingFinished(); }); + QObject::connect(ui->downloadDirEdit, &QLineEdit::editingFinished, [&]{ on_downloadDirEdit_editingFinished(); }); + QObject::connect(ui->modDirEdit, &QLineEdit::editingFinished, [&]{ on_modDirEdit_editingFinished(); }); + QObject::connect(ui->overwriteDirEdit, &QLineEdit::editingFinished, [&]{ on_overwriteDirEdit_editingFinished(); }); + QObject::connect(ui->profilesDirEdit, &QLineEdit::editingFinished, [&]{ on_profilesDirEdit_editingFinished(); }); +} + +void PathsSettingsTab::update() +{ + typedef std::tuple<QString, QString, std::wstring> Directory; + + QString basePath = m_parent->getBaseDirectory(); + + for (const Directory &dir :{ + Directory{ui->downloadDirEdit->text(), "download_directory", AppConfig::downloadPath()}, + Directory{ui->cacheDirEdit->text(), "cache_directory", AppConfig::cachePath()}, + Directory{ui->modDirEdit->text(), "mod_directory", AppConfig::modsPath()}, + Directory{ui->overwriteDirEdit->text(), "overwrite_directory", AppConfig::overwritePath()}, + Directory{ui->profilesDirEdit->text(), "profiles_directory", AppConfig::profilesPath()} + }) { + QString path, settingsKey; + std::wstring defaultName; + std::tie(path, settingsKey, defaultName) = dir; + + settingsKey = QString("Settings/%1").arg(settingsKey); + + QString realPath = path; + realPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + + if (!QDir(realPath).exists()) { + if (!QDir().mkpath(realPath)) { + QMessageBox::warning(qApp->activeWindow(), QObject::tr("Error"), + QObject::tr("Failed to create \"%1\", you may not have the " + "necessary permission. path remains unchanged.") + .arg(realPath)); + } + } + + if (QFileInfo(realPath) + != QFileInfo(basePath + "/" + QString::fromStdWString(defaultName))) { + m_Settings.setValue(settingsKey, path); + } else { + m_Settings.remove(settingsKey); + } + } + + if (QFileInfo(ui->baseDirEdit->text()) != + QFileInfo(qApp->property("dataPath").toString())) { + m_Settings.setValue("Settings/base_directory", ui->baseDirEdit->text()); + } else { + m_Settings.remove("Settings/base_directory"); + } + + QFileInfo oldGameExe(m_parent->gamePlugin()->gameDirectory().absoluteFilePath(m_parent->gamePlugin()->binaryName())); + QFileInfo newGameExe(ui->managedGameDirEdit->text()); + if (oldGameExe != newGameExe) { + m_Settings.setValue("gamePath", newGameExe.absolutePath()); + } +} + +void PathsSettingsTab::on_browseBaseDirBtn_clicked() +{ + QString temp = QFileDialog::getExistingDirectory( + parentWidget(), QObject::tr("Select base directory"), ui->baseDirEdit->text()); + if (!temp.isEmpty()) { + ui->baseDirEdit->setText(temp); + } +} + +void PathsSettingsTab::on_browseDownloadDirBtn_clicked() +{ + QString searchPath = ui->downloadDirEdit->text(); + searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + + QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select download directory"), searchPath); + if (!temp.isEmpty()) { + ui->downloadDirEdit->setText(temp); + } +} + +void PathsSettingsTab::on_browseModDirBtn_clicked() +{ + QString searchPath = ui->modDirEdit->text(); + searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + + QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select mod directory"), searchPath); + if (!temp.isEmpty()) { + ui->modDirEdit->setText(temp); + } +} + +void PathsSettingsTab::on_browseCacheDirBtn_clicked() +{ + QString searchPath = ui->cacheDirEdit->text(); + searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + + QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select cache directory"), searchPath); + if (!temp.isEmpty()) { + ui->cacheDirEdit->setText(temp); + } +} + +void PathsSettingsTab::on_browseProfilesDirBtn_clicked() +{ + QString searchPath = ui->profilesDirEdit->text(); + searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + + QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select profiles directory"), searchPath); + if (!temp.isEmpty()) { + ui->profilesDirEdit->setText(temp); + } +} + +void PathsSettingsTab::on_browseOverwriteDirBtn_clicked() +{ + QString searchPath = ui->overwriteDirEdit->text(); + searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + + QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select overwrite directory"), searchPath); + if (!temp.isEmpty()) { + ui->overwriteDirEdit->setText(temp); + } +} + +void PathsSettingsTab::on_browseGameDirBtn_clicked() +{ + QFileInfo oldGameExe(ui->managedGameDirEdit->text()); + + QString temp = QFileDialog::getOpenFileName(parentWidget(), QObject::tr("Select game executable"), oldGameExe.absolutePath(), oldGameExe.fileName()); + if (!temp.isEmpty()) { + ui->managedGameDirEdit->setText(temp); + } +} + +void PathsSettingsTab::on_baseDirEdit_editingFinished() +{ + normalizePath(ui->baseDirEdit); +} + +void PathsSettingsTab::on_downloadDirEdit_editingFinished() +{ + normalizePath(ui->downloadDirEdit); +} + +void PathsSettingsTab::on_modDirEdit_editingFinished() +{ + normalizePath(ui->modDirEdit); +} + +void PathsSettingsTab::on_cacheDirEdit_editingFinished() +{ + normalizePath(ui->cacheDirEdit); +} + +void PathsSettingsTab::on_profilesDirEdit_editingFinished() +{ + normalizePath(ui->profilesDirEdit); +} + +void PathsSettingsTab::on_overwriteDirEdit_editingFinished() +{ + normalizePath(ui->overwriteDirEdit); +} + +void PathsSettingsTab::normalizePath(QLineEdit *lineEdit) +{ + QString text = lineEdit->text(); + while (text.endsWith('/') || text.endsWith('\\')) { + text.chop(1); + } + lineEdit->setText(text); +} diff --git a/src/settingsdialogpaths.h b/src/settingsdialogpaths.h new file mode 100644 index 00000000..f661b624 --- /dev/null +++ b/src/settingsdialogpaths.h @@ -0,0 +1,33 @@ +#ifndef SETTINGSDIALOGPATHS_H +#define SETTINGSDIALOGPATHS_H + +#include "settings.h" +#include "settingsdialog.h" + +class PathsSettingsTab : public SettingsTab +{ +public: + PathsSettingsTab(Settings *parent, SettingsDialog &dialog); + + void update(); + +private: + void on_browseBaseDirBtn_clicked(); + void on_browseCacheDirBtn_clicked(); + void on_browseDownloadDirBtn_clicked(); + void on_browseGameDirBtn_clicked(); + void on_browseModDirBtn_clicked(); + void on_browseOverwriteDirBtn_clicked(); + void on_browseProfilesDirBtn_clicked(); + + void on_baseDirEdit_editingFinished(); + void on_cacheDirEdit_editingFinished(); + void on_downloadDirEdit_editingFinished(); + void on_modDirEdit_editingFinished(); + void on_overwriteDirEdit_editingFinished(); + void on_profilesDirEdit_editingFinished(); + + void normalizePath(QLineEdit *lineEdit); +}; + +#endif // SETTINGSDIALOGPATHS_H diff --git a/src/settingsdialogplugins.cpp b/src/settingsdialogplugins.cpp new file mode 100644 index 00000000..53b28fcc --- /dev/null +++ b/src/settingsdialogplugins.cpp @@ -0,0 +1,121 @@ +#include "settingsdialogplugins.h" +#include "ui_settingsdialog.h" +#include "noeditdelegate.h" +#include <iplugin.h> + +using MOBase::IPlugin; + +PluginsSettingsTab::PluginsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) + : SettingsTab(m_parent, m_dialog) +{ + ui->pluginSettingsList->setStyleSheet("QTreeWidget::item {padding-right: 10px;}"); + + // display plugin settings + QSet<QString> handledNames; + for (IPlugin *plugin : m_parent->plugins()) { + if (handledNames.contains(plugin->name())) + continue; + QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), ui->pluginsList); + listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin)); + listItem->setData(Qt::UserRole + 1, m_parent->m_PluginSettings[plugin->name()]); + listItem->setData(Qt::UserRole + 2, m_parent->m_PluginDescriptions[plugin->name()]); + ui->pluginsList->addItem(listItem); + handledNames.insert(plugin->name()); + } + + // display plugin blacklist + for (const QString &pluginName : m_parent->m_PluginBlacklist) { + ui->pluginBlacklist->addItem(pluginName); + } + + QObject::connect( + ui->pluginsList, &QListWidget::currentItemChanged, + [&](auto* current, auto* previous) { on_pluginsList_currentItemChanged(current, previous); }); + + QShortcut *delShortcut = new QShortcut( + QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); + QObject::connect(delShortcut, &QShortcut::activated, parentWidget(), [&]{ deleteBlacklistItem(); }); +} + +void PluginsSettingsTab::update() +{ + // transfer plugin settings to in-memory structure + for (int i = 0; i < ui->pluginsList->count(); ++i) { + QListWidgetItem *item = ui->pluginsList->item(i); + m_parent->m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap(); + } + // store plugin settings on disc + for (auto iterPlugins = m_parent->m_PluginSettings.begin(); iterPlugins != m_parent->m_PluginSettings.end(); ++iterPlugins) { + for (auto iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) { + m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value()); + } + } + + // store plugin blacklist + m_parent->m_PluginBlacklist.clear(); + for (QListWidgetItem *item : ui->pluginBlacklist->findItems("*", Qt::MatchWildcard)) { + m_parent->m_PluginBlacklist.insert(item->text()); + } + m_parent->writePluginBlacklist(); +} + +void PluginsSettingsTab::closing() +{ + storeSettings(ui->pluginsList->currentItem()); +} + +void PluginsSettingsTab::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 PluginsSettingsTab::deleteBlacklistItem() +{ + ui->pluginBlacklist->takeItem(ui->pluginBlacklist->currentIndex().row()); +} + +void PluginsSettingsTab::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); + } +} diff --git a/src/settingsdialogplugins.h b/src/settingsdialogplugins.h new file mode 100644 index 00000000..9d21daa6 --- /dev/null +++ b/src/settingsdialogplugins.h @@ -0,0 +1,21 @@ +#ifndef SETTINGSDIALOGPLUGINS_H +#define SETTINGSDIALOGPLUGINS_H + +#include "settings.h" +#include "settingsdialog.h" + +class PluginsSettingsTab : public SettingsTab +{ +public: + PluginsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + + void update(); + void closing() override; + +private: + void on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void deleteBlacklistItem(); + void storeSettings(QListWidgetItem *pluginItem); +}; + +#endif // SETTINGSDIALOGPLUGINS_H diff --git a/src/settingsdialogsteam.cpp b/src/settingsdialogsteam.cpp new file mode 100644 index 00000000..34c2d76b --- /dev/null +++ b/src/settingsdialogsteam.cpp @@ -0,0 +1,17 @@ +#include "settingsdialogsteam.h" +#include "ui_settingsdialog.h" + +SteamSettingsTab::SteamSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) + : SettingsTab(m_parent, m_dialog) +{ + QString username, password; + m_parent->getSteamLogin(username, password); + + ui->steamUserEdit->setText(username); + ui->steamPassEdit->setText(password); +} + +void SteamSettingsTab::update() +{ + m_parent->setSteamLogin(ui->steamUserEdit->text(), ui->steamPassEdit->text()); +} diff --git a/src/settingsdialogsteam.h b/src/settingsdialogsteam.h new file mode 100644 index 00000000..dbd85151 --- /dev/null +++ b/src/settingsdialogsteam.h @@ -0,0 +1,17 @@ +#ifndef SETTINGSDIALOGSTEAM_H +#define SETTINGSDIALOGSTEAM_H + +#include "settings.h" +#include "settingsdialog.h" + +class SteamSettingsTab : public SettingsTab +{ +public: + SteamSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + + void update(); + +private: +}; + +#endif // SETTINGSDIALOGSTEAM_H diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp new file mode 100644 index 00000000..9ac46ac1 --- /dev/null +++ b/src/settingsdialogworkarounds.cpp @@ -0,0 +1,94 @@ +#include "settingsdialogworkarounds.h" +#include "ui_settingsdialog.h" +#include "helper.h" +#include <iplugingame.h> + +WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) + : SettingsTab(m_parent, m_dialog) +{ + ui->appIDEdit->setText(m_parent->getSteamAppID()); + + LoadMechanism::EMechanism mechanismID = m_parent->getLoadMechanism(); + int index = 0; + + if (m_parent->loadMechanism().isDirectLoadingSupported()) { + ui->mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER); + if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) { + index = ui->mechanismBox->count() - 1; + } + } + + ui->mechanismBox->setCurrentIndex(index); + + ui->hideUncheckedBox->setChecked(m_parent->hideUncheckedPlugins()); + ui->forceEnableBox->setChecked(m_parent->forceEnableCoreFiles()); + ui->displayForeignBox->setChecked(m_parent->displayForeign()); + ui->lockGUIBox->setChecked(m_parent->lockGUI()); + ui->enableArchiveParsingBox->setChecked(m_parent->archiveParsing()); + + ui->resetGeometryBtn->setChecked(m_parent->directInterface().value("reset_geometry", false).toBool()); + + setExecutableBlacklist(m_parent->executablesBlacklist()); + + QObject::connect(ui->bsaDateBtn, &QPushButton::clicked, [&]{ on_bsaDateBtn_clicked(); }); + QObject::connect(ui->execBlacklistBtn, &QPushButton::clicked, [&]{ on_execBlacklistBtn_clicked(); }); + QObject::connect(ui->resetGeometryBtn, &QPushButton::clicked, [&]{ on_resetGeometryBtn_clicked(); }); +} + +void WorkaroundsSettingsTab::update() +{ + if (ui->appIDEdit->text() != m_parent->gamePlugin()->steamAPPId()) { + m_Settings.setValue("Settings/app_id", ui->appIDEdit->text()); + } else { + m_Settings.remove("Settings/app_id"); + } + m_Settings.setValue("Settings/load_mechanism", ui->mechanismBox->itemData(ui->mechanismBox->currentIndex()).toInt()); + m_Settings.setValue("Settings/hide_unchecked_plugins", ui->hideUncheckedBox->isChecked()); + m_Settings.setValue("Settings/force_enable_core_files", ui->forceEnableBox->isChecked()); + m_Settings.setValue("Settings/display_foreign", ui->displayForeignBox->isChecked()); + m_Settings.setValue("Settings/lock_gui", ui->lockGUIBox->isChecked()); + m_Settings.setValue("Settings/archive_parsing_experimental", ui->enableArchiveParsingBox->isChecked()); + + m_Settings.setValue("Settings/executable_blacklist", getExecutableBlacklist()); +} + +void WorkaroundsSettingsTab::on_execBlacklistBtn_clicked() +{ + bool ok = false; + QString result = QInputDialog::getMultiLineText( + parentWidget(), + QObject::tr("Executables Blacklist"), + QObject::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 WorkaroundsSettingsTab::on_bsaDateBtn_clicked() +{ + const auto* game = qApp->property("managed_game").value<MOBase::IPluginGame*>(); + QDir dir = game->dataDirectory(); + + Helper::backdateBSAs(qApp->applicationDirPath().toStdWString(), + dir.absolutePath().toStdWString()); +} + +void WorkaroundsSettingsTab::on_resetGeometryBtn_clicked() +{ + m_dialog.m_GeometriesReset = true; + ui->resetGeometryBtn->setChecked(true); +} diff --git a/src/settingsdialogworkarounds.h b/src/settingsdialogworkarounds.h new file mode 100644 index 00000000..1687624b --- /dev/null +++ b/src/settingsdialogworkarounds.h @@ -0,0 +1,25 @@ +#ifndef SETTINGSDIALOGWORKAROUNDS_H +#define SETTINGSDIALOGWORKAROUNDS_H + +#include "settings.h" +#include "settingsdialog.h" + +class WorkaroundsSettingsTab : public SettingsTab +{ +public: + WorkaroundsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + + void update(); + +private: + QString m_ExecutableBlacklist; + + void on_bsaDateBtn_clicked(); + void on_execBlacklistBtn_clicked(); + void on_resetGeometryBtn_clicked(); + + QString getExecutableBlacklist() { return m_ExecutableBlacklist; } + void setExecutableBlacklist(QString blacklist) { m_ExecutableBlacklist = blacklist; } +}; + +#endif // SETTINGSDIALOGWORKAROUNDS_H |
