Rework launcher UI

7220-lua-add-a-general-purpose-lexical-parser
Andrei Kortunov 1 year ago
parent 58b8bf883a
commit 902c48d1bb

@ -33,6 +33,7 @@
Bug #7088: Deleting last save game of last character doesn't clear character name/details
Feature #5492: Let rain and snow collide with statics
Feature #6447: Add LOD support to Object Paging
Feature #6922: Improve launcher appearance
Feature #6933: Support high-resolution cursor textures
Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData
Feature #6979: Add support of loading and displaying LOD assets purely based on their filename extension

@ -820,12 +820,20 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE)
get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME)
get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME)
configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
get_property(QT_QMACSTYLE_PLUGIN_PATH TARGET Qt5::QMacStylePlugin PROPERTY LOCATION_RELEASE)
get_filename_component(QT_QMACSTYLE_PLUGIN_DIR "${QT_QMACSTYLE_PLUGIN_PATH}" DIRECTORY)
get_filename_component(QT_QMACSTYLE_PLUGIN_GROUP "${QT_QMACSTYLE_PLUGIN_DIR}" NAME)
get_filename_component(QT_QMACSTYLE_PLUGIN_NAME "${QT_QMACSTYLE_PLUGIN_PATH}" NAME)
configure_file("${QT_QMACSTYLE_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}" COPYONLY)
configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${APP_BUNDLE_DIR}/Contents/Resources/qt.conf" COPYONLY)
if (BUILD_OPENCS)
get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME)
set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app")
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
configure_file("${QT_QMACSTYLE_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}" COPYONLY)
configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${OPENCS_BUNDLE_NAME}/Contents/Resources/qt.conf" COPYONLY)
endif ()
@ -895,7 +903,9 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE)
install_plugins_for_bundle("${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}")
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}")
install(CODE "
function(gp_item_default_embedded_path_override item default_embedded_path_var)

@ -4,10 +4,9 @@ set(LAUNCHER
sdlinit.cpp
main.cpp
maindialog.cpp
playpage.cpp
textslotmsgbox.cpp
importpage.cpp
settingspage.cpp
advancedpage.cpp
utils/cellnameloader.cpp
utils/profilescombobox.cpp
@ -23,10 +22,9 @@ set(LAUNCHER_HEADER
graphicspage.hpp
sdlinit.hpp
maindialog.hpp
playpage.hpp
textslotmsgbox.hpp
importpage.hpp
settingspage.hpp
advancedpage.hpp
utils/cellnameloader.hpp
utils/profilescombobox.hpp
@ -40,10 +38,9 @@ set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
${CMAKE_SOURCE_DIR}/files/ui/importpage.ui
${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui
${CMAKE_SOURCE_DIR}/files/ui/advancedpage.ui
${CMAKE_SOURCE_DIR}/files/ui/directorypicker.ui
)

@ -1,490 +0,0 @@
#include "advancedpage.hpp"
#include <array>
#include <cmath>
#include <string>
#include <QCompleter>
#include <QFileDialog>
#include <QString>
#include <components/config/gamesettings.hpp>
#include "utils/openalutil.hpp"
Launcher::AdvancedPage::AdvancedPage(Config::GameSettings& gameSettings, QWidget* parent)
: QWidget(parent)
, mGameSettings(gameSettings)
{
setObjectName("AdvancedPage");
setupUi(this);
for (const std::string& name : Launcher::enumerateOpenALDevices())
{
audioDeviceSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
}
for (const std::string& name : Launcher::enumerateOpenALDevicesHrtf())
{
hrtfProfileSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
}
loadSettings();
mCellNameCompleter.setModel(&mCellNameCompleterModel);
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
}
void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames)
{
// Update the list of suggestions for the "Start default character at" field
mCellNameCompleterModel.setStringList(cellNames);
mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion);
mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
}
void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state)
{
startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked);
startDefaultCharacterAtField->setEnabled(state == Qt::Checked);
}
void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
{
QString scriptFile = QFileDialog::getOpenFileName(
this, QObject::tr("Select script file"), QDir::currentPath(), QString(tr("Text file (*.txt)")));
if (scriptFile.isEmpty())
return;
QFileInfo info(scriptFile);
if (!info.exists() || !info.isReadable())
return;
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
runScriptAfterStartupField->setText(path);
}
namespace
{
constexpr double CellSizeInUnits = 8192;
double convertToCells(double unitRadius)
{
return unitRadius / CellSizeInUnits;
}
int convertToUnits(double CellGridRadius)
{
return static_cast<int>(CellSizeInUnits * CellGridRadius);
}
}
bool Launcher::AdvancedPage::loadSettings()
{
// Game mechanics
{
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
loadSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game");
loadSettingBool(
requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
int unarmedFactorsStrengthIndex = Settings::Manager::getInt("strength influences hand to hand", "Game");
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
loadSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
loadSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
int numPhysicsThreads = Settings::Manager::getInt("async num threads", "Physics");
if (numPhysicsThreads >= 0)
physicsThreadsSpinBox->setValue(numPhysicsThreads);
loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game");
loadSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game");
const int actorCollisionShapeType = Settings::Manager::getInt("actor collision shape type", "Game");
if (0 <= actorCollisionShapeType && actorCollisionShapeType < actorCollisonShapeTypeComboBox->count())
actorCollisonShapeTypeComboBox->setCurrentIndex(actorCollisionShapeType);
}
// Visuals
{
loadSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders");
loadSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders");
loadSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders");
loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
loadSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
loadSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
if (Settings::Manager::getInt("antialiasing", "Video") == 0)
{
antialiasAlphaTestCheckBox->setCheckState(Qt::Unchecked);
}
loadSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders");
loadSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders");
loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
connect(animSourcesCheckBox, &QCheckBox::toggled, this, &AdvancedPage::slotAnimSourcesToggled);
loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
if (animSourcesCheckBox->checkState() != Qt::Unchecked)
{
loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
}
loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain");
if (distantTerrain && objectPaging)
{
distantLandCheckBox->setCheckState(Qt::Checked);
}
loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt("viewing distance", "Camera")));
objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain"));
loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
connect(postprocessEnabledCheckBox, &QCheckBox::toggled, this, &AdvancedPage::slotPostProcessToggled);
loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("auto exposure speed", "Post Processing"));
connect(skyBlendingCheckBox, &QCheckBox::toggled, this, &AdvancedPage::slotSkyBlendingToggled);
loadSettingBool(radialFogCheckBox, "radial fog", "Fog");
loadSettingBool(exponentialFogCheckBox, "exponential fog", "Fog");
loadSettingBool(skyBlendingCheckBox, "sky blending", "Fog");
skyBlendingStartComboBox->setValue(Settings::Manager::getDouble("sky blending start", "Fog"));
}
// Audio
{
const std::string& selectedAudioDevice = Settings::Manager::getString("device", "Sound");
if (selectedAudioDevice.empty() == false)
{
int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice));
if (audioDeviceIndex != -1)
{
audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex);
}
}
int hrtfEnabledIndex = Settings::Manager::getInt("hrtf enable", "Sound");
if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1)
{
enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1);
}
const std::string& selectedHRTFProfile = Settings::Manager::getString("hrtf", "Sound");
if (selectedHRTFProfile.empty() == false)
{
int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile));
if (hrtfProfileIndex != -1)
{
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
}
}
}
// Interface Changes
{
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
int showOwnedIndex = Settings::Manager::getInt("show owned", "Game");
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
loadSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI"));
fontSizeSpinBox->setValue(Settings::Manager::getInt("font size", "GUI"));
}
// Bug fixes
{
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(
trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game");
}
// Miscellaneous
{
// Saves
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
loadSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves");
// Other Settings
QString screenshotFormatString
= QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper();
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
screenshotFormatComboBox->addItem(screenshotFormatString);
screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString));
loadSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General");
}
// Testing
{
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1;
if (skipMenu)
{
skipMenuCheckBox->setCheckState(Qt::Checked);
}
startDefaultCharacterAtLabel->setEnabled(skipMenu);
startDefaultCharacterAtField->setEnabled(skipMenu);
startDefaultCharacterAtField->setText(mGameSettings.value("start"));
runScriptAfterStartupField->setText(mGameSettings.value("script-run"));
}
return true;
}
void Launcher::AdvancedPage::saveSettings()
{
// Game mechanics
{
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
saveSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game");
saveSettingBool(
requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
saveSettingInt(unarmedFactorsStrengthComboBox, "strength influences hand to hand", "Game");
saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
saveSettingInt(physicsThreadsSpinBox, "async num threads", "Physics");
saveSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game");
saveSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game");
saveSettingInt(actorCollisonShapeTypeComboBox, "actor collision shape type", "Game");
}
// Visuals
{
saveSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders");
saveSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders");
saveSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders");
saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
saveSettingBool(radialFogCheckBox, "radial fog", "Fog");
saveSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
saveSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
saveSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders");
saveSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders");
saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain");
const bool wantDistantLand = distantLandCheckBox->checkState();
if (wantDistantLand != (distantTerrain && objectPaging))
{
Settings::Manager::setBool("distant terrain", "Terrain", wantDistantLand);
Settings::Manager::setBool("object paging", "Terrain", wantDistantLand);
}
saveSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
int viewingDistance = convertToUnits(viewingDistanceComboBox->value());
if (viewingDistance != Settings::Manager::getInt("viewing distance", "Camera"))
{
Settings::Manager::setInt("viewing distance", "Camera", viewingDistance);
}
double objectPagingMinSize = objectPagingMinSizeComboBox->value();
if (objectPagingMinSize != Settings::Manager::getDouble("object paging min size", "Terrain"))
Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize);
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
double hdrExposureTime = postprocessHDRTimeComboBox->value();
if (hdrExposureTime != Settings::Manager::getDouble("auto exposure speed", "Post Processing"))
Settings::Manager::setDouble("auto exposure speed", "Post Processing", hdrExposureTime);
saveSettingBool(radialFogCheckBox, "radial fog", "Fog");
saveSettingBool(exponentialFogCheckBox, "exponential fog", "Fog");
saveSettingBool(skyBlendingCheckBox, "sky blending", "Fog");
Settings::Manager::setDouble("sky blending start", "Fog", skyBlendingStartComboBox->value());
}
// Audio
{
int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex();
const std::string& prevAudioDevice = Settings::Manager::getString("device", "Sound");
if (audioDeviceIndex != 0)
{
const std::string& newAudioDevice = audioDeviceSelectorComboBox->currentText().toUtf8().constData();
if (newAudioDevice != prevAudioDevice)
Settings::Manager::setString("device", "Sound", newAudioDevice);
}
else if (!prevAudioDevice.empty())
{
Settings::Manager::setString("device", "Sound", {});
}
int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1;
if (hrtfEnabledIndex != Settings::Manager::getInt("hrtf enable", "Sound"))
{
Settings::Manager::setInt("hrtf enable", "Sound", hrtfEnabledIndex);
}
int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex();
const std::string& prevHRTFProfile = Settings::Manager::getString("hrtf", "Sound");
if (selectedHRTFProfileIndex != 0)
{
const std::string& newHRTFProfile = hrtfProfileSelectorComboBox->currentText().toUtf8().constData();
if (newHRTFProfile != prevHRTFProfile)
Settings::Manager::setString("hrtf", "Sound", newHRTFProfile);
}
else if (!prevHRTFProfile.empty())
{
Settings::Manager::setString("hrtf", "Sound", {});
}
}
// Interface Changes
{
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
saveSettingInt(showOwnedComboBox, "show owned", "Game");
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
float uiScalingFactor = scalingSpinBox->value();
if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI"))
Settings::Manager::setFloat("scaling factor", "GUI", uiScalingFactor);
int fontSize = fontSizeSpinBox->value();
if (fontSize != Settings::Manager::getInt("font size", "GUI"))
Settings::Manager::setInt("font size", "GUI", fontSize);
}
// Bug fixes
{
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(
trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game");
}
// Miscellaneous
{
// Saves Settings
saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
saveSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves");
// Other Settings
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
if (screenshotFormatString != Settings::Manager::getString("screenshot format", "General"))
Settings::Manager::setString("screenshot format", "General", screenshotFormatString);
saveSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General");
}
// Testing
{
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked;
if (skipMenu != mGameSettings.value("skip-menu").toInt())
mGameSettings.setValue("skip-menu", QString::number(skipMenu));
QString startCell = startDefaultCharacterAtField->text();
if (startCell != mGameSettings.value("start"))
{
mGameSettings.setValue("start", startCell);
}
QString scriptRun = runScriptAfterStartupField->text();
if (scriptRun != mGameSettings.value("script-run"))
mGameSettings.setValue("script-run", scriptRun);
}
}
void Launcher::AdvancedPage::loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group)
{
if (Settings::Manager::getBool(setting, group))
checkbox->setCheckState(Qt::Checked);
}
void Launcher::AdvancedPage::saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group)
{
bool cValue = checkbox->checkState();
if (cValue != Settings::Manager::getBool(setting, group))
Settings::Manager::setBool(setting, group, cValue);
}
void Launcher::AdvancedPage::loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group)
{
int currentIndex = Settings::Manager::getInt(setting, group);
comboBox->setCurrentIndex(currentIndex);
}
void Launcher::AdvancedPage::saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group)
{
int currentIndex = comboBox->currentIndex();
if (currentIndex != Settings::Manager::getInt(setting, group))
Settings::Manager::setInt(setting, group, currentIndex);
}
void Launcher::AdvancedPage::loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group)
{
int value = Settings::Manager::getInt(setting, group);
spinBox->setValue(value);
}
void Launcher::AdvancedPage::saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group)
{
int value = spinBox->value();
if (value != Settings::Manager::getInt(setting, group))
Settings::Manager::setInt(setting, group, value);
}
void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
{
loadCellsForAutocomplete(cellNames);
}
void Launcher::AdvancedPage::slotAnimSourcesToggled(bool checked)
{
weaponSheathingCheckBox->setEnabled(checked);
shieldSheathingCheckBox->setEnabled(checked);
if (!checked)
{
weaponSheathingCheckBox->setCheckState(Qt::Unchecked);
shieldSheathingCheckBox->setCheckState(Qt::Unchecked);
}
}
void Launcher::AdvancedPage::slotPostProcessToggled(bool checked)
{
postprocessTransparentPostpassCheckBox->setEnabled(checked);
postprocessHDRTimeComboBox->setEnabled(checked);
postprocessHDRTimeLabel->setEnabled(checked);
}
void Launcher::AdvancedPage::slotSkyBlendingToggled(bool checked)
{
skyBlendingStartComboBox->setEnabled(checked);
skyBlendingStartLabel->setEnabled(checked);
}

@ -1,56 +0,0 @@
#ifndef ADVANCEDPAGE_H
#define ADVANCEDPAGE_H
#include <QCompleter>
#include <QStringListModel>
#include "ui_advancedpage.h"
#include <components/settings/settings.hpp>
namespace Config
{
class GameSettings;
}
namespace Launcher
{
class AdvancedPage : public QWidget, private Ui::AdvancedPage
{
Q_OBJECT
public:
explicit AdvancedPage(Config::GameSettings& gameSettings, QWidget* parent = nullptr);
bool loadSettings();
void saveSettings();
public slots:
void slotLoadedCellsChanged(QStringList cellNames);
private slots:
void on_skipMenuCheckBox_stateChanged(int state);
void on_runScriptAfterStartupBrowseButton_clicked();
void slotAnimSourcesToggled(bool checked);
void slotPostProcessToggled(bool checked);
void slotSkyBlendingToggled(bool checked);
private:
Config::GameSettings& mGameSettings;
QCompleter mCellNameCompleter;
QStringListModel mCellNameCompleterModel;
/**
* Load the cells associated with the given content files for use in autocomplete
* @param filePaths the file paths of the content files to be examined
*/
void loadCellsForAutocomplete(QStringList filePaths);
static void loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group);
static void saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group);
static void loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group);
static void saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group);
static void loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group);
static void saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group);
};
}
#endif

@ -112,6 +112,12 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager& cfg, Config:
const QString encoding = mGameSettings.value("encoding", "win1252");
mSelector->setEncoding(encoding);
QStringList languages;
languages << tr("English") << tr("French") << tr("German") << tr("Italian") << tr("Polish") << tr("Russian")
<< tr("Spanish");
mSelector->languageBox()->addItems(languages);
mNewProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this);
mCloneProfileDialog = new TextInputDialog(tr("Clone Content List"), tr("Content List name:"), this);
@ -196,6 +202,11 @@ bool Launcher::DataFilesPage::loadSettings()
if (!currentProfile.isEmpty())
addProfile(currentProfile, true);
QString language(mLauncherSettings.value(QLatin1String("Settings/language")));
int index = mSelector->languageBox()->findText(language);
if (index != -1)
mSelector->languageBox()->setCurrentIndex(index);
return true;
}
@ -340,6 +351,23 @@ void Launcher::DataFilesPage::saveSettings(const QString& profile)
}
mLauncherSettings.setContentList(profileName, dirList, selectedArchivePaths(), fileNames);
mGameSettings.setContentList(dirList, selectedArchivePaths(), fileNames);
QString language(mSelector->languageBox()->currentText());
mLauncherSettings.setValue(QLatin1String("Settings/language"), language);
if (language == QLatin1String("Polish"))
{
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250"));
}
else if (language == QLatin1String("Russian"))
{
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251"));
}
else
{
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252"));
}
}
QStringList Launcher::DataFilesPage::selectedDirectoriesPaths() const

@ -0,0 +1,229 @@
#include "importpage.hpp"
#include <QDebug>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <components/files/qtconversion.hpp>
#include "utils/textinputdialog.hpp"
using namespace Process;
Launcher::ImportPage::ImportPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
Config::LauncherSettings& launcherSettings, MainDialog* parent)
: QWidget(parent)
, mCfgMgr(cfg)
, mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings)
, mMain(parent)
{
setupUi(this);
mWizardInvoker = new ProcessInvoker();
mImporterInvoker = new ProcessInvoker();
resetProgressBar();
connect(mWizardInvoker->getProcess(), &QProcess::started, this, &ImportPage::wizardStarted);
connect(mWizardInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
&ImportPage::wizardFinished);
connect(mImporterInvoker->getProcess(), &QProcess::started, this, &ImportPage::importerStarted);
connect(mImporterInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
&ImportPage::importerFinished);
// Detect Morrowind configuration files
QStringList iniPaths;
for (const QString& path : mGameSettings.getDataDirs())
{
QDir dir(path);
dir.setPath(dir.canonicalPath()); // Resolve symlinks
if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
else
{
if (!dir.cdUp())
continue; // Cannot move from Data Files
if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
}
}
if (!iniPaths.isEmpty())
{
settingsComboBox->addItems(iniPaths);
importerButton->setEnabled(true);
}
else
{
importerButton->setEnabled(false);
}
loadSettings();
}
Launcher::ImportPage::~ImportPage()
{
delete mWizardInvoker;
delete mImporterInvoker;
}
void Launcher::ImportPage::on_wizardButton_clicked()
{
mMain->writeSettings();
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false))
return;
}
void Launcher::ImportPage::on_importerButton_clicked()
{
mMain->writeSettings();
// Create the file if it doesn't already exist, else the importer will fail
auto path = mCfgMgr.getUserConfigPath();
path /= "openmw.cfg";
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFile file(path);
#else
QFile file(Files::pathToQString(path));
#endif
if (!file.exists())
{
if (!file.open(QIODevice::ReadWrite))
{
// File cannot be created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(
tr("<html><head/><body><p><b>Could not open or create %1 for writing </b></p> \
<p>Please make sure you have the right permissions \
and try again.</p></body></html>")
.arg(file.fileName()));
msgBox.exec();
return;
}
file.close();
}
// Construct the arguments to run the importer
QStringList arguments;
if (addonsCheckBox->isChecked())
arguments.append(QString("--game-files"));
if (fontsCheckBox->isChecked())
arguments.append(QString("--fonts"));
arguments.append(QString("--encoding"));
arguments.append(mGameSettings.value(QString("encoding"), QString("win1252")));
arguments.append(QString("--ini"));
arguments.append(settingsComboBox->currentText());
arguments.append(QString("--cfg"));
arguments.append(Files::pathToQString(path));
qDebug() << "arguments " << arguments;
// start the progress bar as a "bouncing ball"
progressBar->setMaximum(0);
progressBar->setValue(0);
if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false))
{
resetProgressBar();
}
}
void Launcher::ImportPage::on_browseButton_clicked()
{
QString iniFile = QFileDialog::getOpenFileName(this, QObject::tr("Select configuration file"), QDir::currentPath(),
QString(tr("Morrowind configuration file (*.ini)")));
if (iniFile.isEmpty())
return;
QFileInfo info(iniFile);
if (!info.exists() || !info.isReadable())
return;
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
if (settingsComboBox->findText(path) == -1)
{
settingsComboBox->addItem(path);
settingsComboBox->setCurrentIndex(settingsComboBox->findText(path));
importerButton->setEnabled(true);
}
}
void Launcher::ImportPage::wizardStarted()
{
mMain->hide(); // Hide the launcher
wizardButton->setEnabled(false);
}
void Launcher::ImportPage::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
return qApp->quit();
mMain->reloadSettings();
wizardButton->setEnabled(true);
mMain->show(); // Show the launcher again
}
void Launcher::ImportPage::importerStarted()
{
importerButton->setEnabled(false);
}
void Launcher::ImportPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
{
resetProgressBar();
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Importer finished"));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Warning);
msgBox.setText(tr("Failed to import settings from INI file."));
msgBox.exec();
}
else
{
// indicate progress finished
progressBar->setMaximum(1);
progressBar->setValue(1);
// Importer may have changed settings, so refresh
mMain->reloadSettings();
}
importerButton->setEnabled(true);
}
void Launcher::ImportPage::resetProgressBar()
{
// set progress bar to 0 %
progressBar->reset();
}
void Launcher::ImportPage::saveSettings() {}
bool Launcher::ImportPage::loadSettings()
{
return true;
}

@ -0,0 +1,64 @@
#ifndef IMPORTSPAGE_HPP
#define IMPORTSPAGE_HPP
#include <components/process/processinvoker.hpp>
#include "ui_importpage.h"
#include "maindialog.hpp"
namespace Files
{
struct ConfigurationManager;
}
namespace Config
{
class GameSettings;
class LauncherSettings;
}
namespace Launcher
{
class TextInputDialog;
class ImportPage : public QWidget, private Ui::ImportPage
{
Q_OBJECT
public:
ImportPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
Config::LauncherSettings& launcherSettings, MainDialog* parent = nullptr);
~ImportPage() override;
void saveSettings();
bool loadSettings();
/// set progress bar on page to 0%
void resetProgressBar();
private slots:
void on_wizardButton_clicked();
void on_importerButton_clicked();
void on_browseButton_clicked();
void wizardStarted();
void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);
void importerStarted();
void importerFinished(int exitCode, QProcess::ExitStatus exitStatus);
private:
Process::ProcessInvoker* mWizardInvoker;
Process::ProcessInvoker* mImporterInvoker;
Files::ConfigurationManager& mCfgMgr;
Config::GameSettings& mGameSettings;
Config::LauncherSettings& mLauncherSettings;
MainDialog* mMain;
};
}
#endif // IMPORTSPAGE_HPP

@ -15,10 +15,9 @@
#include <components/files/qtconversion.hpp>
#include <components/misc/utf8qtextstream.hpp>
#include "advancedpage.hpp"
#include "datafilespage.hpp"
#include "graphicspage.hpp"
#include "playpage.hpp"
#include "importpage.hpp"
#include "settingspage.hpp"
using namespace Process;
@ -47,21 +46,13 @@ Launcher::MainDialog::MainDialog(QWidget* parent)
connect(mWizardInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
&MainDialog::wizardFinished);
iconWidget->setViewMode(QListView::IconMode);
iconWidget->setWrapping(false);
iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
iconWidget->setIconSize(QSize(48, 48));
iconWidget->setMovement(QListView::Static);
iconWidget->setSpacing(4);
iconWidget->setCurrentRow(0);
iconWidget->setFlow(QListView::LeftToRight);
auto* helpButton = new QPushButton(tr("Help"));
auto* playButton = new QPushButton(tr("Play"));
buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close"));
buttonBox->addButton(helpButton, QDialogButtonBox::HelpRole);
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
buttonBox->button(QDialogButtonBox::Ok)->setText(tr(" Launch OpenMW "));
buttonBox->button(QDialogButtonBox::Help)->setText(tr("Help"));
// Order of buttons can be different on different setups,
// so make sure that the Play button has a focus by default.
buttonBox->button(QDialogButtonBox::Ok)->setFocus();
connect(buttonBox, &QDialogButtonBox::rejected, this, &MainDialog::close);
connect(buttonBox, &QDialogButtonBox::accepted, this, &MainDialog::play);
@ -84,37 +75,10 @@ void Launcher::MainDialog::createIcons()
if (!QIcon::hasThemeIcon("document-new"))
QIcon::setThemeName("tango");
auto* playButton = new QListWidgetItem(iconWidget);
playButton->setIcon(QIcon(":/images/openmw.png"));
playButton->setText(tr("Play"));
playButton->setTextAlignment(Qt::AlignCenter);
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
auto* dataFilesButton = new QListWidgetItem(iconWidget);
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
dataFilesButton->setText(tr("Data Files"));
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
auto* graphicsButton = new QListWidgetItem(iconWidget);
graphicsButton->setIcon(QIcon(":/images/preferences-video.png"));
graphicsButton->setText(tr("Graphics"));
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
auto* settingsButton = new QListWidgetItem(iconWidget);
settingsButton->setIcon(QIcon(":/images/preferences.png"));
settingsButton->setText(tr("Settings"));
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
auto* advancedButton = new QListWidgetItem(iconWidget);
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png"));
advancedButton->setText(tr("Advanced"));
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
connect(iconWidget, &QListWidget::currentItemChanged, this, &MainDialog::changePage);
connect(dataAction, &QAction::triggered, this, &MainDialog::enableDataPage);
connect(graphicsAction, &QAction::triggered, this, &MainDialog::enableGraphicsPage);
connect(settingsAction, &QAction::triggered, this, &MainDialog::enableSettingsPage);
connect(importAction, &QAction::triggered, this, &MainDialog::enableImportPage);
}
void Launcher::MainDialog::createPages()
@ -123,33 +87,23 @@ void Launcher::MainDialog::createPages()
if (pagesWidget->count() != 0)
return;
mPlayPage = new PlayPage(this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mGraphicsPage = new GraphicsPage(this);
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mAdvancedPage = new AdvancedPage(mGameSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex());
mImportPage = new ImportPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mSettingsPage = new SettingsPage(mGameSettings, this);
// Add the pages to the stacked widget
pagesWidget->addWidget(mPlayPage);
pagesWidget->addWidget(mDataFilesPage);
pagesWidget->addWidget(mGraphicsPage);
pagesWidget->addWidget(mSettingsPage);
pagesWidget->addWidget(mAdvancedPage);
pagesWidget->addWidget(mImportPage);
// Select the first page
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
dataAction->setChecked(true);
connect(mPlayPage, &PlayPage::playButtonClicked, this, &MainDialog::play);
connect(mPlayPage, &PlayPage::signalProfileChanged, mDataFilesPage, &DataFilesPage::slotProfileChanged);
connect(mDataFilesPage, &DataFilesPage::signalProfileChanged, mPlayPage, &PlayPage::setProfilesIndex);
// Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread
connect(mDataFilesPage, &DataFilesPage::signalLoadedCellsChanged, mAdvancedPage,
&AdvancedPage::slotLoadedCellsChanged, Qt::QueuedConnection);
connect(mDataFilesPage, &DataFilesPage::signalLoadedCellsChanged, mSettingsPage,
&SettingsPage::slotLoadedCellsChanged, Qt::QueuedConnection);
}
Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
@ -270,7 +224,7 @@ bool Launcher::MainDialog::reloadSettings()
if (!setupGraphicsSettings())
return false;
if (!mSettingsPage->loadSettings())
if (!mImportPage->loadSettings())
return false;
if (!mDataFilesPage->loadSettings())
@ -279,20 +233,50 @@ bool Launcher::MainDialog::reloadSettings()
if (!mGraphicsPage->loadSettings())
return false;
if (!mAdvancedPage->loadSettings())
if (!mSettingsPage->loadSettings())
return false;
return true;
}
void Launcher::MainDialog::changePage(QListWidgetItem* current, QListWidgetItem* previous)
void Launcher::MainDialog::enableDataPage()
{
if (!current)
current = previous;
pagesWidget->setCurrentIndex(0);
mImportPage->resetProgressBar();
dataAction->setChecked(true);
graphicsAction->setChecked(false);
importAction->setChecked(false);
settingsAction->setChecked(false);
}
int currentIndex = iconWidget->row(current);
pagesWidget->setCurrentIndex(currentIndex);
mSettingsPage->resetProgressBar();
void Launcher::MainDialog::enableGraphicsPage()
{
pagesWidget->setCurrentIndex(1);
mImportPage->resetProgressBar();
dataAction->setChecked(false);
graphicsAction->setChecked(true);
settingsAction->setChecked(false);
importAction->setChecked(false);
}
void Launcher::MainDialog::enableSettingsPage()
{
pagesWidget->setCurrentIndex(2);
mImportPage->resetProgressBar();
dataAction->setChecked(false);
graphicsAction->setChecked(false);
settingsAction->setChecked(true);
importAction->setChecked(false);
}
void Launcher::MainDialog::enableImportPage()
{
pagesWidget->setCurrentIndex(3);
mImportPage->resetProgressBar();
dataAction->setChecked(false);
graphicsAction->setChecked(false);
settingsAction->setChecked(false);
importAction->setChecked(true);
}
bool Launcher::MainDialog::setupLauncherSettings()
@ -490,8 +474,8 @@ bool Launcher::MainDialog::writeSettings()
saveSettings();
mDataFilesPage->saveSettings();
mGraphicsPage->saveSettings();
mImportPage->saveSettings();
mSettingsPage->saveSettings();
mAdvancedPage->saveSettings();
const auto& userPath = mCfgMgr.getUserConfigPath();

@ -20,12 +20,11 @@ class QString;
namespace Launcher
{
class PlayPage;
class GraphicsPage;
class DataFilesPage;
class UnshieldThread;
class ImportPage;
class SettingsPage;
class AdvancedPage;
enum FirstRunDialogResult
{
@ -52,7 +51,10 @@ namespace Launcher
bool writeSettings();
public slots:
void changePage(QListWidgetItem* current, QListWidgetItem* previous);
void enableDataPage();
void enableGraphicsPage();
void enableSettingsPage();
void enableImportPage();
void play();
void help();
@ -84,11 +86,10 @@ namespace Launcher
void closeEvent(QCloseEvent* event) override;
PlayPage* mPlayPage;
GraphicsPage* mGraphicsPage;
DataFilesPage* mDataFilesPage;
ImportPage* mImportPage;
SettingsPage* mSettingsPage;
AdvancedPage* mAdvancedPage;
Process::ProcessInvoker* mGameInvoker;
Process::ProcessInvoker* mWizardInvoker;

@ -1,30 +0,0 @@
#include "playpage.hpp"
#include <QListView>
Launcher::PlayPage::PlayPage(QWidget* parent)
: QWidget(parent)
{
setObjectName("PlayPage");
setupUi(this);
profilesComboBox->setView(new QListView());
connect(profilesComboBox, qOverload<int>(&QComboBox::activated), this, &PlayPage::signalProfileChanged);
connect(playButton, &QPushButton::clicked, this, &PlayPage::slotPlayClicked);
}
void Launcher::PlayPage::setProfilesModel(QAbstractItemModel* model)
{
profilesComboBox->setModel(model);
}
void Launcher::PlayPage::setProfilesIndex(int index)
{
profilesComboBox->setCurrentIndex(index);
}
void Launcher::PlayPage::slotPlayClicked()
{
emit playButtonClicked();
}

@ -1,31 +0,0 @@
#ifndef PLAYPAGE_H
#define PLAYPAGE_H
#include "ui_playpage.h"
class QComboBox;
class QPushButton;
class QAbstractItemModel;
namespace Launcher
{
class PlayPage : public QWidget, private Ui::PlayPage
{
Q_OBJECT
public:
PlayPage(QWidget* parent = nullptr);
void setProfilesModel(QAbstractItemModel* model);
signals:
void signalProfileChanged(int index);
void playButtonClicked();
public slots:
void setProfilesIndex(int index);
private slots:
void slotPlayClicked();
};
}
#endif

@ -1,278 +1,490 @@
#include "settingspage.hpp"
#include <QDebug>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <array>
#include <cmath>
#include <string>
#include <components/files/qtconversion.hpp>
#include <QCompleter>
#include <QFileDialog>
#include <QString>
#include "utils/textinputdialog.hpp"
#include <components/config/gamesettings.hpp>
using namespace Process;
#include "utils/openalutil.hpp"
Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
Config::LauncherSettings& launcherSettings, MainDialog* parent)
Launcher::SettingsPage::SettingsPage(Config::GameSettings& gameSettings, QWidget* parent)
: QWidget(parent)
, mCfgMgr(cfg)
, mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings)
, mMain(parent)
{
setObjectName("SettingsPage");
setupUi(this);
QStringList languages;
languages << tr("English") << tr("French") << tr("German") << tr("Italian") << tr("Polish") << tr("Russian")
<< tr("Spanish");
for (const std::string& name : Launcher::enumerateOpenALDevices())
{
audioDeviceSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
}
for (const std::string& name : Launcher::enumerateOpenALDevicesHrtf())
{
hrtfProfileSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
}
loadSettings();
mCellNameCompleter.setModel(&mCellNameCompleterModel);
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
}
void Launcher::SettingsPage::loadCellsForAutocomplete(QStringList cellNames)
{
// Update the list of suggestions for the "Start default character at" field
mCellNameCompleterModel.setStringList(cellNames);
mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion);
mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
}
languageComboBox->addItems(languages);
void Launcher::SettingsPage::on_skipMenuCheckBox_stateChanged(int state)
{
startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked);
startDefaultCharacterAtField->setEnabled(state == Qt::Checked);
}
mWizardInvoker = new ProcessInvoker();
mImporterInvoker = new ProcessInvoker();
resetProgressBar();
void Launcher::SettingsPage::on_runScriptAfterStartupBrowseButton_clicked()
{
QString scriptFile = QFileDialog::getOpenFileName(
this, QObject::tr("Select script file"), QDir::currentPath(), QString(tr("Text file (*.txt)")));
connect(mWizardInvoker->getProcess(), &QProcess::started, this, &SettingsPage::wizardStarted);
if (scriptFile.isEmpty())
return;
connect(mWizardInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
&SettingsPage::wizardFinished);
QFileInfo info(scriptFile);
connect(mImporterInvoker->getProcess(), &QProcess::started, this, &SettingsPage::importerStarted);
if (!info.exists() || !info.isReadable())
return;
connect(mImporterInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
&SettingsPage::importerFinished);
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
runScriptAfterStartupField->setText(path);
}
mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this);
namespace
{
constexpr double CellSizeInUnits = 8192;
connect(mProfileDialog->lineEdit(), &LineEdit::textChanged, this, &SettingsPage::updateOkButton);
double convertToCells(double unitRadius)
{
return unitRadius / CellSizeInUnits;
}
// Detect Morrowind configuration files
QStringList iniPaths;
int convertToUnits(double CellGridRadius)
{
return static_cast<int>(CellSizeInUnits * CellGridRadius);
}
}
for (const QString& path : mGameSettings.getDataDirs())
bool Launcher::SettingsPage::loadSettings()
{
// Game mechanics
{
QDir dir(path);
dir.setPath(dir.canonicalPath()); // Resolve symlinks
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
loadSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game");
loadSettingBool(
requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
int unarmedFactorsStrengthIndex = Settings::Manager::getInt("strength influences hand to hand", "Game");
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
loadSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
loadSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
int numPhysicsThreads = Settings::Manager::getInt("async num threads", "Physics");
if (numPhysicsThreads >= 0)
physicsThreadsSpinBox->setValue(numPhysicsThreads);
loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game");
loadSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game");
const int actorCollisionShapeType = Settings::Manager::getInt("actor collision shape type", "Game");
if (0 <= actorCollisionShapeType && actorCollisionShapeType < actorCollisonShapeTypeComboBox->count())
actorCollisonShapeTypeComboBox->setCurrentIndex(actorCollisionShapeType);
}
if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
else
// Visuals
{
loadSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders");
loadSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders");
loadSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders");
loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
loadSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
loadSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
if (Settings::Manager::getInt("antialiasing", "Video") == 0)
{
if (!dir.cdUp())
continue; // Cannot move from Data Files
antialiasAlphaTestCheckBox->setCheckState(Qt::Unchecked);
}
loadSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders");
loadSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders");
loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
connect(animSourcesCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotAnimSourcesToggled);
loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
if (animSourcesCheckBox->checkState() != Qt::Unchecked)
{
loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
}
loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain");
if (distantTerrain && objectPaging)
{
distantLandCheckBox->setCheckState(Qt::Checked);
}
loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt("viewing distance", "Camera")));
objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain"));
loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
connect(postprocessEnabledCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotPostProcessToggled);
loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("auto exposure speed", "Post Processing"));
connect(skyBlendingCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotSkyBlendingToggled);
loadSettingBool(radialFogCheckBox, "radial fog", "Fog");
loadSettingBool(exponentialFogCheckBox, "exponential fog", "Fog");
loadSettingBool(skyBlendingCheckBox, "sky blending", "Fog");
skyBlendingStartComboBox->setValue(Settings::Manager::getDouble("sky blending start", "Fog"));
}
if (!iniPaths.isEmpty())
// Audio
{
settingsComboBox->addItems(iniPaths);
importerButton->setEnabled(true);
const std::string& selectedAudioDevice = Settings::Manager::getString("device", "Sound");
if (selectedAudioDevice.empty() == false)
{
int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice));
if (audioDeviceIndex != -1)
{
audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex);
}
}
int hrtfEnabledIndex = Settings::Manager::getInt("hrtf enable", "Sound");
if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1)
{
enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1);
}
const std::string& selectedHRTFProfile = Settings::Manager::getString("hrtf", "Sound");
if (selectedHRTFProfile.empty() == false)
{
int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile));
if (hrtfProfileIndex != -1)
{
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
}
}
}
else
// Interface Changes
{
importerButton->setEnabled(false);
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
int showOwnedIndex = Settings::Manager::getInt("show owned", "Game");
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
loadSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI"));
fontSizeSpinBox->setValue(Settings::Manager::getInt("font size", "GUI"));
}
loadSettings();
}
// Bug fixes
{
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(
trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game");
}
Launcher::SettingsPage::~SettingsPage()
{
delete mWizardInvoker;
delete mImporterInvoker;
}
// Miscellaneous
{
// Saves
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
loadSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves");
// Other Settings
QString screenshotFormatString
= QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper();
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
screenshotFormatComboBox->addItem(screenshotFormatString);
screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString));
loadSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General");
}
void Launcher::SettingsPage::on_wizardButton_clicked()
{
mMain->writeSettings();
// Testing
{
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false))
return;
bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1;
if (skipMenu)
{
skipMenuCheckBox->setCheckState(Qt::Checked);
}
startDefaultCharacterAtLabel->setEnabled(skipMenu);
startDefaultCharacterAtField->setEnabled(skipMenu);
startDefaultCharacterAtField->setText(mGameSettings.value("start"));
runScriptAfterStartupField->setText(mGameSettings.value("script-run"));
}
return true;
}
void Launcher::SettingsPage::on_importerButton_clicked()
void Launcher::SettingsPage::saveSettings()
{
mMain->writeSettings();
// Create the file if it doesn't already exist, else the importer will fail
auto path = mCfgMgr.getUserConfigPath();
path /= "openmw.cfg";
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QFile file(path);
#else
QFile file(Files::pathToQString(path));
#endif
if (!file.exists())
// Game mechanics
{
if (!file.open(QIODevice::ReadWrite))
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
saveSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game");
saveSettingBool(
requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game");
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
saveSettingInt(unarmedFactorsStrengthComboBox, "strength influences hand to hand", "Game");
saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
saveSettingInt(physicsThreadsSpinBox, "async num threads", "Physics");
saveSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game");
saveSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game");
saveSettingInt(actorCollisonShapeTypeComboBox, "actor collision shape type", "Game");
}
// Visuals
{
saveSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders");
saveSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders");
saveSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders");
saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
saveSettingBool(radialFogCheckBox, "radial fog", "Fog");
saveSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
saveSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
saveSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders");
saveSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders");
saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game");
saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game");
saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game");
const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain");
const bool wantDistantLand = distantLandCheckBox->checkState();
if (wantDistantLand != (distantTerrain && objectPaging))
{
// File cannot be created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(
tr("<html><head/><body><p><b>Could not open or create %1 for writing </b></p> \
<p>Please make sure you have the right permissions \
and try again.</p></body></html>")
.arg(file.fileName()));
msgBox.exec();
return;
Settings::Manager::setBool("distant terrain", "Terrain", wantDistantLand);
Settings::Manager::setBool("object paging", "Terrain", wantDistantLand);
}
file.close();
saveSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
int viewingDistance = convertToUnits(viewingDistanceComboBox->value());
if (viewingDistance != Settings::Manager::getInt("viewing distance", "Camera"))
{
Settings::Manager::setInt("viewing distance", "Camera", viewingDistance);
}
double objectPagingMinSize = objectPagingMinSizeComboBox->value();
if (objectPagingMinSize != Settings::Manager::getDouble("object paging min size", "Terrain"))
Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize);
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
double hdrExposureTime = postprocessHDRTimeComboBox->value();
if (hdrExposureTime != Settings::Manager::getDouble("auto exposure speed", "Post Processing"))
Settings::Manager::setDouble("auto exposure speed", "Post Processing", hdrExposureTime);
saveSettingBool(radialFogCheckBox, "radial fog", "Fog");
saveSettingBool(exponentialFogCheckBox, "exponential fog", "Fog");
saveSettingBool(skyBlendingCheckBox, "sky blending", "Fog");
Settings::Manager::setDouble("sky blending start", "Fog", skyBlendingStartComboBox->value());
}
// Construct the arguments to run the importer
QStringList arguments;
if (addonsCheckBox->isChecked())
arguments.append(QString("--game-files"));
if (fontsCheckBox->isChecked())
arguments.append(QString("--fonts"));
arguments.append(QString("--encoding"));
arguments.append(mGameSettings.value(QString("encoding"), QString("win1252")));
arguments.append(QString("--ini"));
arguments.append(settingsComboBox->currentText());
arguments.append(QString("--cfg"));
arguments.append(Files::pathToQString(path));
// Audio
{
int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex();
const std::string& prevAudioDevice = Settings::Manager::getString("device", "Sound");
if (audioDeviceIndex != 0)
{
const std::string& newAudioDevice = audioDeviceSelectorComboBox->currentText().toUtf8().constData();
if (newAudioDevice != prevAudioDevice)
Settings::Manager::setString("device", "Sound", newAudioDevice);
}
else if (!prevAudioDevice.empty())
{
Settings::Manager::setString("device", "Sound", {});
}
int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1;
if (hrtfEnabledIndex != Settings::Manager::getInt("hrtf enable", "Sound"))
{
Settings::Manager::setInt("hrtf enable", "Sound", hrtfEnabledIndex);
}
int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex();
const std::string& prevHRTFProfile = Settings::Manager::getString("hrtf", "Sound");
if (selectedHRTFProfileIndex != 0)
{
const std::string& newHRTFProfile = hrtfProfileSelectorComboBox->currentText().toUtf8().constData();
if (newHRTFProfile != prevHRTFProfile)
Settings::Manager::setString("hrtf", "Sound", newHRTFProfile);
}
else if (!prevHRTFProfile.empty())
{
Settings::Manager::setString("hrtf", "Sound", {});
}
}
qDebug() << "arguments " << arguments;
// Interface Changes
{
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
saveSettingInt(showOwnedComboBox, "show owned", "Game");
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
float uiScalingFactor = scalingSpinBox->value();
if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI"))
Settings::Manager::setFloat("scaling factor", "GUI", uiScalingFactor);
int fontSize = fontSizeSpinBox->value();
if (fontSize != Settings::Manager::getInt("font size", "GUI"))
Settings::Manager::setInt("font size", "GUI", fontSize);
}
// start the progress bar as a "bouncing ball"
progressBar->setMaximum(0);
progressBar->setValue(0);
if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false))
// Bug fixes
{
resetProgressBar();
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(
trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game");
}
}
void Launcher::SettingsPage::on_browseButton_clicked()
{
QString iniFile = QFileDialog::getOpenFileName(this, QObject::tr("Select configuration file"), QDir::currentPath(),
QString(tr("Morrowind configuration file (*.ini)")));
// Miscellaneous
{
// Saves Settings
saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
saveSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves");
if (iniFile.isEmpty())
return;
// Other Settings
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
if (screenshotFormatString != Settings::Manager::getString("screenshot format", "General"))
Settings::Manager::setString("screenshot format", "General", screenshotFormatString);
QFileInfo info(iniFile);
saveSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General");
}
if (!info.exists() || !info.isReadable())
return;
// Testing
{
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked;
if (skipMenu != mGameSettings.value("skip-menu").toInt())
mGameSettings.setValue("skip-menu", QString::number(skipMenu));
if (settingsComboBox->findText(path) == -1)
{
settingsComboBox->addItem(path);
settingsComboBox->setCurrentIndex(settingsComboBox->findText(path));
importerButton->setEnabled(true);
QString startCell = startDefaultCharacterAtField->text();
if (startCell != mGameSettings.value("start"))
{
mGameSettings.setValue("start", startCell);
}
QString scriptRun = runScriptAfterStartupField->text();
if (scriptRun != mGameSettings.value("script-run"))
mGameSettings.setValue("script-run", scriptRun);
}
}
void Launcher::SettingsPage::wizardStarted()
void Launcher::SettingsPage::loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group)
{
mMain->hide(); // Hide the launcher
wizardButton->setEnabled(false);
if (Settings::Manager::getBool(setting, group))
checkbox->setCheckState(Qt::Checked);
}
void Launcher::SettingsPage::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus)
void Launcher::SettingsPage::saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group)
{
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
return qApp->quit();
mMain->reloadSettings();
wizardButton->setEnabled(true);
mMain->show(); // Show the launcher again
bool cValue = checkbox->checkState();
if (cValue != Settings::Manager::getBool(setting, group))
Settings::Manager::setBool(setting, group, cValue);
}
void Launcher::SettingsPage::importerStarted()
void Launcher::SettingsPage::loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group)
{
importerButton->setEnabled(false);
int currentIndex = Settings::Manager::getInt(setting, group);
comboBox->setCurrentIndex(currentIndex);
}
void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
void Launcher::SettingsPage::saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group)
{
if (exitCode != 0 || exitStatus == QProcess::CrashExit)
{
resetProgressBar();
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Importer finished"));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Warning);
msgBox.setText(tr("Failed to import settings from INI file."));
msgBox.exec();
}
else
{
// indicate progress finished
progressBar->setMaximum(1);
progressBar->setValue(1);
// Importer may have changed settings, so refresh
mMain->reloadSettings();
}
importerButton->setEnabled(true);
int currentIndex = comboBox->currentIndex();
if (currentIndex != Settings::Manager::getInt(setting, group))
Settings::Manager::setInt(setting, group, currentIndex);
}
void Launcher::SettingsPage::resetProgressBar()
void Launcher::SettingsPage::loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group)
{
// set progress bar to 0 %
progressBar->reset();
int value = Settings::Manager::getInt(setting, group);
spinBox->setValue(value);
}
void Launcher::SettingsPage::updateOkButton(const QString& text)
void Launcher::SettingsPage::saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group)
{
// We do this here because we need to access the profiles
if (text.isEmpty())
{
mProfileDialog->setOkButtonEnabled(false);
return;
}
const QStringList profiles(mLauncherSettings.getContentLists());
(profiles.contains(text)) ? mProfileDialog->setOkButtonEnabled(false) : mProfileDialog->setOkButtonEnabled(true);
int value = spinBox->value();
if (value != Settings::Manager::getInt(setting, group))
Settings::Manager::setInt(setting, group, value);
}
void Launcher::SettingsPage::saveSettings()
void Launcher::SettingsPage::slotLoadedCellsChanged(QStringList cellNames)
{
QString language(languageComboBox->currentText());
mLauncherSettings.setValue(QLatin1String("Settings/language"), language);
loadCellsForAutocomplete(cellNames);
}
if (language == QLatin1String("Polish"))
{
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250"));
}
else if (language == QLatin1String("Russian"))
{
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251"));
}
else
void Launcher::SettingsPage::slotAnimSourcesToggled(bool checked)
{
weaponSheathingCheckBox->setEnabled(checked);
shieldSheathingCheckBox->setEnabled(checked);
if (!checked)
{
mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252"));
weaponSheathingCheckBox->setCheckState(Qt::Unchecked);
shieldSheathingCheckBox->setCheckState(Qt::Unchecked);
}
}
bool Launcher::SettingsPage::loadSettings()
void Launcher::SettingsPage::slotPostProcessToggled(bool checked)
{
QString language(mLauncherSettings.value(QLatin1String("Settings/language")));
int index = languageComboBox->findText(language);
if (index != -1)
languageComboBox->setCurrentIndex(index);
postprocessTransparentPostpassCheckBox->setEnabled(checked);
postprocessHDRTimeComboBox->setEnabled(checked);
postprocessHDRTimeLabel->setEnabled(checked);
}
return true;
void Launcher::SettingsPage::slotSkyBlendingToggled(bool checked)
{
skyBlendingStartComboBox->setEnabled(checked);
skyBlendingStartLabel->setEnabled(checked);
}

@ -1,67 +1,56 @@
#ifndef SETTINGSPAGE_HPP
#define SETTINGSPAGE_HPP
#ifndef SETTINGSPAGE_H
#define SETTINGSPAGE_H
#include <components/process/processinvoker.hpp>
#include <QCompleter>
#include <QStringListModel>
#include "ui_settingspage.h"
#include "maindialog.hpp"
#include <components/settings/settings.hpp>
namespace Files
{
struct ConfigurationManager;
}
namespace Config
{
class GameSettings;
class LauncherSettings;
}
namespace Launcher
{
class TextInputDialog;
class SettingsPage : public QWidget, private Ui::SettingsPage
{
Q_OBJECT
public:
SettingsPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
Config::LauncherSettings& launcherSettings, MainDialog* parent = nullptr);
~SettingsPage() override;
explicit SettingsPage(Config::GameSettings& gameSettings, QWidget* parent = nullptr);
void saveSettings();
bool loadSettings();
void saveSettings();
/// set progress bar on page to 0%
void resetProgressBar();
public slots:
void slotLoadedCellsChanged(QStringList cellNames);
private slots:
void on_wizardButton_clicked();
void on_importerButton_clicked();
void on_browseButton_clicked();
void wizardStarted();
void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus);
void importerStarted();
void importerFinished(int exitCode, QProcess::ExitStatus exitStatus);
void updateOkButton(const QString& text);
void on_skipMenuCheckBox_stateChanged(int state);
void on_runScriptAfterStartupBrowseButton_clicked();
void slotAnimSourcesToggled(bool checked);
void slotPostProcessToggled(bool checked);
void slotSkyBlendingToggled(bool checked);
private:
Process::ProcessInvoker* mWizardInvoker;
Process::ProcessInvoker* mImporterInvoker;
Files::ConfigurationManager& mCfgMgr;
Config::GameSettings& mGameSettings;
Config::LauncherSettings& mLauncherSettings;
MainDialog* mMain;
TextInputDialog* mProfileDialog;
QCompleter mCellNameCompleter;
QStringListModel mCellNameCompleterModel;
/**
* Load the cells associated with the given content files for use in autocomplete
* @param filePaths the file paths of the content files to be examined
*/
void loadCellsForAutocomplete(QStringList filePaths);
static void loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group);
static void saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group);
static void loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group);
static void saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group);
static void loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group);
static void saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group);
};
}
#endif // SETTINGSPAGE_HPP
#endif

@ -9,9 +9,7 @@ LineEdit::LineEdit(QWidget* parent)
void LineEdit::setupClearButton()
{
mClearButton = new QToolButton(this);
QPixmap pixmap(":images/clear.png");
mClearButton->setIcon(QIcon(pixmap));
mClearButton->setIconSize(pixmap.size());
mClearButton->setIcon(QIcon::fromTheme("edit-clear"));
mClearButton->setCursor(Qt::ArrowCursor);
mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }");
mClearButton->hide();

@ -13,6 +13,12 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget* parent, bool show
ui.setupUi(parent);
ui.addonView->setDragDropMode(QAbstractItemView::InternalMove);
if (!showOMWScripts)
{
ui.languageComboBox->setHidden(true);
ui.refreshButton->setHidden(true);
}
buildContentModel(showOMWScripts);
buildGameFileView();
buildAddonView();

@ -43,6 +43,8 @@ namespace ContentSelectorView
QWidget* uiWidget() const { return ui.contentGroupBox; }
QComboBox* languageBox() const { return ui.languageComboBox; }
QToolButton* refreshButton() const { return ui.refreshButton; }
QLineEdit* searchFilter() const { return ui.searchFilter; }

Before

Width:  |  Height:  |  Size: 699 B

After

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

@ -2,10 +2,7 @@
Name=Tango
Comment=Tango Theme
Inherits=default
Directories=16x16,48x48
Directories=16x16
[16x16]
Size=16
[48x48]
Size=48
Size=16

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

@ -1,26 +1,18 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="images">
<file alias="clear.png">images/clear.png</file>
<file alias="down.png">images/down.png</file>
<file alias="openmw.png">images/openmw.png</file>
<file alias="openmw-plugin.png">images/openmw-plugin.png</file>
<file alias="openmw-header.png">images/openmw-header.png</file>
<file alias="openmw-plugin.png">images/openmw-plugin.png</file>
<file alias="preferences.png">images/preferences.png</file>
<file alias="preferences-advanced.png">images/preferences-advanced.png</file>
<file alias="preferences-video.png">images/preferences-video.png</file>
<file alias="playpage-background.png">images/playpage-background.png</file>
</qresource>
<qresource prefix="icons/tango">
<file alias="index.theme">icons/tango/index.theme</file>
<file alias="48x48/emblem-system.png">icons/tango/48x48/emblem-system.png</file>
<file alias="48x48/preferences-system.png">icons/tango/48x48/preferences-system.png</file>
<file alias="48x48/video-display.png">icons/tango/48x48/video-display.png</file>
<file alias="16x16/document-new.png">icons/tango/16x16/document-new.png</file>
<file alias="16x16/edit-clear.png">icons/tango/16x16/edit-clear.png</file>
<file alias="16x16/edit-copy.png">icons/tango/16x16/edit-copy.png</file>
<file alias="16x16/edit-delete.png">icons/tango/16x16/edit-delete.png</file>
<file alias="16x16/go-bottom.png">icons/tango/16x16/go-bottom.png</file>
<file alias="16x16/go-down.png">icons/tango/16x16/go-down.png</file>
<file alias="16x16/go-top.png">icons/tango/16x16/go-top.png</file>
<file alias="16x16/go-up.png">icons/tango/16x16/go-up.png</file>
<file alias="16x16/view-refresh.png">icons/tango/16x16/view-refresh.png</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>518</width>
<width>614</width>
<height>436</height>
</rect>
</property>
@ -66,6 +66,19 @@
<item>
<widget class="QLineEdit" name="searchFilter"/>
</item>
<item>
<widget class="QComboBox" name="languageComboBox">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Select language used by ESM/ESP content files to allow OpenMW to detect their encoding. </string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="refreshButton">
<property name="enabled">

@ -48,8 +48,33 @@
</property>
</widget>
</item>
<item row="32" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;note: directories that are not part of current Content List are &lt;/span&gt;&lt;span style=&quot; font-style:italic; background-color:#00ff00;&quot;&gt;highlighted&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="directoryAddSubdirsButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="toolTip">
<string>Scan directories for likely data directories and append them at the end of the list.</string>
</property>
@ -58,8 +83,20 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="4" column="1">
<widget class="QPushButton" name="directoryInsertButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="toolTip">
<string>Scan directories for likely data directories and insert them above the selected position</string>
</property>
@ -68,8 +105,20 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="8" column="1">
<widget class="QPushButton" name="directoryUpButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="toolTip">
<string>Move selected directory one position up</string>
</property>
@ -78,8 +127,20 @@
</property>
</widget>
</item>
<item row="3" column="1">
<item row="12" column="1">
<widget class="QPushButton" name="directoryDownButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="toolTip">
<string>Move selected directory one position down</string>
</property>
@ -88,8 +149,20 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="16" column="1">
<widget class="QPushButton" name="directoryRemoveButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="toolTip">
<string>Remove selected directory</string>
</property>
@ -98,19 +171,6 @@
</property>
</widget>
</item>
<item row="27" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;note: directories that are not part of current Content List are &lt;/span&gt;&lt;span style=&quot; font-style:italic; background-color:#00ff00;&quot;&gt;highlighted&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="archiveTab">
@ -127,6 +187,18 @@
</item>
<item row="0" column="1">
<widget class="QPushButton" name="archiveUpButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>33</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="toolTip">
<string>Move selected archive one position up</string>
</property>
@ -135,8 +207,27 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="27" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;note: archives that are not part of current Content List are &lt;/span&gt;&lt;span style=&quot; font-style:italic; background-color:#00ff00;&quot;&gt;highlighted&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="archiveDownButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>33</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<property name="toolTip">
<string>Move selected archive one position down</string>
</property>
@ -145,13 +236,6 @@
</property>
</widget>
</item>
<item row="27" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;note: archives that are not part of current Content List are &lt;/span&gt;&lt;span style=&quot; font-style:italic; background-color:#00ff00;&quot;&gt;highlighted&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
@ -339,8 +423,7 @@
<action name="newProfileAction">
<property name="icon">
<iconset theme="document-new">
<normaloff/>
</iconset>
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>New Content List</string>
@ -355,8 +438,7 @@
<action name="cloneProfileAction">
<property name="icon">
<iconset theme="edit-copy">
<normaloff/>
</iconset>
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Clone Content List</string>
@ -374,8 +456,7 @@
</property>
<property name="icon">
<iconset theme="edit-delete">
<normaloff/>
</iconset>
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Delete Content List</string>
@ -403,8 +484,7 @@
<action name="refreshDataFilesAction">
<property name="icon">
<iconset theme="view-refresh">
<normaloff/>
</iconset>
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Refresh Data Files</string>

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImportPage</class>
<widget class="QWidget" name="ImportPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>514</width>
<height>397</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="wizardGroup">
<property name="title">
<string>Morrowind Installation Wizard</string>
</property>
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,1">
<item row="1" column="1">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="wizardButton">
<property name="text">
<string>Run &amp;Installation Wizard</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="importerGroup">
<property name="title">
<string>Morrowind Settings Importer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0" columnstretch="1,0">
<item row="0" column="0">
<widget class="QLabel" name="importerLabel">
<property name="text">
<string>File to import settings from:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="settingsComboBox"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="browseButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="addonsCheckBox">
<property name="text">
<string>Import add-on and plugin selection (creates a new Content List)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="fontsCheckBox">
<property name="toolTip">
<string>Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters,
so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar
to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts.</string>
</property>
<property name="text">
<string>Import bitmap fonts setup</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
<item row="2" column="1">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="importerButton">
<property name="text">
<string>Run &amp;Settings Importer</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>4</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -25,32 +25,6 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QListWidget" name="iconWidget">
<property name="minimumSize">
<size>
<width>400</width>
<height>80</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#iconWidget {
background-image: url(&quot;:/images/openmw-header.png&quot;);
background-color: palette(base);
background-repeat: no-repeat;
background-attachment: scroll;
background-position: right;
}
</string>
</property>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="pagesWidget"/>
</item>
@ -86,7 +60,7 @@
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
<set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@ -94,6 +68,119 @@
</item>
</layout>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="styleSheet">
<string notr="true">QToolBar {
background-attachment: fixed;
background-image: url(:/images/openmw-header.png);
background-repeat: none;
background-position: right;
border: 0px;
}
QToolButton {
min-width: 70px;
}
</string>
</property>
<property name="movable">
<bool>false</bool>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
<property name="floatable">
<bool>false</bool>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="dataAction"/>
<addaction name="graphicsAction"/>
<addaction name="settingsAction"/>
<addaction name="importAction"/>
</widget>
<action name="dataAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../launcher/launcher.qrc">
<normaloff>:/images/openmw-plugin.png</normaloff>:/images/openmw-plugin.png</iconset>
</property>
<property name="text">
<string>Data Files</string>
</property>
<property name="toolTip">
<string>Allows to setup data files and directories</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="whatsThis">
<string/>
</property>
</action>
<action name="graphicsAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../launcher/launcher.qrc">
<normaloff>:/images/preferences-video.png</normaloff>:/images/preferences-video.png</iconset>
</property>
<property name="text">
<string>Graphics</string>
</property>
<property name="toolTip">
<string>Allows to change graphics settings</string>
</property>
</action>
<action name="settingsAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../launcher/launcher.qrc">
<normaloff>:/images/preferences.png</normaloff>:/images/preferences.png</iconset>
</property>
<property name="text">
<string>Settings</string>
</property>
<property name="toolTip">
<string>Allows to tweak engine settings</string>
</property>
</action>
<action name="importAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../launcher/launcher.qrc">
<normaloff>:/images/preferences-advanced.png</normaloff>:/images/preferences-advanced.png</iconset>
</property>
<property name="text">
<string>Import</string>
</property>
<property name="toolTip">
<string>Allows to import data from original engine</string>
</property>
</action>
</widget>
<resources>
<include location="../launcher/launcher.qrc"/>

@ -1,189 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PlayPage</class>
<widget class="QWidget" name="PlayPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>274</width>
<height>317</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="Scroll" native="true">
<property name="styleSheet">
<string notr="true">#Scroll {
background-image: url(&quot;:/images/playpage-background.png&quot;);
background-repeat: no-repeat;
background-position: top;
}
</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>30</number>
</property>
<property name="topMargin">
<number>100</number>
</property>
<property name="rightMargin">
<number>30</number>
</property>
<item row="4" column="1">
<widget class="QComboBox" name="profilesComboBox">
<property name="styleSheet">
<string notr="true">#profilesComboBox {
padding: 1px 18px 1px 3px;
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white);
border-width: 1px;
border-color: rgba(0, 0, 0, 125);
border-style: solid;
border-radius: 2px;
font-size: 12pt;
color: black;
}
/*QComboBox gets the &quot;on&quot; state when the popup is open */
#profilesComboBox:!editable:on {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(0, 0, 0, 75),
stop:0.1 rgba(0, 0, 0, 15),
stop:0.2 rgba(255, 255, 255, 55));
border: 1px solid rgba(0, 0, 0, 55);
}
#profilesComboBox:on { /* shift the text when the popup opens */
padding-top: 3px;
padding-left: 4px;
}
#profilesComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
border-width: 1px;
border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid; /* just a single line */
border-top-right-radius: 3px; /* same radius as the QComboBox */
border-bottom-right-radius: 3px;
}
#profilesComboBox::down-arrow {
image: url(&quot;:/images/down.png&quot;);
}
#profilesComboBox::down-arrow:on { /* shift the arrow when popup is open */
top: 1px;
left: 1px;
}
#profilesComboBox QAbstractItemView {
border: 0px;
}</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="profileLabel">
<property name="styleSheet">
<string notr="true">#profileLabel {
font-size: 18pt;
color: black;
}
</string>
</property>
<property name="text">
<string>Current Content List:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="playButton">
<property name="minimumSize">
<size>
<width>200</width>
<height>85</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>85</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#playButton {
height: 50px;
margin-bottom: 30px;
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255, 255, 255, 200),
stop:0.1 rgba(255, 255, 255, 15),
stop:0.49 rgba(255, 255, 255, 75),
stop:0.5 rgba(0, 0, 0, 0),
stop:0.9 rgba(0, 0, 0, 55),
stop:1 rgba(0, 0, 0, 100));
font-size: 26pt;
color: black;
border-right: 1px solid rgba(0, 0, 0, 155);
border-left: 1px solid rgba(0, 0, 0, 55);
border-top: 1px solid rgba(0, 0, 0, 55);
border-bottom: 1px solid rgba(0, 0, 0, 155);
border-radius: 5px;
}
#playButton:hover {
border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-width: 2px;
border-style: solid;
}
#playButton:pressed {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(0, 0, 0, 75),
stop:0.1 rgba(0, 0, 0, 15),
stop:0.2 rgba(255, 255, 255, 55)
stop:0.95 rgba(255, 255, 255, 55),
stop:1 rgba(255, 255, 255, 155));
border: 1px solid rgba(0, 0, 0, 55);
}</string>
</property>
<property name="text">
<string>Play</string>
</property>
</widget>
</item>
<item row="5" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save