1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-20 19:21:13 +00:00

Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor

This commit is contained in:
Zackhasacat 2024-01-12 12:01:44 -06:00
commit f3229f8674
518 changed files with 7571 additions and 4143 deletions

View File

@ -142,7 +142,7 @@ Ubuntu_GCC:
variables:
CC: gcc
CXX: g++
CCACHE_SIZE: 4G
CCACHE_SIZE: 3G
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
timeout: 2h
@ -193,7 +193,7 @@ Ubuntu_GCC_Debug:
variables:
CC: gcc
CXX: g++
CCACHE_SIZE: 4G
CCACHE_SIZE: 3G
CMAKE_BUILD_TYPE: Debug
CMAKE_CXX_FLAGS_DEBUG: -O0
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.

View File

@ -245,6 +245,7 @@ Programmers
xyzz
Yohaulticetl
Yuri Krupenin
Yury Stepovikov
zelurker
Documentation

View File

@ -10,11 +10,15 @@
Bug #4382: Sound output device does not change when it should
Bug #4508: Can't stack enchantment buffs from different instances of the same self-cast generic magic apparel
Bug #4610: Casting a Bound Weapon spell cancels the casting animation by equipping the weapon prematurely
Bug #4683: Disposition decrease when player commits crime is not implemented properly
Bug #4742: Actors with wander never stop walking after Loopgroup Walkforward
Bug #4743: PlayGroup doesn't play non-looping animations correctly
Bug #4754: Stack of ammunition cannot be equipped partially
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
Bug #4822: Non-weapon equipment and body parts can't inherit time from parent animation
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
Bug #5062: Root bone rotations for NPC animation don't work the same as for creature animation
Bug #5066: Quirks with starting and stopping scripted animations
Bug #5129: Stuttering animation on Centurion Archer
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
@ -28,12 +32,15 @@
Bug #6190: Unintuitive sun specularity time of day dependence
Bug #6222: global map cell size can crash openmw if set to too high a value
Bug #6313: Followers with high Fight can turn hostile
Bug #6402: The sound of a thunderstorm does not stop playing after entering the premises
Bug #6427: Enemy health bar disappears before damaging effect ends
Bug #6550: Cloned body parts don't inherit texture effects
Bug #6645: Enemy block sounds align with animation instead of blocked hits
Bug #6657: Distant terrain tiles become black when using FWIW mod
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
Bug #6716: mwscript comparison operator handling is too restrictive
Bug #6754: Beast to Non-beast transformation mod is not working on OpenMW
Bug #6758: Main menu background video can be stopped by opening the options menu
Bug #6807: Ultimate Galleon is not working properly
Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands
Bug #6894: Added item combines with equipped stack instead of creating a new unequipped stack
@ -58,6 +65,7 @@
Bug #7084: Resurrecting an actor doesn't take into account base record changes
Bug #7088: Deleting last save game of last character doesn't clear character name/details
Bug #7092: BSA archives from higher priority directories don't take priority
Bug #7103: Multiple paths pointing to the same plugin but with different cases lead to automatically removed config entries
Bug #7122: Teleportation to underwater should cancel active water walking effect
Bug #7131: MyGUI log spam when post processing HUD is open
Bug #7134: Saves with an invalid last generated RefNum can be loaded
@ -67,38 +75,63 @@
Bug #7229: Error marker loading failure is not handled
Bug #7243: Supporting loading external files from VFS from esm files
Bug #7284: "Your weapon has no effect." message doesn't always show when the player character attempts to attack
Bug #7292: Weather settings for disabling or enabling snow and rain ripples don't work
Bug #7298: Water ripples from projectiles sometimes are not spawned
Bug #7307: Alchemy "Magic Effect" search string does not match on tool tip for effects related to attributes
Bug #7309: Sunlight scattering is visible in inappropriate situations
Bug #7322: Shadows don't cover groundcover depending on the view angle and perspective with compute scene bounds = primitives
Bug #7354: Disabling post processing in-game causes a crash
Bug #7364: Post processing is not reflected in savegame previews
Bug #7380: NiZBufferProperty issue
Bug #7413: Generated wilderness cells don't spawn fish
Bug #7415: Unbreakable lock discrepancies
Bug #7416: Modpccrimelevel is different from vanilla
Bug #7428: AutoCalc flag is not used to calculate enchantment costs
Bug #7450: Evading obstacles does not work for actors missing certain animations
Bug #7459: Icons get stacked on the cursor when picking up multiple items simultaneously
Bug #7472: Crash when enchanting last projectiles
Bug #7475: Equipping a constant effect item doesn't update the magic menu
Bug #7502: Data directories dialog (0.48.0) forces adding subdirectory instead of intended directory
Bug #7505: Distant terrain does not support sample size greater than cell size
Bug #7553: Faction reaction loading is incorrect
Bug #7557: Terrain::ChunkManager::createChunk is called twice for the same position, lod on initial loading
Bug #7573: Drain Fatigue can't bring fatigue below zero by default
Bug #7585: Difference in interior lighting between OpenMW with legacy lighting method enabled and vanilla Morrowind
Bug #7603: Scripts menu size is not updated properly
Bug #7604: Goblins Grunt becomes idle once injured
Bug #7609: ForceGreeting should not open dialogue for werewolves
Bug #7611: Beast races' idle animations slide after turning or jumping in place
Bug #7617: The death prompt asks the player if they wanted to load the character's last created save
Bug #7619: Long map notes may get cut off
Bug #7630: Charm can be cast on creatures
Bug #7631: Cannot trade with/talk to Creeper or Mudcrab Merchant when they're fleeing
Bug #7636: Animations bug out when switching between 1st and 3rd person, while playing a scripted animation
Bug #7637: Actors can sometimes move while playing scripted animations
Bug #7639: NPCs don't use hand-to-hand if their other melee skills were damaged during combat
Bug #7641: loopgroup loops the animation one time too many for actors
Bug #7642: Items in repair and recharge menus aren't sorted alphabetically
Bug #7643: Can't enchant items with constant effect on self magic effects for non-player character
Bug #7646: Follower voices pain sounds when attacked with magic
Bug #7647: NPC walk cycle bugs after greeting player
Bug #7654: Tooltips for enchantments with invalid effects cause crashes
Bug #7660: Some inconsistencies regarding Invisibility breaking
Bug #7661: Player followers should stop attacking newly recruited actors
Bug #7665: Alchemy menu is missing the ability to deselect and choose different qualities of an apparatus
Bug #7675: Successful lock spell doesn't produce a sound
Bug #7676: Incorrect magic effect order in alchemy
Bug #7679: Scene luminance value flashes when toggling shaders
Bug #7685: Corky sometimes doesn't follow Llovyn Andus
Bug #7712: Casting doesn't support spells and enchantments with no effects
Bug #7723: Assaulting vampires and werewolves shouldn't be a crime
Bug #7724: Guards don't help vs werewolves
Bug #7733: Launcher shows incorrect data paths when there's two plugins with the same name
Bug #7742: Governing attribute training limit should use the modified attribute
Bug #7758: Water walking is not taken into account to compute path cost on the water
Bug #7761: Rain and ambient loop sounds are mutually exclusive
Bug #7770: Sword of the Perithia: Script execution failure
Feature #2566: Handle NAM9 records for manual cell references
Feature #3537: Shader-based water ripples
Feature #5173: Support for NiFogProperty
Feature #5492: Let rain and snow collide with statics
Feature #6149: Dehardcode Lua API_REVISION
Feature #6152: Playing music via lua scripts
@ -127,14 +160,17 @@
Feature #7477: NegativeLight Magic Effect flag
Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field
Feature #7546: Start the game on Fredas
Feature #7554: Controller binding for tab for menu navigation
Feature #7568: Uninterruptable scripted music
Feature #7608: Make the missing dependencies warning when loading a savegame more helpful
Feature #7618: Show the player character's health in the save details
Feature #7625: Add some missing console error outputs
Feature #7634: Support NiParticleBomb
Feature #7652: Sort inactive post processing shaders list properly
Feature #7698: Implement sAbsorb, sDamage, sDrain, sFortify and sRestore
Feature #7709: Improve resolution selection in Launcher
Task #5896: Do not use deprecated MyGUI properties
Task #6624: Drop support for saves made prior to 0.45
Task #7113: Move from std::atoi to std::from_char
Task #7117: Replace boost::scoped_array with std::vector
Task #7151: Do not use std::strerror to get errno error message

View File

@ -1,31 +1,26 @@
#!/bin/sh -ex
export HOMEBREW_NO_EMOJI=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
export HOMEBREW_AUTOREMOVE=1
brew uninstall --ignore-dependencies python@3.8 || true
brew uninstall --ignore-dependencies python@3.9 || true
brew uninstall --ignore-dependencies qt@6 || true
brew uninstall --ignore-dependencies jpeg || true
# workaround for gitlab's pre-installed brew
# purge large and unnecessary packages that get in our way and have caused issues
brew uninstall ruby php openjdk node postgresql maven curl || true
brew tap --repair
brew update --quiet
# Some of these tools can come from places other than brew, so check before installing
brew reinstall xquartz fontconfig freetype harfbuzz brotli
# Fix: can't open file: @loader_path/libbrotlicommon.1.dylib (No such file or directory)
BREW_LIB_PATH="$(brew --prefix)/lib"
install_name_tool -change "@loader_path/libbrotlicommon.1.dylib" "${BREW_LIB_PATH}/libbrotlicommon.1.dylib" ${BREW_LIB_PATH}/libbrotlidec.1.dylib
install_name_tool -change "@loader_path/libbrotlicommon.1.dylib" "${BREW_LIB_PATH}/libbrotlicommon.1.dylib" ${BREW_LIB_PATH}/libbrotlienc.1.dylib
brew install curl xquartz gd fontconfig freetype harfbuzz brotli
command -v ccache >/dev/null 2>&1 || brew install ccache
command -v cmake >/dev/null 2>&1 || brew install cmake
command -v qmake >/dev/null 2>&1 || brew install qt@5
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
# Install deps
brew install icu4c yaml-cpp sqlite
brew install openal-soft icu4c yaml-cpp sqlite
ccache --version
cmake --version

View File

@ -902,7 +902,6 @@ printf "Qt ${QT_VER}... "
fi
cd $QT_SDK
add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe"
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
if [ $CONFIGURATION == "Debug" ]; then
DLLSUFFIX="d"

View File

@ -10,12 +10,13 @@ DEPENDENCIES_ROOT="/tmp/openmw-deps"
QT_PATH=$(brew --prefix qt@5)
ICU_PATH=$(brew --prefix icu4c)
OPENAL_PATH=$(brew --prefix openal-soft)
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
mkdir build
cd build
cmake \
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH;$OPENAL_PATH" \
-D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \
-D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \
-D CMAKE_CXX_FLAGS="-stdlib=libc++" \

View File

@ -19,6 +19,7 @@ apps/openmw_test_suite/lua/test_serialization.cpp
apps/openmw_test_suite/lua/test_storage.cpp
apps/openmw_test_suite/lua/test_ui_content.cpp
apps/openmw_test_suite/lua/test_utilpackage.cpp
apps/openmw_test_suite/lua/test_inputactions.cpp
apps/openmw_test_suite/misc/test_endianness.cpp
apps/openmw_test_suite/misc/test_resourcehelpers.cpp
apps/openmw_test_suite/misc/test_stringops.cpp

View File

@ -1,4 +1,4 @@
set -e
#!/bin/bash -e
docs/source/install_luadocumentor_in_docker.sh
PATH=$PATH:~/luarocks/bin

View File

@ -54,6 +54,7 @@ IF(NOT CMAKE_BUILD_TYPE)
ENDIF()
if (APPLE)
set(CMAKE_FIND_FRAMEWORK LAST) # prefer dylibs over frameworks
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")

View File

@ -194,7 +194,8 @@ int extract(std::unique_ptr<File>& bsa, Arguments& info)
// Get a stream for the file to extract
for (auto it = bsa->getList().rbegin(); it != bsa->getList().rend(); ++it)
{
if (Misc::StringUtils::ciEqual(Misc::StringUtils::stringToU8String(it->name()), archivePath))
auto streamPath = Misc::StringUtils::stringToU8String(it->name());
if (Misc::StringUtils::ciEqual(streamPath, archivePath) || Misc::StringUtils::ciEqual(streamPath, extractPath))
{
stream = bsa->getFile(&*it);
break;

View File

@ -145,12 +145,12 @@ namespace
config.filterOutNonExistingPaths(dataDirs);
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
const auto& resDir = variables["resources"].as<Files::MaybeQuotedPath>();
Log(Debug::Info) << Version::getOpenmwVersionDescription();
dataDirs.insert(dataDirs.begin(), resDir / "vfs");
const auto fileCollections = Files::Collections(dataDirs);
const auto archives = variables["fallback-archive"].as<StringsVector>();
const auto contentFiles = variables["content"].as<StringsVector>();
const Files::Collections fileCollections(dataDirs);
const auto& archives = variables["fallback-archive"].as<StringsVector>();
const auto& contentFiles = variables["content"].as<StringsVector>();
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap);

View File

@ -156,7 +156,7 @@ Allowed options)");
return false;
}*/
const auto inputFiles = variables["input-file"].as<Files::MaybeQuotedPathContainer>();
const auto& inputFiles = variables["input-file"].as<Files::MaybeQuotedPathContainer>();
info.filename = inputFiles[0].u8string(); // This call to u8string is redundant, but required to build on
// MSVC 14.26 due to implementation bugs.
if (inputFiles.size() > 1)
@ -265,7 +265,7 @@ namespace
std::cout << " Faction rank: " << ref.mFactionRank << '\n';
std::cout << " Enchantment charge: " << ref.mEnchantmentCharge << '\n';
std::cout << " Uses/health: " << ref.mChargeInt << '\n';
std::cout << " Gold value: " << ref.mGoldValue << '\n';
std::cout << " Count: " << ref.mCount << '\n';
std::cout << " Blocked: " << static_cast<int>(ref.mReferenceBlocked) << '\n';
std::cout << " Deleted: " << deleted << '\n';
if (!ref.mKey.empty())
@ -341,7 +341,7 @@ namespace
{
std::cout << "Author: " << esm.getAuthor() << '\n'
<< "Description: " << esm.getDesc() << '\n'
<< "File format version: " << esm.getFVer() << '\n';
<< "File format version: " << esm.esmVersionF() << '\n';
std::vector<ESM::Header::MasterData> masterData = esm.getGameFiles();
if (!masterData.empty())
{
@ -508,7 +508,7 @@ namespace
ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(info.encoding));
esm.setEncoder(&encoder);
esm.setHeader(data.mHeader);
esm.setVersion(ESM::VER_13);
esm.setVersion(ESM::VER_130);
esm.setRecordCount(recordCount);
std::fstream save(info.outname, std::fstream::out | std::fstream::binary);

View File

@ -1084,14 +1084,8 @@ namespace EsmTool
std::cout << " Rank: " << (int)mData.mNpdt.mRank << std::endl;
std::cout << " Attributes:" << std::endl;
std::cout << " Strength: " << (int)mData.mNpdt.mStrength << std::endl;
std::cout << " Intelligence: " << (int)mData.mNpdt.mIntelligence << std::endl;
std::cout << " Willpower: " << (int)mData.mNpdt.mWillpower << std::endl;
std::cout << " Agility: " << (int)mData.mNpdt.mAgility << std::endl;
std::cout << " Speed: " << (int)mData.mNpdt.mSpeed << std::endl;
std::cout << " Endurance: " << (int)mData.mNpdt.mEndurance << std::endl;
std::cout << " Personality: " << (int)mData.mNpdt.mPersonality << std::endl;
std::cout << " Luck: " << (int)mData.mNpdt.mLuck << std::endl;
for (size_t i = 0; i != mData.mNpdt.mAttributes.size(); i++)
std::cout << " " << attributeLabel(i) << ": " << int(mData.mNpdt.mAttributes[i]) << std::endl;
std::cout << " Skills:" << std::endl;
for (size_t i = 0; i != mData.mNpdt.mSkills.size(); i++)
@ -1169,19 +1163,23 @@ namespace EsmTool
std::cout << " Description: " << mData.mDescription << std::endl;
std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl;
for (int i = 0; i < 2; ++i)
std::cout << " Male:" << std::endl;
for (int j = 0; j < ESM::Attribute::Length; ++j)
{
bool male = i == 0;
std::cout << (male ? " Male:" : " Female:") << std::endl;
for (int j = 0; j < ESM::Attribute::Length; ++j)
std::cout << " " << ESM::Attribute::indexToRefId(j) << ": "
<< mData.mData.mAttributeValues[j].getValue(male) << std::endl;
std::cout << " Height: " << mData.mData.mHeight.getValue(male) << std::endl;
std::cout << " Weight: " << mData.mData.mWeight.getValue(male) << std::endl;
ESM::RefId id = ESM::Attribute::indexToRefId(j);
std::cout << " " << id << ": " << mData.mData.getAttribute(id, true) << std::endl;
}
std::cout << " Height: " << mData.mData.mMaleHeight << std::endl;
std::cout << " Weight: " << mData.mData.mMaleWeight << std::endl;
std::cout << " Female:" << std::endl;
for (int j = 0; j < ESM::Attribute::Length; ++j)
{
ESM::RefId id = ESM::Attribute::indexToRefId(j);
std::cout << " " << id << ": " << mData.mData.getAttribute(id, false) << std::endl;
}
std::cout << " Height: " << mData.mData.mFemaleHeight << std::endl;
std::cout << " Weight: " << mData.mData.mFemaleWeight << std::endl;
for (const auto& bonus : mData.mData.mBonus)
// Not all races have 7 skills.

View File

@ -1,6 +1,7 @@
#include "converter.hpp"
#include <algorithm>
#include <cstdint>
#include <stdexcept>
#include <osgDB/WriteFile>
@ -33,7 +34,7 @@ namespace
objstate.mPosition = cellref.mPos;
objstate.mRef.mRefNum = cellref.mRefNum;
if (cellref.mDeleted)
objstate.mCount = 0;
objstate.mRef.mCount = 0;
convertSCRI(cellref.mActorData.mSCRI, objstate.mLocals);
objstate.mHasLocals = !objstate.mLocals.mVariables.empty();
@ -90,14 +91,14 @@ namespace ESSImport
struct MAPH
{
unsigned int size;
unsigned int value;
uint32_t size;
uint32_t value;
};
void ConvertFMAP::read(ESM::ESMReader& esm)
{
MAPH maph;
esm.getHNTSized<8>(maph, "MAPH");
esm.getHNT("MAPH", maph.size, maph.value);
std::vector<char> data;
esm.getSubNameIs("MAPD");
esm.getSubHeader();
@ -278,7 +279,7 @@ namespace ESSImport
while (esm.isNextSub("MPCD"))
{
float notepos[3];
esm.getHTSized<3 * sizeof(float)>(notepos);
esm.getHT(notepos);
// Markers seem to be arranged in a 32*32 grid, notepos has grid-indices.
// This seems to be the reason markers can't be placed everywhere in interior cells,

View File

@ -16,7 +16,7 @@ namespace ESSImport
objstate.blank();
objstate.mRef = item;
objstate.mRef.mRefID = ESM::RefId::stringRefId(item.mId);
objstate.mCount = item.mCount;
objstate.mRef.mCount = item.mCount;
state.mItems.push_back(objstate);
if (item.mRelativeEquipmentSlot != -1)
// Note we should really write the absolute slot here, which we do not know about

View File

@ -1,6 +1,7 @@
#ifndef OPENMW_ESSIMPORT_ACDT_H
#define OPENMW_ESSIMPORT_ACDT_H
#include <cstdint>
#include <string>
#include "importscri.hpp"
@ -25,14 +26,12 @@ namespace ESSImport
};
/// Actor data, shared by (at least) REFR and CellRef
#pragma pack(push)
#pragma pack(1)
struct ACDT
{
// Note, not stored at *all*:
// - Level changes are lost on reload, except for the player (there it's in the NPC record).
unsigned char mUnknown[12];
unsigned int mFlags;
uint32_t mFlags;
float mBreathMeter; // Seconds left before drowning
unsigned char mUnknown2[20];
float mDynamic[3][2];
@ -41,7 +40,7 @@ namespace ESSImport
float mMagicEffects[27]; // Effect attributes:
// https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes
unsigned char mUnknown4[4];
unsigned int mGoldPool;
uint32_t mGoldPool;
unsigned char mCountDown; // seen the same value as in ACSC.mCorpseClearCountdown, maybe
// this one is for respawning?
unsigned char mUnknown5[3];
@ -60,7 +59,6 @@ namespace ESSImport
unsigned char mUnknown[3];
float mTime;
};
#pragma pack(pop)
struct ActorData
{

View File

@ -1,6 +1,7 @@
#include "importcellref.hpp"
#include <components/esm3/esmreader.hpp>
#include <cstdint>
namespace ESSImport
{
@ -44,19 +45,14 @@ namespace ESSImport
bool isDeleted = false;
ESM::CellRef::loadData(esm, isDeleted);
mActorData.mHasACDT = false;
if (esm.isNextSub("ACDT"))
{
mActorData.mHasACDT = true;
esm.getHTSized<264>(mActorData.mACDT);
}
mActorData.mHasACDT
= esm.getHNOT("ACDT", mActorData.mACDT.mUnknown, mActorData.mACDT.mFlags, mActorData.mACDT.mBreathMeter,
mActorData.mACDT.mUnknown2, mActorData.mACDT.mDynamic, mActorData.mACDT.mUnknown3,
mActorData.mACDT.mAttributes, mActorData.mACDT.mMagicEffects, mActorData.mACDT.mUnknown4,
mActorData.mACDT.mGoldPool, mActorData.mACDT.mCountDown, mActorData.mACDT.mUnknown5);
mActorData.mHasACSC = false;
if (esm.isNextSub("ACSC"))
{
mActorData.mHasACSC = true;
esm.getHTSized<112>(mActorData.mACSC);
}
mActorData.mHasACSC = esm.getHNOT("ACSC", mActorData.mACSC.mUnknown1, mActorData.mACSC.mFlags,
mActorData.mACSC.mUnknown2, mActorData.mACSC.mCorpseClearCountdown, mActorData.mACSC.mUnknown3);
if (esm.isNextSub("ACSL"))
esm.skipHSubSize(112);
@ -122,23 +118,17 @@ namespace ESSImport
}
// FIXME: not all actors have this, add flag
if (esm.isNextSub("CHRD")) // npc only
esm.getHExact(mActorData.mSkills, 27 * 2 * sizeof(int));
esm.getHNOT("CHRD", mActorData.mSkills); // npc only
if (esm.isNextSub("CRED")) // creature only
esm.getHExact(mActorData.mCombatStats, 3 * 2 * sizeof(int));
esm.getHNOT("CRED", mActorData.mCombatStats); // creature only
mActorData.mSCRI.load(esm);
if (esm.isNextSub("ND3D"))
esm.skipHSub();
mActorData.mHasANIS = false;
if (esm.isNextSub("ANIS"))
{
mActorData.mHasANIS = true;
esm.getHTSized<8>(mActorData.mANIS);
}
mActorData.mHasANIS
= esm.getHNOT("ANIS", mActorData.mANIS.mGroupIndex, mActorData.mANIS.mUnknown, mActorData.mANIS.mTime);
if (esm.isNextSub("LVCR"))
{
@ -155,13 +145,13 @@ namespace ESSImport
// DATA should occur for all references, except levelled creature spawners
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird
// alarmvoi0000.ess
esm.getHNOTSized<24>(mPos, "DATA");
esm.getHNOTSized<24>(mPos, "DATA");
for (int i = 0; i < 2; ++i)
esm.getHNOT("DATA", mPos.pos, mPos.rot);
mDeleted = 0;
if (esm.isNextSub("DELE"))
{
unsigned int deleted;
uint32_t deleted;
esm.getHT(deleted);
mDeleted = ((deleted >> 24) & 0x2) != 0; // the other 3 bytes seem to be uninitialized garbage
}

View File

@ -1,6 +1,7 @@
#include "importcntc.hpp"
#include <components/esm3/esmreader.hpp>
#include <cstdint>
namespace ESSImport
{

View File

@ -14,7 +14,7 @@ namespace ESSImport
/// Changed container contents
struct CNTC
{
int mIndex;
int32_t mIndex;
Inventory mInventory;

View File

@ -3,6 +3,7 @@
#include "importinventory.hpp"
#include <components/esm3/aipackage.hpp>
#include <cstdint>
namespace ESM
{
@ -15,7 +16,7 @@ namespace ESSImport
/// Creature changes
struct CREC
{
int mIndex;
int32_t mIndex;
Inventory mInventory;
ESM::AIPackageList mAiPackages;

View File

@ -8,11 +8,11 @@ namespace ESSImport
void DIAL::load(ESM::ESMReader& esm)
{
// See ESM::Dialogue::Type enum, not sure why we would need this here though
int type = 0;
int32_t type = 0;
esm.getHNOT(type, "DATA");
// Deleted dialogue in a savefile. No clue what this means...
int deleted = 0;
int32_t deleted = 0;
esm.getHNOT(deleted, "DELE");
mIndex = 0;

View File

@ -1,5 +1,8 @@
#ifndef OPENMW_ESSIMPORT_IMPORTDIAL_H
#define OPENMW_ESSIMPORT_IMPORTDIAL_H
#include <cstdint>
namespace ESM
{
class ESMReader;
@ -10,7 +13,7 @@ namespace ESSImport
struct DIAL
{
int mIndex; // Journal index
int32_t mIndex; // Journal index
void load(ESM::ESMReader& esm);
};

View File

@ -9,17 +9,17 @@ namespace ESSImport
{
esm.getSubNameIs("GMDT");
esm.getSubHeader();
if (esm.getSubSize() == 92)
{
esm.getExact(&mGMDT, 92);
mGMDT.mSecundaPhase = 0;
}
else if (esm.getSubSize() == 96)
{
esm.getTSized<96>(mGMDT);
}
else
esm.fail("unexpected subrecord size for GAME.GMDT");
bool hasSecundaPhase = esm.getSubSize() == 96;
esm.getT(mGMDT.mCellName);
esm.getT(mGMDT.mFogColour);
esm.getT(mGMDT.mFogDensity);
esm.getT(mGMDT.mCurrentWeather);
esm.getT(mGMDT.mNextWeather);
esm.getT(mGMDT.mWeatherTransition);
esm.getT(mGMDT.mTimeOfNextTransition);
esm.getT(mGMDT.mMasserPhase);
if (hasSecundaPhase)
esm.getT(mGMDT.mSecundaPhase);
mGMDT.mWeatherTransition &= (0x000000ff);
mGMDT.mSecundaPhase &= (0x000000ff);

View File

@ -1,6 +1,8 @@
#ifndef OPENMW_ESSIMPORT_GAME_H
#define OPENMW_ESSIMPORT_GAME_H
#include <cstdint>
namespace ESM
{
class ESMReader;
@ -15,12 +17,12 @@ namespace ESSImport
struct GMDT
{
char mCellName[64]{};
int mFogColour{ 0 };
int32_t mFogColour{ 0 };
float mFogDensity{ 0.f };
int mCurrentWeather{ 0 }, mNextWeather{ 0 };
int mWeatherTransition{ 0 }; // 0-100 transition between weathers, top 3 bytes may be garbage
int32_t mCurrentWeather{ 0 }, mNextWeather{ 0 };
int32_t mWeatherTransition{ 0 }; // 0-100 transition between weathers, top 3 bytes may be garbage
float mTimeOfNextTransition{ 0.f }; // weather changes when gamehour == timeOfNextTransition
int mMasserPhase{ 0 }, mSecundaPhase{ 0 }; // top 3 bytes may be garbage
int32_t mMasserPhase{ 0 }, mSecundaPhase{ 0 }; // top 3 bytes may be garbage
};
GMDT mGMDT;

View File

@ -12,7 +12,7 @@ namespace ESSImport
while (esm.isNextSub("NPCO"))
{
ContItem contItem;
esm.getHTSized<36>(contItem);
esm.getHT(contItem.mCount, contItem.mItem.mData);
InventoryItem item;
item.mId = contItem.mItem.toString();
@ -28,7 +28,7 @@ namespace ESSImport
bool newStack = esm.isNextSub("XIDX");
if (newStack)
{
unsigned int idx;
uint32_t idx;
esm.getHT(idx);
separateStacks = true;
item.mCount = 1;
@ -40,7 +40,7 @@ namespace ESSImport
bool isDeleted = false;
item.ESM::CellRef::loadData(esm, isDeleted);
int charge = -1;
int32_t charge = -1;
esm.getHNOT(charge, "XHLT");
item.mChargeInt = charge;
@ -60,7 +60,7 @@ namespace ESSImport
// this is currently not handled properly.
esm.getSubHeader();
int itemIndex; // index of the item in the NPCO list
int32_t itemIndex; // index of the item in the NPCO list
esm.getT(itemIndex);
if (itemIndex < 0 || itemIndex >= int(mItems.size()))
@ -68,7 +68,7 @@ namespace ESSImport
// appears to be a relative index for only the *possible* slots this item can be equipped in,
// i.e. 0 most of the time
int slotIndex;
int32_t slotIndex;
esm.getT(slotIndex);
mItems[itemIndex].mRelativeEquipmentSlot = slotIndex;

View File

@ -1,6 +1,7 @@
#ifndef OPENMW_ESSIMPORT_IMPORTINVENTORY_H
#define OPENMW_ESSIMPORT_IMPORTINVENTORY_H
#include <cstdint>
#include <string>
#include <vector>
@ -19,7 +20,7 @@ namespace ESSImport
struct ContItem
{
int mCount;
int32_t mCount;
ESM::NAME32 mItem;
};
@ -28,8 +29,8 @@ namespace ESSImport
struct InventoryItem : public ESM::CellRef
{
std::string mId;
int mCount;
int mRelativeEquipmentSlot;
int32_t mCount;
int32_t mRelativeEquipmentSlot;
SCRI mSCRI;
};
std::vector<InventoryItem> mItems;

View File

@ -10,7 +10,7 @@ namespace ESSImport
while (esm.isNextSub("KNAM"))
{
std::string refId = esm.getHString();
int count;
int32_t count;
esm.getHNT(count, "CNAM");
mKillCounter[refId] = count;
}

View File

@ -1,6 +1,7 @@
#ifndef OPENMW_ESSIMPORT_KLST_H
#define OPENMW_ESSIMPORT_KLST_H
#include <cstdint>
#include <map>
#include <string>
@ -18,9 +19,9 @@ namespace ESSImport
void load(ESM::ESMReader& esm);
/// RefId, kill count
std::map<std::string, int> mKillCounter;
std::map<std::string, int32_t> mKillCounter;
int mWerewolfKills;
int32_t mWerewolfKills;
};
}

View File

@ -7,7 +7,7 @@ namespace ESSImport
void NPCC::load(ESM::ESMReader& esm)
{
esm.getHNTSized<8>(mNPDT, "NPDT");
esm.getHNT("NPDT", mNPDT.mDisposition, mNPDT.unknown, mNPDT.mReputation, mNPDT.unknown2, mNPDT.mIndex);
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A"))

View File

@ -2,6 +2,7 @@
#define OPENMW_ESSIMPORT_NPCC_H
#include <components/esm3/aipackage.hpp>
#include <cstdint>
#include "importinventory.hpp"
@ -21,7 +22,7 @@ namespace ESSImport
unsigned char unknown;
unsigned char mReputation;
unsigned char unknown2;
int mIndex;
int32_t mIndex;
} mNPDT;
Inventory mInventory;

View File

@ -19,7 +19,12 @@ namespace ESSImport
mMNAM = esm.getHString();
}
esm.getHNTSized<212>(mPNAM, "PNAM");
esm.getHNT("PNAM", mPNAM.mPlayerFlags, mPNAM.mLevelProgress, mPNAM.mSkillProgress, mPNAM.mSkillIncreases,
mPNAM.mTelekinesisRangeBonus, mPNAM.mVisionBonus, mPNAM.mDetectKeyMagnitude,
mPNAM.mDetectEnchantmentMagnitude, mPNAM.mDetectAnimalMagnitude, mPNAM.mMarkLocation.mX,
mPNAM.mMarkLocation.mY, mPNAM.mMarkLocation.mZ, mPNAM.mMarkLocation.mRotZ, mPNAM.mMarkLocation.mCellX,
mPNAM.mMarkLocation.mCellY, mPNAM.mUnknown3, mPNAM.mVerticalRotation.mData, mPNAM.mSpecIncreases,
mPNAM.mUnknown4);
if (esm.isNextSub("SNAM"))
esm.skipHSub();
@ -50,12 +55,7 @@ namespace ESSImport
if (esm.isNextSub("NAM3"))
esm.skipHSub();
mHasENAM = false;
if (esm.isNextSub("ENAM"))
{
mHasENAM = true;
esm.getHTSized<8>(mENAM);
}
mHasENAM = esm.getHNOT("ENAM", mENAM.mCellX, mENAM.mCellY);
if (esm.isNextSub("LNAM"))
esm.skipHSub();
@ -63,16 +63,12 @@ namespace ESSImport
while (esm.isNextSub("FNAM"))
{
FNAM fnam;
esm.getHTSized<44>(fnam);
esm.getHT(
fnam.mRank, fnam.mUnknown1, fnam.mReputation, fnam.mFlags, fnam.mUnknown2, fnam.mFactionName.mData);
mFactions.push_back(fnam);
}
mHasAADT = false;
if (esm.isNextSub("AADT")) // Attack animation data?
{
mHasAADT = true;
esm.getHTSized<44>(mAADT);
}
mHasAADT = esm.getHNOT("AADT", mAADT.animGroupIndex, mAADT.mUnknown5); // Attack animation data?
if (esm.isNextSub("KNAM"))
esm.skipHSub(); // assigned Quick Keys, I think

View File

@ -1,6 +1,7 @@
#ifndef OPENMW_ESSIMPORT_PLAYER_H
#define OPENMW_ESSIMPORT_PLAYER_H
#include <cstdint>
#include <string>
#include <vector>
@ -17,7 +18,7 @@ namespace ESSImport
/// Other player data
struct PCDT
{
int mBounty;
int32_t mBounty;
std::string mBirthsign;
std::vector<std::string> mKnownDialogueTopics;
@ -41,13 +42,11 @@ namespace ESSImport
PlayerFlags_LevitationDisabled = 0x80000
};
#pragma pack(push)
#pragma pack(1)
struct FNAM
{
unsigned char mRank;
unsigned char mUnknown1[3];
int mReputation;
int32_t mReputation;
unsigned char mFlags; // 0x1: unknown, 0x2: expelled
unsigned char mUnknown2[3];
ESM::NAME32 mFactionName;
@ -59,7 +58,7 @@ namespace ESSImport
{
float mX, mY, mZ; // worldspace position
float mRotZ; // Z angle in radians
int mCellX, mCellY; // grid coordinates; for interior cells this is always (0, 0)
int32_t mCellX, mCellY; // grid coordinates; for interior cells this is always (0, 0)
};
struct Rotation
@ -67,15 +66,15 @@ namespace ESSImport
float mData[3][3];
};
int mPlayerFlags; // controls, camera and draw state
unsigned int mLevelProgress;
int32_t mPlayerFlags; // controls, camera and draw state
uint32_t mLevelProgress;
float mSkillProgress[27]; // skill progress, non-uniform scaled
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
int mTelekinesisRangeBonus; // in units; seems redundant
int32_t mTelekinesisRangeBonus; // in units; seems redundant
float mVisionBonus; // range: <0.0, 1.0>; affected by light spells and Get/Mod/SetPCVisionBonus
int mDetectKeyMagnitude; // seems redundant
int mDetectEnchantmentMagnitude; // seems redundant
int mDetectAnimalMagnitude; // seems redundant
int32_t mDetectKeyMagnitude; // seems redundant
int32_t mDetectEnchantmentMagnitude; // seems redundant
int32_t mDetectAnimalMagnitude; // seems redundant
MarkLocation mMarkLocation;
unsigned char mUnknown3[4];
Rotation mVerticalRotation;
@ -85,16 +84,15 @@ namespace ESSImport
struct ENAM
{
int mCellX;
int mCellY;
int32_t mCellX;
int32_t mCellY;
};
struct AADT // 44 bytes
{
int animGroupIndex; // See convertANIS() for the mapping.
int32_t animGroupIndex; // See convertANIS() for the mapping.
unsigned char mUnknown5[40];
};
#pragma pack(pop)
std::vector<FNAM> mFactions;
PNAM mPNAM;

View File

@ -10,7 +10,9 @@ namespace ESSImport
while (esm.isNextSub("PNAM"))
{
PNAM pnam;
esm.getHTSized<184>(pnam);
esm.getHT(pnam.mAttackStrength, pnam.mSpeed, pnam.mUnknown, pnam.mFlightTime, pnam.mSplmIndex,
pnam.mUnknown2, pnam.mVelocity.mValues, pnam.mPosition.mValues, pnam.mUnknown3, pnam.mActorId.mData,
pnam.mArrowId.mData, pnam.mBowId.mData);
mProjectiles.push_back(pnam);
}
}

View File

@ -3,6 +3,7 @@
#include <components/esm/esmcommon.hpp>
#include <components/esm/util.hpp>
#include <cstdint>
#include <vector>
namespace ESM
@ -16,15 +17,13 @@ namespace ESSImport
struct PROJ
{
#pragma pack(push)
#pragma pack(1)
struct PNAM // 184 bytes
{
float mAttackStrength;
float mSpeed;
unsigned char mUnknown[4 * 2];
float mFlightTime;
int mSplmIndex; // reference to a SPLM record (0 for ballistic projectiles)
int32_t mSplmIndex; // reference to a SPLM record (0 for ballistic projectiles)
unsigned char mUnknown2[4];
ESM::Vector3 mVelocity;
ESM::Vector3 mPosition;
@ -35,7 +34,6 @@ namespace ESSImport
bool isMagic() const { return mSplmIndex != 0; }
};
#pragma pack(pop)
std::vector<PNAM> mProjectiles;

View File

@ -7,7 +7,8 @@ namespace ESSImport
void SCPT::load(ESM::ESMReader& esm)
{
esm.getHNTSized<52>(mSCHD, "SCHD");
esm.getHNT("SCHD", mSCHD.mName.mData, mSCHD.mData.mNumShorts, mSCHD.mData.mNumLongs, mSCHD.mData.mNumFloats,
mSCHD.mData.mScriptDataSize, mSCHD.mData.mStringTableSize);
mSCRI.load(esm);

View File

@ -3,6 +3,8 @@
#include "importscri.hpp"
#include <cstdint>
#include <components/esm/esmcommon.hpp>
#include <components/esm3/loadscpt.hpp>
@ -29,7 +31,7 @@ namespace ESSImport
SCRI mSCRI;
bool mRunning;
int mRefNum; // Targeted reference, -1: no reference
int32_t mRefNum; // Targeted reference, -1: no reference
void load(ESM::ESMReader& esm);
};

View File

@ -9,7 +9,7 @@ namespace ESSImport
{
mScript = esm.getHNOString("SCRI");
int numShorts = 0, numLongs = 0, numFloats = 0;
int32_t numShorts = 0, numLongs = 0, numFloats = 0;
if (esm.isNextSub("SLCS"))
{
esm.getSubHeader();
@ -23,7 +23,7 @@ namespace ESSImport
esm.getSubHeader();
for (int i = 0; i < numShorts; ++i)
{
short val;
int16_t val;
esm.getT(val);
mShorts.push_back(val);
}
@ -35,7 +35,7 @@ namespace ESSImport
esm.getSubHeader();
for (int i = 0; i < numLongs; ++i)
{
int val;
int32_t val;
esm.getT(val);
mLongs.push_back(val);
}

View File

@ -3,6 +3,7 @@
#include <components/esm3/variant.hpp>
#include <cstdint>
#include <vector>
namespace ESM

View File

@ -11,13 +11,15 @@ namespace ESSImport
{
ActiveSpell spell;
esm.getHT(spell.mIndex);
esm.getHNTSized<160>(spell.mSPDT, "SPDT");
esm.getHNT("SPDT", spell.mSPDT.mType, spell.mSPDT.mId.mData, spell.mSPDT.mUnknown,
spell.mSPDT.mCasterId.mData, spell.mSPDT.mSourceId.mData, spell.mSPDT.mUnknown2);
spell.mTarget = esm.getHNOString("TNAM");
while (esm.isNextSub("NPDT"))
{
ActiveEffect effect;
esm.getHTSized<56>(effect.mNPDT);
esm.getHT(effect.mNPDT.mAffectedActorId.mData, effect.mNPDT.mUnknown, effect.mNPDT.mMagnitude,
effect.mNPDT.mSecondsActive, effect.mNPDT.mUnknown2);
// Effect-specific subrecords can follow:
// - INAM for disintegration and bound effects

View File

@ -2,6 +2,7 @@
#define OPENMW_ESSIMPORT_IMPORTSPLM_H
#include <components/esm/esmcommon.hpp>
#include <cstdint>
#include <vector>
namespace ESM
@ -15,11 +16,9 @@ namespace ESSImport
struct SPLM
{
#pragma pack(push)
#pragma pack(1)
struct SPDT // 160 bytes
{
int mType; // 1 = spell, 2 = enchantment, 3 = potion
int32_t mType; // 1 = spell, 2 = enchantment, 3 = potion
ESM::NAME32 mId; // base ID of a spell/enchantment/potion
unsigned char mUnknown[4 * 4];
ESM::NAME32 mCasterId;
@ -31,31 +30,29 @@ namespace ESSImport
{
ESM::NAME32 mAffectedActorId;
unsigned char mUnknown[4 * 2];
int mMagnitude;
int32_t mMagnitude;
float mSecondsActive;
unsigned char mUnknown2[4 * 2];
};
struct INAM // 40 bytes
{
int mUnknown;
int32_t mUnknown;
unsigned char mUnknown2;
ESM::FixedString<35> mItemId; // disintegrated item / bound item / item to re-equip after expiration
};
struct CNAM // 36 bytes
{
int mUnknown; // seems to always be 0
int32_t mUnknown; // seems to always be 0
ESM::NAME32 mSummonedOrCommandedActor[32];
};
struct VNAM // 4 bytes
{
int mUnknown;
int32_t mUnknown;
};
#pragma pack(pop)
struct ActiveEffect
{
NPDT mNPDT;
@ -63,7 +60,7 @@ namespace ESSImport
struct ActiveSpell
{
int mIndex;
int32_t mIndex;
SPDT mSPDT;
std::string mTarget;
std::vector<ActiveEffect> mActiveEffects;

View File

@ -42,8 +42,8 @@ Allowed options)");
Files::ConfigurationManager cfgManager(true);
cfgManager.readConfiguration(variables, desc);
const auto essFile = variables["mwsave"].as<Files::MaybeQuotedPath>();
const auto outputFile = variables["output"].as<Files::MaybeQuotedPath>();
const auto& essFile = variables["mwsave"].as<Files::MaybeQuotedPath>();
const auto& outputFile = variables["output"].as<Files::MaybeQuotedPath>();
std::string encoding = variables["encoding"].as<std::string>();
ESSImport::Importer importer(essFile, outputFile, encoding);

View File

@ -35,13 +35,12 @@ set(LAUNCHER_HEADER
# Headers that must be pre-processed
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/contentselector.ui
${CMAKE_SOURCE_DIR}/files/ui/importpage.ui
${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui
${CMAKE_SOURCE_DIR}/files/ui/directorypicker.ui
${CMAKE_CURRENT_SOURCE_DIR}/ui/datafilespage.ui
${CMAKE_CURRENT_SOURCE_DIR}/ui/graphicspage.ui
${CMAKE_CURRENT_SOURCE_DIR}/ui/mainwindow.ui
${CMAKE_CURRENT_SOURCE_DIR}/ui/importpage.ui
${CMAKE_CURRENT_SOURCE_DIR}/ui/settingspage.ui
${CMAKE_CURRENT_SOURCE_DIR}/ui/directorypicker.ui
)
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})

View File

@ -125,27 +125,6 @@ namespace Launcher
{
return Settings::navigator().mMaxNavmeshdbFileSize / (1024 * 1024);
}
std::optional<QString> findFirstPath(const QStringList& directories, const QString& fileName)
{
for (const QString& directoryPath : directories)
{
const QString filePath = QDir(directoryPath).absoluteFilePath(fileName);
if (QFile::exists(filePath))
return filePath;
}
return std::nullopt;
}
QStringList findAllFilePaths(const QStringList& directories, const QStringList& fileNames)
{
QStringList result;
result.reserve(fileNames.size());
for (const QString& fileName : fileNames)
if (const auto filepath = findFirstPath(directories, fileName))
result.append(*filepath);
return result;
}
}
}
@ -164,11 +143,14 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C
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");
QVector<std::pair<QString, QString>> languages = { { "English", tr("English") }, { "French", tr("French") },
{ "German", tr("German") }, { "Italian", tr("Italian") }, { "Polish", tr("Polish") },
{ "Russian", tr("Russian") }, { "Spanish", tr("Spanish") } };
mSelector->languageBox()->addItems(languages);
for (auto lang : languages)
{
mSelector->languageBox()->addItem(lang.second, lang.first);
}
mNewProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this);
mCloneProfileDialog = new TextInputDialog(tr("Clone Content List"), tr("Content List name:"), this);
@ -254,9 +236,17 @@ bool Launcher::DataFilesPage::loadSettings()
if (!currentProfile.isEmpty())
addProfile(currentProfile, true);
const int index = mSelector->languageBox()->findText(mLauncherSettings.getLanguage());
if (index != -1)
mSelector->languageBox()->setCurrentIndex(index);
auto language = mLauncherSettings.getLanguage();
for (int i = 0; i < mSelector->languageBox()->count(); ++i)
{
QString languageItem = mSelector->languageBox()->itemData(i).toString();
if (language == languageItem)
{
mSelector->languageBox()->setCurrentIndex(i);
break;
}
}
return true;
}
@ -301,12 +291,14 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
auto row = ui.directoryListWidget->count() - 1;
auto* item = ui.directoryListWidget->item(row);
// Display new content with green background
// Display new content with custom formatting
if (mNewDataDirs.contains(canonicalDirPath))
{
tooltip += "Will be added to the current profile\n";
item->setBackground(Qt::green);
item->setForeground(Qt::black);
QFont font = item->font();
font.setBold(true);
font.setItalic(true);
item->setFont(font);
}
// deactivate data-local and global data directory: they are always included
@ -353,8 +345,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
row++;
}
mSelector->setProfileContent(
findAllFilePaths(directories, mLauncherSettings.getContentListFiles(contentModelName)));
mSelector->setProfileContent(mLauncherSettings.getContentListFiles(contentModelName));
}
void Launcher::DataFilesPage::saveSettings(const QString& profile)
@ -384,7 +375,7 @@ void Launcher::DataFilesPage::saveSettings(const QString& profile)
mLauncherSettings.setContentList(profileName, dirList, selectedArchivePaths(), fileNames);
mGameSettings.setContentList(dirList, selectedArchivePaths(), fileNames);
QString language(mSelector->languageBox()->currentText());
QString language(mSelector->languageBox()->currentData().toString());
mLauncherSettings.setLanguage(language);
@ -737,8 +728,11 @@ void Launcher::DataFilesPage::addArchive(const QString& name, Qt::CheckState sel
ui.archiveListWidget->item(row)->setCheckState(selected);
if (mKnownArchives.filter(name).isEmpty()) // XXX why contains doesn't work here ???
{
ui.archiveListWidget->item(row)->setBackground(Qt::green);
ui.archiveListWidget->item(row)->setForeground(Qt::black);
auto item = ui.archiveListWidget->item(row);
QFont font = item->font();
font.setBold(true);
font.setItalic(true);
item->setFont(font);
}
}

View File

@ -154,8 +154,13 @@ bool Launcher::GraphicsPage::loadSettings()
if (Settings::shadows().mEnableIndoorShadows)
indoorShadowsCheckBox->setCheckState(Qt::Checked);
shadowComputeSceneBoundsComboBox->setCurrentIndex(
shadowComputeSceneBoundsComboBox->findText(QString(tr(Settings::shadows().mComputeSceneBounds.get().c_str()))));
const auto& boundMethod = Settings::shadows().mComputeSceneBounds.get();
if (boundMethod == "bounds")
shadowComputeSceneBoundsComboBox->setCurrentIndex(0);
else if (boundMethod == "primitives")
shadowComputeSceneBoundsComboBox->setCurrentIndex(1);
else
shadowComputeSceneBoundsComboBox->setCurrentIndex(2);
const int shadowDistLimit = Settings::shadows().mMaximumShadowMapDistance;
if (shadowDistLimit > 0)
@ -254,7 +259,14 @@ void Launcher::GraphicsPage::saveSettings()
Settings::shadows().mEnableIndoorShadows.set(indoorShadowsCheckBox->checkState() != Qt::Unchecked);
Settings::shadows().mShadowMapResolution.set(shadowResolutionComboBox->currentText().toInt());
Settings::shadows().mComputeSceneBounds.set(shadowComputeSceneBoundsComboBox->currentText().toStdString());
auto index = shadowComputeSceneBoundsComboBox->currentIndex();
if (index == 0)
Settings::shadows().mComputeSceneBounds.set("bounds");
else if (index == 1)
Settings::shadows().mComputeSceneBounds.set("primitives");
else
Settings::shadows().mComputeSceneBounds.set("none");
}
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)

View File

@ -104,9 +104,9 @@ void Launcher::ImportPage::on_importerButton_clicked()
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>")
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;

View File

@ -41,11 +41,6 @@ int runLauncher(int argc, char* argv[])
appTranslator.load(":/translations/" + locale + ".qm");
app.installTranslator(&appTranslator);
// Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath());
QDir::setCurrent(dir.absolutePath());
Launcher::MainDialog mainWin(configurationManager);
Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog();

View File

@ -118,13 +118,14 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
const auto& userConfigDir = mCfgMgr.getUserConfigPath();
if (!exists(userConfigDir))
{
if (!create_directories(userConfigDir))
std::error_code ec;
if (!create_directories(userConfigDir, ec))
{
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not create directory %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>")
.arg(Files::pathToQString(canonical(userConfigDir))));
cfgError(tr("Error creating OpenMW configuration directory: code %0").arg(ec.value()),
tr("<br><b>Could not create directory %0</b><br><br>"
"%1<br>")
.arg(Files::pathToQString(userConfigDir))
.arg(QString(ec.message().c_str())));
return FirstRunDialogResultFailure;
}
}
@ -136,10 +137,10 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
msgBox.setIcon(QMessageBox::Question);
msgBox.setStandardButtons(QMessageBox::NoButton);
msgBox.setText(
tr("<html><head/><body><p><b>Welcome to OpenMW!</b></p> \
<p>It is recommended to run the Installation Wizard.</p> \
<p>The Wizard will let you select an existing Morrowind installation, \
or install Morrowind for OpenMW to use.</p></body></html>"));
tr("<html><head/><body><p><b>Welcome to OpenMW!</b></p>"
"<p>It is recommended to run the Installation Wizard.</p>"
"<p>The Wizard will let you select an existing Morrowind installation, "
"or install Morrowind for OpenMW to use.</p></body></html>"));
QAbstractButton* wizardButton
= msgBox.addButton(tr("Run &Installation Wizard"), QMessageBox::AcceptRole); // ActionRole doesn't work?!
@ -297,9 +298,9 @@ bool Launcher::MainDialog::setupLauncherSettings()
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading:</b><br><br>%1<br><br> \
Please make sure you have the right permissions \
and try again.<br>")
tr("<br><b>Could not open %0 for reading:</b><br><br>%1<br><br>"
"Please make sure you have the right permissions "
"and try again.<br>")
.arg(file.fileName())
.arg(file.errorString()));
return false;
@ -327,9 +328,9 @@ bool Launcher::MainDialog::setupGameSettings()
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>")
tr("<br><b>Could not open %0 for reading</b><br><br>"
"Please make sure you have the right permissions "
"and try again.<br>")
.arg(file.fileName()));
return {};
}
@ -388,8 +389,8 @@ bool Launcher::MainDialog::setupGameData()
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::NoButton);
msgBox.setText(
tr("<br><b>Could not find the Data Files location</b><br><br> \
The directory containing the data files was not found."));
tr("<br><b>Could not find the Data Files location</b><br><br>"
"The directory containing the data files was not found."));
QAbstractButton* wizardButton = msgBox.addButton(tr("Run &Installation Wizard..."), QMessageBox::ActionRole);
QAbstractButton* skipButton = msgBox.addButton(tr("Skip"), QMessageBox::RejectRole);
@ -419,8 +420,8 @@ bool Launcher::MainDialog::setupGraphicsSettings()
catch (std::exception& e)
{
cfgError(tr("Error reading OpenMW configuration files"),
tr("<br>The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem.<br>")
tr("<br>The problem may be due to an incomplete installation of OpenMW.<br>"
"Reinstalling OpenMW may resolve the problem.<br>")
+ e.what());
return false;
}
@ -457,13 +458,14 @@ bool Launcher::MainDialog::writeSettings()
if (!exists(userPath))
{
if (!create_directories(userPath))
std::error_code ec;
if (!create_directories(userPath, ec))
{
cfgError(tr("Error creating OpenMW configuration directory"),
tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>")
.arg(Files::pathToQString(userPath)));
cfgError(tr("Error creating OpenMW configuration directory: code %0").arg(ec.value()),
tr("<br><b>Could not create directory %0</b><br><br>"
"%1<br>")
.arg(Files::pathToQString(userPath))
.arg(QString(ec.message().c_str())));
return false;
}
}
@ -479,9 +481,9 @@ bool Launcher::MainDialog::writeSettings()
{
// File cannot be opened or created
cfgError(tr("Error writing OpenMW configuration file"),
tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>")
tr("<br><b>Could not open or create %0 for writing</b><br><br>"
"Please make sure you have the right permissions "
"and try again.<br>")
.arg(file.fileName()));
return false;
}
@ -510,9 +512,9 @@ bool Launcher::MainDialog::writeSettings()
{
// File cannot be opened or created
cfgError(tr("Error writing Launcher configuration file"),
tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>")
tr("<br><b>Could not open or create %0 for writing</b><br><br>"
"Please make sure you have the right permissions "
"and try again.<br>")
.arg(file.fileName()));
return false;
}
@ -562,8 +564,8 @@ void Launcher::MainDialog::play()
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(
tr("<br><b>You do not have a game file selected.</b><br><br> \
OpenMW will not start without a game file selected.<br>"));
tr("<br><b>You do not have a game file selected.</b><br><br>"
"OpenMW will not start without a game file selected.<br>"));
msgBox.exec();
return;
}

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>571</width>
<width>573</width>
<height>384</height>
</rect>
</property>
@ -30,7 +30,7 @@
<item row="1" column="0">
<widget class="QLabel" name="dataNoteLabel">
<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: content files 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>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;note: content files that are not part of current Content List are &lt;span style=&quot; font-style:italic;font-weight: bold&quot;&gt;highlighted&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@ -57,7 +57,7 @@
</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>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;note: directories that are not part of current Content List are &lt;span style=&quot; font-style:italic;font-weight: bold&quot;&gt;highlighted&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@ -210,7 +210,7 @@
<item row="27" column="0" colspan="2">
<widget class="QLabel" name="archiveNoteLabel">
<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>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;note: archives that are not part of current Content List are &lt;span style=&quot; font-style:italic;font-weight: bold&quot;&gt;highlighted&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

View File

@ -107,7 +107,7 @@
<item>
<widget class="QLabel" name="multiplyLabel">
<property name="text">
<string> x </string>
<string> × </string>
</property>
</widget>
</item>

View File

@ -1,7 +1,7 @@
#include <cstring>
#include <vector>
#include <apps/openmw/mwsound/alext.h>
#include "apps/openmw/mwsound/alext.h"
#include "openalutil.hpp"

View File

@ -164,12 +164,12 @@ namespace NavMeshTool
config.filterOutNonExistingPaths(dataDirs);
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
const auto& resDir = variables["resources"].as<Files::MaybeQuotedPath>();
Log(Debug::Info) << Version::getOpenmwVersionDescription();
dataDirs.insert(dataDirs.begin(), resDir / "vfs");
const auto fileCollections = Files::Collections(dataDirs);
const auto archives = variables["fallback-archive"].as<StringsVector>();
const auto contentFiles = variables["content"].as<StringsVector>();
const Files::Collections fileCollections(dataDirs);
const auto& archives = variables["fallback-archive"].as<StringsVector>();
const auto& contentFiles = variables["content"].as<StringsVector>();
const std::size_t threadsNumber = variables["threads"].as<std::size_t>();
if (threadsNumber < 1)

View File

@ -131,7 +131,7 @@ namespace NavMeshTool
osg::ref_ptr<const Resource::BulletShape> shape = [&] {
try
{
return bulletShapeManager.getShape(Misc::ResourceHelpers::correctMeshPath(model, &vfs));
return bulletShapeManager.getShape(Misc::ResourceHelpers::correctMeshPath(model));
}
catch (const std::exception& e)
{

View File

@ -45,9 +45,6 @@ std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
{
switch (Bsa::BSAFile::detectVersion(path))
{
case Bsa::BSAVER_UNKNOWN:
std::cerr << '"' << path << "\" is unknown BSA archive" << std::endl;
return nullptr;
case Bsa::BSAVER_COMPRESSED:
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_COMPRESSED>::type>(path);
case Bsa::BSAVER_BA2_GNRL:
@ -56,11 +53,11 @@ std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_BA2_DX10>::type>(path);
case Bsa::BSAVER_UNCOMPRESSED:
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_UNCOMPRESSED>::type>(path);
case Bsa::BSAVER_UNKNOWN:
default:
std::cerr << "'" << Files::pathToUnicodeString(path) << "' is not a recognized BSA archive" << std::endl;
return nullptr;
}
std::cerr << '"' << path << "\" is unsupported BSA archive" << std::endl;
return nullptr;
}
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
@ -72,58 +69,86 @@ std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
return nullptr;
}
void readNIF(
const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet)
{
const std::string pathStr = Files::pathToUnicodeString(path);
if (!quiet)
{
std::cout << "Reading NIF file '" << pathStr << "'";
if (!source.empty())
std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'";
std::cout << std::endl;
}
std::filesystem::path fullPath = !source.empty() ? source / path : path;
try
{
Nif::NIFFile file(fullPath);
Nif::Reader reader(file);
if (vfs != nullptr)
reader.parse(vfs->get(pathStr));
else
reader.parse(Files::openConstrainedFileStream(fullPath));
}
catch (std::exception& e)
{
std::cerr << "Failed to read '" << pathStr << "':" << std::endl << e.what() << std::endl;
}
}
/// Check all the nif files in a given VFS::Archive
/// \note Can not read a bsa file inside of a bsa file.
void readVFS(std::unique_ptr<VFS::Archive>&& anArchive, const std::filesystem::path& archivePath = {})
void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::path& archivePath, bool quiet)
{
if (anArchive == nullptr)
if (archive == nullptr)
return;
VFS::Manager myManager;
myManager.addArchive(std::move(anArchive));
myManager.buildIndex();
if (!quiet)
std::cout << "Reading data source '" << Files::pathToUnicodeString(archivePath) << "'" << std::endl;
for (const auto& name : myManager.getRecursiveDirectoryIterator(""))
VFS::Manager vfs;
vfs.addArchive(std::move(archive));
vfs.buildIndex();
for (const auto& name : vfs.getRecursiveDirectoryIterator(""))
{
try
if (isNIF(name))
{
if (isNIF(name))
{
// std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile file(archivePath / name);
Nif::Reader reader(file);
reader.parse(myManager.get(name));
}
else if (isBSA(name))
{
if (!archivePath.empty() && !isBSA(archivePath))
{
// std::cout << "Reading BSA File: " << name << std::endl;
readVFS(makeBsaArchive(archivePath / name), archivePath / name);
// std::cout << "Done with BSA File: " << name << std::endl;
}
}
readNIF(archivePath, name, &vfs, quiet);
}
catch (std::exception& e)
}
if (!archivePath.empty() && !isBSA(archivePath))
{
Files::PathContainer dataDirs = { archivePath };
const Files::Collections fileCollections = Files::Collections(dataDirs);
const Files::MultiDirCollection& bsaCol = fileCollections.getCollection(".bsa");
const Files::MultiDirCollection& ba2Col = fileCollections.getCollection(".ba2");
for (auto& file : bsaCol)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
readVFS(makeBsaArchive(file.second), file.second, quiet);
}
for (auto& file : ba2Col)
{
readVFS(makeBsaArchive(file.second), file.second, quiet);
}
}
}
bool parseOptions(int argc, char** argv, std::vector<Files::MaybeQuotedPath>& files, bool& writeDebugLog,
std::vector<Files::MaybeQuotedPath>& archives)
bool parseOptions(int argc, char** argv, Files::PathContainer& files, Files::PathContainer& archives,
bool& writeDebugLog, bool& quiet)
{
bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF and BSA files
Usages:
niftool <nif files, BSA files, or directories>
Scan the file or directories for nif errors.
niftest <nif files, BSA files, or directories>
Scan the file or directories for NIF errors.
Allowed options)");
auto addOption = desc.add_options();
addOption("help,h", "print help message.");
addOption("write-debug-log,v", "write debug log for unsupported nif files");
addOption("quiet,q", "do not log read archives/files");
addOption("archives", bpo::value<Files::MaybeQuotedPathContainer>(), "path to archive files to provide files");
addOption("input-file", bpo::value<Files::MaybeQuotedPathContainer>(), "input file");
@ -143,17 +168,18 @@ Allowed options)");
return false;
}
writeDebugLog = variables.count("write-debug-log") > 0;
quiet = variables.count("quiet") > 0;
if (variables.count("input-file"))
{
files = variables["input-file"].as<Files::MaybeQuotedPathContainer>();
files = asPathContainer(variables["input-file"].as<Files::MaybeQuotedPathContainer>());
if (const auto it = variables.find("archives"); it != variables.end())
archives = it->second.as<Files::MaybeQuotedPathContainer>();
archives = asPathContainer(it->second.as<Files::MaybeQuotedPathContainer>());
return true;
}
}
catch (std::exception& e)
{
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n" << desc << std::endl;
std::cout << "Error parsing arguments: " << e.what() << "\n\n" << desc << std::endl;
return false;
}
@ -164,64 +190,62 @@ Allowed options)");
int main(int argc, char** argv)
{
std::vector<Files::MaybeQuotedPath> files;
Files::PathContainer files, sources;
bool writeDebugLog = false;
std::vector<Files::MaybeQuotedPath> archives;
if (!parseOptions(argc, argv, files, writeDebugLog, archives))
bool quiet = false;
if (!parseOptions(argc, argv, files, sources, writeDebugLog, quiet))
return 1;
Nif::Reader::setLoadUnsupportedFiles(true);
Nif::Reader::setWriteNifDebugLog(writeDebugLog);
std::unique_ptr<VFS::Manager> vfs;
if (!archives.empty())
if (!sources.empty())
{
vfs = std::make_unique<VFS::Manager>();
for (const std::filesystem::path& path : archives)
for (const std::filesystem::path& path : sources)
{
const std::string pathStr = Files::pathToUnicodeString(path);
if (!quiet)
std::cout << "Adding data source '" << pathStr << "'" << std::endl;
try
{
if (auto archive = makeArchive(path))
vfs->addArchive(std::move(archive));
else
std::cerr << '"' << path << "\" is unsupported archive" << std::endl;
vfs->buildIndex();
std::cerr << "Error: '" << pathStr << "' is not an archive or directory" << std::endl;
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
std::cerr << "Failed to add data source '" << pathStr << "': " << e.what() << std::endl;
}
}
vfs->buildIndex();
}
// std::cout << "Reading Files" << std::endl;
for (const auto& path : files)
{
const std::string pathStr = Files::pathToUnicodeString(path);
try
{
if (isNIF(path))
{
// std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile file(path);
Nif::Reader reader(file);
if (vfs != nullptr)
reader.parse(vfs->get(Files::pathToUnicodeString(path)));
else
reader.parse(Files::openConstrainedFileStream(path));
readNIF({}, path, vfs.get(), quiet);
}
else if (auto archive = makeArchive(path))
{
readVFS(std::move(archive), path);
readVFS(std::move(archive), path, quiet);
}
else
{
std::cerr << "ERROR: \"" << Files::pathToUnicodeString(path)
<< "\" is not a nif file, bsa/ba2 file, or directory!" << std::endl;
std::cerr << "Error: '" << pathStr << "' is not a NIF file, BSA/BA2 archive, or directory" << std::endl;
}
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
std::cerr << "Failed to read '" << pathStr << "': " << e.what() << std::endl;
}
}
return 0;

View File

@ -1,5 +1,4 @@
set (OPENCS_SRC
${CMAKE_SOURCE_DIR}/files/windows/opencs.rc
)
opencs_units (. editor)
@ -116,7 +115,7 @@ opencs_units (view/prefs
opencs_units (model/prefs
state setting intsetting doublesetting boolsetting enumsetting coloursetting shortcut
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting stringsetting
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting stringsetting subcategory
)
opencs_units (model/prefs
@ -139,14 +138,16 @@ set (OPENCS_RES ${CMAKE_SOURCE_DIR}/files/opencs/resources.qrc
)
set (OPENCS_UI
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui
${CMAKE_CURRENT_SOURCE_DIR}/ui/filedialog.ui
)
source_group (openmw-cs FILES main.cpp ${OPENCS_SRC} ${OPENCS_HDR})
if(WIN32)
set(QT_USE_QTMAIN TRUE)
set(OPENCS_RC_FILE ${CMAKE_SOURCE_DIR}/files/windows/opencs.rc)
else(WIN32)
set(OPENCS_RC_FILE "")
endif(WIN32)
if (QT_VERSION_MAJOR VERSION_EQUAL 5)
@ -187,6 +188,7 @@ if(BUILD_OPENCS)
${OPENCS_CFG}
${OPENCS_DEFAULT_FILTERS_FILE}
${OPENCS_OPENMW_CFG}
${OPENCS_RC_FILE}
main.cpp
)

View File

@ -200,6 +200,8 @@ std::pair<Files::PathContainer, std::vector<std::string>> CS::Editor::readConfig
dataDirs.insert(dataDirs.end(), dataLocal.begin(), dataLocal.end());
dataDirs.insert(dataDirs.begin(), mResources / "vfs");
// iterate the data directories and add them to the file dialog for loading
mFileDialog.addFiles(dataDirs);

View File

@ -81,11 +81,6 @@ int runApplication(int argc, char* argv[])
Application application(argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
QDir::setCurrent(dir.absolutePath());
#endif
application.setWindowIcon(QIcon(":./openmw-cs.png"));
CS::Editor editor(argc, argv);

View File

@ -25,6 +25,7 @@
#include <components/esm3/loadsoun.hpp>
#include <components/esm3/loadspel.hpp>
#include <components/esm3/loadsscr.hpp>
#include <components/esm3/selectiongroup.hpp>
#include "../world/data.hpp"
#include "../world/idcollection.hpp"
@ -52,6 +53,9 @@ CSMDoc::Saving::Saving(Document& document, const std::filesystem::path& projectP
appendStage(new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script>>(
mDocument.getData().getScripts(), mState, CSMWorld::Scope_Project));
appendStage(new WriteCollectionStage<CSMWorld::IdCollection<ESM::SelectionGroup>>(
mDocument.getData().getSelectionGroups(), mState, CSMWorld::Scope_Project));
appendStage(new CloseSaveStage(mState));
// save content file

View File

@ -11,9 +11,8 @@
#include "state.hpp"
CSMPrefs::BoolSetting::BoolSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, bool default_)
: Setting(parent, mutex, key, label)
, mDefault(default_)
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mWidget(nullptr)
{
}
@ -24,10 +23,10 @@ CSMPrefs::BoolSetting& CSMPrefs::BoolSetting::setTooltip(const std::string& tool
return *this;
}
std::pair<QWidget*, QWidget*> CSMPrefs::BoolSetting::makeWidgets(QWidget* parent)
CSMPrefs::SettingWidgets CSMPrefs::BoolSetting::makeWidgets(QWidget* parent)
{
mWidget = new QCheckBox(QString::fromUtf8(getLabel().c_str()), parent);
mWidget->setCheckState(mDefault ? Qt::Checked : Qt::Unchecked);
mWidget = new QCheckBox(getLabel(), parent);
mWidget->setCheckState(getValue() ? Qt::Checked : Qt::Unchecked);
if (!mTooltip.empty())
{
@ -37,24 +36,19 @@ std::pair<QWidget*, QWidget*> CSMPrefs::BoolSetting::makeWidgets(QWidget* parent
connect(mWidget, &QCheckBox::stateChanged, this, &BoolSetting::valueChanged);
return std::make_pair(static_cast<QWidget*>(nullptr), mWidget);
return SettingWidgets{ .mLabel = nullptr, .mInput = mWidget };
}
void CSMPrefs::BoolSetting::updateWidget()
{
if (mWidget)
{
mWidget->setCheckState(
Settings::Manager::getBool(getKey(), getParent()->getKey()) ? Qt::Checked : Qt::Unchecked);
mWidget->setCheckState(getValue() ? Qt::Checked : Qt::Unchecked);
}
}
void CSMPrefs::BoolSetting::valueChanged(int value)
{
{
QMutexLocker lock(getMutex());
Settings::Manager::setBool(getKey(), getParent()->getKey(), value);
}
setValue(value != Qt::Unchecked);
getParent()->getState()->update(*this);
}

View File

@ -12,21 +12,21 @@ namespace CSMPrefs
{
class Category;
class BoolSetting : public Setting
class BoolSetting final : public TypedSetting<bool>
{
Q_OBJECT
std::string mTooltip;
bool mDefault;
QCheckBox* mWidget;
public:
BoolSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label, bool default_);
explicit BoolSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
BoolSetting& setTooltip(const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -5,6 +5,7 @@
#include "setting.hpp"
#include "state.hpp"
#include "subcategory.hpp"
CSMPrefs::Category::Category(State* parent, const std::string& key)
: mParent(parent)
@ -23,6 +24,14 @@ CSMPrefs::State* CSMPrefs::Category::getState() const
}
void CSMPrefs::Category::addSetting(Setting* setting)
{
if (!mIndex.emplace(setting->getKey(), setting).second)
throw std::logic_error("Category " + mKey + " already has setting: " + setting->getKey());
mSettings.push_back(setting);
}
void CSMPrefs::Category::addSubcategory(Subcategory* setting)
{
mSettings.push_back(setting);
}
@ -39,11 +48,12 @@ CSMPrefs::Category::Iterator CSMPrefs::Category::end()
CSMPrefs::Setting& CSMPrefs::Category::operator[](const std::string& key)
{
for (Iterator iter = mSettings.begin(); iter != mSettings.end(); ++iter)
if ((*iter)->getKey() == key)
return **iter;
const auto it = mIndex.find(key);
throw std::logic_error("Invalid user setting: " + key);
if (it != mIndex.end())
return *it->second;
throw std::logic_error("Invalid user setting in " + mKey + " category: " + key);
}
void CSMPrefs::Category::update()

View File

@ -3,12 +3,14 @@
#include <algorithm>
#include <string>
#include <unordered_map>
#include <vector>
namespace CSMPrefs
{
class State;
class Setting;
class Subcategory;
class Category
{
@ -20,6 +22,7 @@ namespace CSMPrefs
State* mParent;
std::string mKey;
Container mSettings;
std::unordered_map<std::string, Setting*> mIndex;
public:
Category(State* parent, const std::string& key);
@ -30,6 +33,8 @@ namespace CSMPrefs
void addSetting(Setting* setting);
void addSubcategory(Subcategory* setting);
Iterator begin();
Iterator end();

View File

@ -14,9 +14,8 @@
#include "state.hpp"
CSMPrefs::ColourSetting::ColourSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, QColor default_)
: Setting(parent, mutex, key, label)
, mDefault(std::move(default_))
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mWidget(nullptr)
{
}
@ -27,11 +26,11 @@ CSMPrefs::ColourSetting& CSMPrefs::ColourSetting::setTooltip(const std::string&
return *this;
}
std::pair<QWidget*, QWidget*> CSMPrefs::ColourSetting::makeWidgets(QWidget* parent)
CSMPrefs::SettingWidgets CSMPrefs::ColourSetting::makeWidgets(QWidget* parent)
{
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QLabel* label = new QLabel(getLabel(), parent);
mWidget = new CSVWidget::ColorEditor(mDefault, parent);
mWidget = new CSVWidget::ColorEditor(toColor(), parent);
if (!mTooltip.empty())
{
@ -42,24 +41,18 @@ std::pair<QWidget*, QWidget*> CSMPrefs::ColourSetting::makeWidgets(QWidget* pare
connect(mWidget, &CSVWidget::ColorEditor::pickingFinished, this, &ColourSetting::valueChanged);
return std::make_pair(label, mWidget);
return SettingWidgets{ .mLabel = label, .mInput = mWidget };
}
void CSMPrefs::ColourSetting::updateWidget()
{
if (mWidget)
{
mWidget->setColor(QString::fromStdString(Settings::Manager::getString(getKey(), getParent()->getKey())));
}
mWidget->setColor(toColor());
}
void CSMPrefs::ColourSetting::valueChanged()
{
CSVWidget::ColorEditor& widget = dynamic_cast<CSVWidget::ColorEditor&>(*sender());
{
QMutexLocker lock(getMutex());
Settings::Manager::setString(getKey(), getParent()->getKey(), widget.color().name().toUtf8().data());
}
setValue(widget.color().name().toStdString());
getParent()->getState()->update(*this);
}

View File

@ -6,6 +6,7 @@
#include <QColor>
#include <string>
#include <string_view>
#include <utility>
class QMutex;
@ -20,22 +21,22 @@ namespace CSVWidget
namespace CSMPrefs
{
class Category;
class ColourSetting : public Setting
class ColourSetting final : public TypedSetting<std::string>
{
Q_OBJECT
std::string mTooltip;
QColor mDefault;
CSVWidget::ColorEditor* mWidget;
public:
ColourSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, QColor default_);
explicit ColourSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
ColourSetting& setTooltip(const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -15,12 +15,11 @@
#include "state.hpp"
CSMPrefs::DoubleSetting::DoubleSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, double default_)
: Setting(parent, mutex, key, label)
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mPrecision(2)
, mMin(0)
, mMax(std::numeric_limits<double>::max())
, mDefault(default_)
, mWidget(nullptr)
{
}
@ -56,14 +55,14 @@ CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setTooltip(const std::string&
return *this;
}
std::pair<QWidget*, QWidget*> CSMPrefs::DoubleSetting::makeWidgets(QWidget* parent)
CSMPrefs::SettingWidgets CSMPrefs::DoubleSetting::makeWidgets(QWidget* parent)
{
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QLabel* label = new QLabel(getLabel(), parent);
mWidget = new QDoubleSpinBox(parent);
mWidget->setDecimals(mPrecision);
mWidget->setRange(mMin, mMax);
mWidget->setValue(mDefault);
mWidget->setValue(getValue());
if (!mTooltip.empty())
{
@ -74,23 +73,17 @@ std::pair<QWidget*, QWidget*> CSMPrefs::DoubleSetting::makeWidgets(QWidget* pare
connect(mWidget, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &DoubleSetting::valueChanged);
return std::make_pair(label, mWidget);
return SettingWidgets{ .mLabel = label, .mInput = mWidget };
}
void CSMPrefs::DoubleSetting::updateWidget()
{
if (mWidget)
{
mWidget->setValue(Settings::Manager::getFloat(getKey(), getParent()->getKey()));
}
mWidget->setValue(getValue());
}
void CSMPrefs::DoubleSetting::valueChanged(double value)
{
{
QMutexLocker lock(getMutex());
Settings::Manager::setFloat(getKey(), getParent()->getKey(), value);
}
setValue(value);
getParent()->getState()->update(*this);
}

View File

@ -9,7 +9,7 @@ namespace CSMPrefs
{
class Category;
class DoubleSetting : public Setting
class DoubleSetting final : public TypedSetting<double>
{
Q_OBJECT
@ -17,12 +17,11 @@ namespace CSMPrefs
double mMin;
double mMax;
std::string mTooltip;
double mDefault;
QDoubleSpinBox* mWidget;
public:
DoubleSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, double default_);
explicit DoubleSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
DoubleSetting& setPrecision(int precision);
@ -36,7 +35,7 @@ namespace CSMPrefs
DoubleSetting& setTooltip(const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -15,39 +15,10 @@
#include "category.hpp"
#include "state.hpp"
CSMPrefs::EnumValue::EnumValue(const std::string& value, const std::string& tooltip)
: mValue(value)
, mTooltip(tooltip)
{
}
CSMPrefs::EnumValue::EnumValue(const char* value)
: mValue(value)
{
}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add(const EnumValues& values)
{
mValues.insert(mValues.end(), values.mValues.begin(), values.mValues.end());
return *this;
}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add(const EnumValue& value)
{
mValues.push_back(value);
return *this;
}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add(const std::string& value, const std::string& tooltip)
{
mValues.emplace_back(value, tooltip);
return *this;
}
CSMPrefs::EnumSetting::EnumSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, const EnumValue& default_)
: Setting(parent, mutex, key, label)
, mDefault(default_)
CSMPrefs::EnumSetting::EnumSetting(Category* parent, QMutex* mutex, std::string_view key, const QString& label,
std::span<const EnumValueView> values, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mValues(values)
, mWidget(nullptr)
{
}
@ -58,43 +29,28 @@ CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::setTooltip(const std::string& tool
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValues(const EnumValues& values)
CSMPrefs::SettingWidgets CSMPrefs::EnumSetting::makeWidgets(QWidget* parent)
{
mValues.add(values);
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue(const EnumValue& value)
{
mValues.add(value);
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue(const std::string& value, const std::string& tooltip)
{
mValues.add(value, tooltip);
return *this;
}
std::pair<QWidget*, QWidget*> CSMPrefs::EnumSetting::makeWidgets(QWidget* parent)
{
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QLabel* label = new QLabel(getLabel(), parent);
mWidget = new QComboBox(parent);
size_t index = 0;
for (size_t i = 0; i < mValues.mValues.size(); ++i)
for (std::size_t i = 0; i < mValues.size(); ++i)
{
if (mDefault.mValue == mValues.mValues[i].mValue)
index = i;
const EnumValueView& v = mValues[i];
mWidget->addItem(QString::fromUtf8(mValues.mValues[i].mValue.c_str()));
mWidget->addItem(QString::fromUtf8(v.mValue.data(), static_cast<int>(v.mValue.size())));
if (!mValues.mValues[i].mTooltip.empty())
mWidget->setItemData(i, QString::fromUtf8(mValues.mValues[i].mTooltip.c_str()), Qt::ToolTipRole);
if (!v.mTooltip.empty())
mWidget->setItemData(static_cast<int>(i),
QString::fromUtf8(v.mTooltip.data(), static_cast<int>(v.mTooltip.size())), Qt::ToolTipRole);
}
const std::string value = getValue();
const std::size_t index = std::find_if(mValues.begin(), mValues.end(), [&](const EnumValueView& v) {
return v.mValue == value;
}) - mValues.begin();
mWidget->setCurrentIndex(static_cast<int>(index));
if (!mTooltip.empty())
@ -105,26 +61,20 @@ std::pair<QWidget*, QWidget*> CSMPrefs::EnumSetting::makeWidgets(QWidget* parent
connect(mWidget, qOverload<int>(&QComboBox::currentIndexChanged), this, &EnumSetting::valueChanged);
return std::make_pair(label, mWidget);
return SettingWidgets{ .mLabel = label, .mInput = mWidget };
}
void CSMPrefs::EnumSetting::updateWidget()
{
if (mWidget)
{
int index
= mWidget->findText(QString::fromStdString(Settings::Manager::getString(getKey(), getParent()->getKey())));
mWidget->setCurrentIndex(index);
}
mWidget->setCurrentIndex(mWidget->findText(QString::fromStdString(getValue())));
}
void CSMPrefs::EnumSetting::valueChanged(int value)
{
{
QMutexLocker lock(getMutex());
Settings::Manager::setString(getKey(), getParent()->getKey(), mValues.mValues.at(value).mValue);
}
if (value < 0 || static_cast<std::size_t>(value) >= mValues.size())
throw std::logic_error("Invalid enum setting \"" + getKey() + "\" value index: " + std::to_string(value));
setValue(std::string(mValues[value].mValue));
getParent()->getState()->update(*this);
}

View File

@ -1,10 +1,13 @@
#ifndef CSM_PREFS_ENUMSETTING_H
#define CSM_PREFS_ENUMSETTING_H
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "enumvalueview.hpp"
#include "setting.hpp"
class QComboBox;
@ -13,50 +16,22 @@ namespace CSMPrefs
{
class Category;
struct EnumValue
{
std::string mValue;
std::string mTooltip;
EnumValue(const std::string& value, const std::string& tooltip = "");
EnumValue(const char* value);
};
struct EnumValues
{
std::vector<EnumValue> mValues;
EnumValues& add(const EnumValues& values);
EnumValues& add(const EnumValue& value);
EnumValues& add(const std::string& value, const std::string& tooltip);
};
class EnumSetting : public Setting
class EnumSetting final : public TypedSetting<std::string>
{
Q_OBJECT
std::string mTooltip;
EnumValue mDefault;
EnumValues mValues;
std::span<const EnumValueView> mValues;
QComboBox* mWidget;
public:
EnumSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label,
const EnumValue& default_);
explicit EnumSetting(Category* parent, QMutex* mutex, std::string_view key, const QString& label,
std::span<const EnumValueView> values, Settings::Index& index);
EnumSetting& setTooltip(const std::string& tooltip);
EnumSetting& addValues(const EnumValues& values);
EnumSetting& addValue(const EnumValue& value);
EnumSetting& addValue(const std::string& value, const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -0,0 +1,15 @@
#ifndef OPENMW_APPS_OPENCS_MODEL_PREFS_ENUMVALUEVIEW_H
#define OPENMW_APPS_OPENCS_MODEL_PREFS_ENUMVALUEVIEW_H
#include <string_view>
namespace CSMPrefs
{
struct EnumValueView
{
std::string_view mValue;
std::string_view mTooltip;
};
}
#endif

View File

@ -15,11 +15,10 @@
#include "state.hpp"
CSMPrefs::IntSetting::IntSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, int default_)
: Setting(parent, mutex, key, label)
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mMin(0)
, mMax(std::numeric_limits<int>::max())
, mDefault(default_)
, mWidget(nullptr)
{
}
@ -49,13 +48,13 @@ CSMPrefs::IntSetting& CSMPrefs::IntSetting::setTooltip(const std::string& toolti
return *this;
}
std::pair<QWidget*, QWidget*> CSMPrefs::IntSetting::makeWidgets(QWidget* parent)
CSMPrefs::SettingWidgets CSMPrefs::IntSetting::makeWidgets(QWidget* parent)
{
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QLabel* label = new QLabel(getLabel(), parent);
mWidget = new QSpinBox(parent);
mWidget->setRange(mMin, mMax);
mWidget->setValue(mDefault);
mWidget->setValue(getValue());
if (!mTooltip.empty())
{
@ -66,23 +65,17 @@ std::pair<QWidget*, QWidget*> CSMPrefs::IntSetting::makeWidgets(QWidget* parent)
connect(mWidget, qOverload<int>(&QSpinBox::valueChanged), this, &IntSetting::valueChanged);
return std::make_pair(label, mWidget);
return SettingWidgets{ .mLabel = label, .mInput = mWidget };
}
void CSMPrefs::IntSetting::updateWidget()
{
if (mWidget)
{
mWidget->setValue(Settings::Manager::getInt(getKey(), getParent()->getKey()));
}
mWidget->setValue(getValue());
}
void CSMPrefs::IntSetting::valueChanged(int value)
{
{
QMutexLocker lock(getMutex());
Settings::Manager::setInt(getKey(), getParent()->getKey(), value);
}
setValue(value);
getParent()->getState()->update(*this);
}

View File

@ -12,18 +12,18 @@ namespace CSMPrefs
{
class Category;
class IntSetting : public Setting
class IntSetting final : public TypedSetting<int>
{
Q_OBJECT
int mMin;
int mMax;
std::string mTooltip;
int mDefault;
QSpinBox* mWidget;
public:
IntSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label, int default_);
explicit IntSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
// defaults to [0, std::numeric_limits<int>::max()]
IntSetting& setRange(int min, int max);
@ -35,7 +35,7 @@ namespace CSMPrefs
IntSetting& setTooltip(const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -19,21 +19,22 @@ class QWidget;
namespace CSMPrefs
{
ModifierSetting::ModifierSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label)
: Setting(parent, mutex, key, label)
ModifierSetting::ModifierSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mButton(nullptr)
, mEditorActive(false)
{
}
std::pair<QWidget*, QWidget*> ModifierSetting::makeWidgets(QWidget* parent)
SettingWidgets ModifierSetting::makeWidgets(QWidget* parent)
{
int modifier = 0;
State::get().getShortcutManager().getModifier(getKey(), modifier);
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(modifier).c_str());
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QLabel* label = new QLabel(getLabel(), parent);
QPushButton* widget = new QPushButton(text, parent);
widget->setCheckable(true);
@ -46,14 +47,14 @@ namespace CSMPrefs
connect(widget, &QPushButton::toggled, this, &ModifierSetting::buttonToggled);
return std::make_pair(label, widget);
return SettingWidgets{ .mLabel = label, .mInput = widget };
}
void ModifierSetting::updateWidget()
{
if (mButton)
{
const std::string& shortcut = Settings::Manager::getString(getKey(), getParent()->getKey());
const std::string& shortcut = getValue();
int modifier;
State::get().getShortcutManager().convertFromString(shortcut, modifier);
@ -131,15 +132,7 @@ namespace CSMPrefs
void ModifierSetting::storeValue(int modifier)
{
State::get().getShortcutManager().setModifier(getKey(), modifier);
// Convert to string and assign
std::string value = State::get().getShortcutManager().convertToString(modifier);
{
QMutexLocker lock(getMutex());
Settings::Manager::setString(getKey(), getParent()->getKey(), value);
}
setValue(State::get().getShortcutManager().convertToString(modifier));
getParent()->getState()->update(*this);
}

View File

@ -15,14 +15,16 @@ class QPushButton;
namespace CSMPrefs
{
class Category;
class ModifierSetting : public Setting
class ModifierSetting final : public TypedSetting<std::string>
{
Q_OBJECT
public:
ModifierSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label);
explicit ModifierSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -5,6 +5,7 @@
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include <components/settings/settingvalue.hpp>
#include "category.hpp"
#include "state.hpp"
@ -14,22 +15,17 @@ QMutex* CSMPrefs::Setting::getMutex()
return mMutex;
}
CSMPrefs::Setting::Setting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label)
CSMPrefs::Setting::Setting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: QObject(parent->getState())
, mParent(parent)
, mMutex(mutex)
, mKey(key)
, mLabel(label)
, mIndex(index)
{
}
std::pair<QWidget*, QWidget*> CSMPrefs::Setting::makeWidgets(QWidget* parent)
{
return std::pair<QWidget*, QWidget*>(0, 0);
}
void CSMPrefs::Setting::updateWidget() {}
const CSMPrefs::Category* CSMPrefs::Setting::getParent() const
{
return mParent;
@ -40,35 +36,6 @@ const std::string& CSMPrefs::Setting::getKey() const
return mKey;
}
const std::string& CSMPrefs::Setting::getLabel() const
{
return mLabel;
}
int CSMPrefs::Setting::toInt() const
{
QMutexLocker lock(mMutex);
return Settings::Manager::getInt(mKey, mParent->getKey());
}
double CSMPrefs::Setting::toDouble() const
{
QMutexLocker lock(mMutex);
return Settings::Manager::getFloat(mKey, mParent->getKey());
}
std::string CSMPrefs::Setting::toString() const
{
QMutexLocker lock(mMutex);
return Settings::Manager::getString(mKey, mParent->getKey());
}
bool CSMPrefs::Setting::isTrue() const
{
QMutexLocker lock(mMutex);
return Settings::Manager::getBool(mKey, mParent->getKey());
}
QColor CSMPrefs::Setting::toColor() const
{
// toString() handles lock

View File

@ -4,15 +4,26 @@
#include <string>
#include <utility>
#include <QMutexLocker>
#include <QObject>
#include <components/settings/settingvalue.hpp>
#include "category.hpp"
class QWidget;
class QColor;
class QMutex;
class QGridLayout;
class QLabel;
namespace CSMPrefs
{
class Category;
struct SettingWidgets
{
QLabel* mLabel;
QWidget* mInput;
};
class Setting : public QObject
{
@ -21,44 +32,82 @@ namespace CSMPrefs
Category* mParent;
QMutex* mMutex;
std::string mKey;
std::string mLabel;
QString mLabel;
Settings::Index& mIndex;
protected:
QMutex* getMutex();
template <class T>
void resetValueImpl()
{
QMutexLocker lock(mMutex);
return mIndex.get<T>(mParent->getKey(), mKey).reset();
}
template <class T>
T getValueImpl() const
{
QMutexLocker lock(mMutex);
return mIndex.get<T>(mParent->getKey(), mKey).get();
}
template <class T>
void setValueImpl(const T& value)
{
QMutexLocker lock(mMutex);
return mIndex.get<T>(mParent->getKey(), mKey).set(value);
}
public:
Setting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label);
explicit Setting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
~Setting() override = default;
/// Return label, input widget.
///
/// \note first can be a 0-pointer, which means that the label is part of the input
/// widget.
virtual std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent);
virtual SettingWidgets makeWidgets(QWidget* parent) = 0;
/// Updates the widget returned by makeWidgets() to the current setting.
///
/// \note If make_widgets() has not been called yet then nothing happens.
virtual void updateWidget();
virtual void updateWidget() = 0;
virtual void reset() = 0;
const Category* getParent() const;
const std::string& getKey() const;
const std::string& getLabel() const;
const QString& getLabel() const { return mLabel; }
int toInt() const;
int toInt() const { return getValueImpl<int>(); }
double toDouble() const;
double toDouble() const { return getValueImpl<double>(); }
std::string toString() const;
std::string toString() const { return getValueImpl<std::string>(); }
bool isTrue() const;
bool isTrue() const { return getValueImpl<bool>(); }
QColor toColor() const;
};
template <class T>
class TypedSetting : public Setting
{
public:
using Setting::Setting;
void reset() final
{
resetValueImpl<T>();
updateWidget();
}
T getValue() const { return getValueImpl<T>(); }
void setValue(const T& value) { return setValueImpl(value); }
};
// note: fullKeys have the format categoryKey/settingKey
bool operator==(const Setting& setting, const std::string& fullKey);
bool operator==(const std::string& fullKey, const Setting& setting);

View File

@ -43,7 +43,7 @@ namespace CSMPrefs
mEventHandler->removeShortcut(shortcut);
}
bool ShortcutManager::getSequence(const std::string& name, QKeySequence& sequence) const
bool ShortcutManager::getSequence(std::string_view name, QKeySequence& sequence) const
{
SequenceMap::const_iterator item = mSequences.find(name);
if (item != mSequences.end())
@ -56,7 +56,7 @@ namespace CSMPrefs
return false;
}
void ShortcutManager::setSequence(const std::string& name, const QKeySequence& sequence)
void ShortcutManager::setSequence(std::string_view name, const QKeySequence& sequence)
{
// Add to map/modify
SequenceMap::iterator item = mSequences.find(name);
@ -91,7 +91,7 @@ namespace CSMPrefs
return false;
}
void ShortcutManager::setModifier(const std::string& name, int modifier)
void ShortcutManager::setModifier(std::string_view name, int modifier)
{
// Add to map/modify
ModifierMap::iterator item = mModifiers.find(name);

View File

@ -28,11 +28,11 @@ namespace CSMPrefs
/// The shortcut class will do this automatically
void removeShortcut(Shortcut* shortcut);
bool getSequence(const std::string& name, QKeySequence& sequence) const;
void setSequence(const std::string& name, const QKeySequence& sequence);
bool getSequence(std::string_view name, QKeySequence& sequence) const;
void setSequence(std::string_view name, const QKeySequence& sequence);
bool getModifier(const std::string& name, int& modifier) const;
void setModifier(const std::string& name, int modifier);
void setModifier(std::string_view name, int modifier);
std::string convertToString(const QKeySequence& sequence) const;
std::string convertToString(int modifier) const;
@ -49,9 +49,9 @@ namespace CSMPrefs
private:
// Need a multimap in case multiple shortcuts share the same name
typedef std::multimap<std::string, Shortcut*> ShortcutMap;
typedef std::map<std::string, QKeySequence> SequenceMap;
typedef std::map<std::string, int> ModifierMap;
typedef std::multimap<std::string, Shortcut*, std::less<>> ShortcutMap;
typedef std::map<std::string, QKeySequence, std::less<>> SequenceMap;
typedef std::map<std::string, int, std::less<>> ModifierMap;
typedef std::map<int, std::string> NameMap;
typedef std::map<std::string, int> KeyMap;

View File

@ -18,8 +18,9 @@
namespace CSMPrefs
{
ShortcutSetting::ShortcutSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label)
: Setting(parent, mutex, key, label)
ShortcutSetting::ShortcutSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mButton(nullptr)
, mEditorActive(false)
, mEditorPos(0)
@ -30,14 +31,14 @@ namespace CSMPrefs
}
}
std::pair<QWidget*, QWidget*> ShortcutSetting::makeWidgets(QWidget* parent)
SettingWidgets ShortcutSetting::makeWidgets(QWidget* parent)
{
QKeySequence sequence;
State::get().getShortcutManager().getSequence(getKey(), sequence);
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(sequence).c_str());
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
QLabel* label = new QLabel(getLabel(), parent);
QPushButton* widget = new QPushButton(text, parent);
widget->setCheckable(true);
@ -50,14 +51,14 @@ namespace CSMPrefs
connect(widget, &QPushButton::toggled, this, &ShortcutSetting::buttonToggled);
return std::make_pair(label, widget);
return SettingWidgets{ .mLabel = label, .mInput = widget };
}
void ShortcutSetting::updateWidget()
{
if (mButton)
{
const std::string& shortcut = Settings::Manager::getString(getKey(), getParent()->getKey());
const std::string shortcut = getValue();
QKeySequence sequence;
State::get().getShortcutManager().convertFromString(shortcut, sequence);
@ -170,15 +171,7 @@ namespace CSMPrefs
void ShortcutSetting::storeValue(const QKeySequence& sequence)
{
State::get().getShortcutManager().setSequence(getKey(), sequence);
// Convert to string and assign
std::string value = State::get().getShortcutManager().convertToString(sequence);
{
QMutexLocker lock(getMutex());
Settings::Manager::setString(getKey(), getParent()->getKey(), value);
}
setValue(State::get().getShortcutManager().convertToString(sequence));
getParent()->getState()->update(*this);
}

View File

@ -2,6 +2,7 @@
#define CSM_PREFS_SHORTCUTSETTING_H
#include <string>
#include <string_view>
#include <utility>
#include <QKeySequence>
@ -17,14 +18,16 @@ class QWidget;
namespace CSMPrefs
{
class Category;
class ShortcutSetting : public Setting
class ShortcutSetting final : public TypedSetting<std::string>
{
Q_OBJECT
public:
ShortcutSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label);
explicit ShortcutSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -13,6 +13,7 @@
#include <apps/opencs/model/prefs/enumsetting.hpp>
#include <apps/opencs/model/prefs/setting.hpp>
#include <apps/opencs/model/prefs/shortcutmanager.hpp>
#include <apps/opencs/model/prefs/subcategory.hpp>
#include <components/settings/categories.hpp>
#include <components/settings/settings.hpp>
@ -24,50 +25,43 @@
#include "modifiersetting.hpp"
#include "shortcutsetting.hpp"
#include "stringsetting.hpp"
#include "values.hpp"
CSMPrefs::State* CSMPrefs::State::sThis = nullptr;
void CSMPrefs::State::declare()
{
declareCategory("Windows");
declareInt("default-width", "Default window width", 800)
declareInt(mValues->mWindows.mDefaultWidth, "Default window width")
.setTooltip("Newly opened top-level windows will open with this width.")
.setMin(80);
declareInt("default-height", "Default window height", 600)
declareInt(mValues->mWindows.mDefaultHeight, "Default window height")
.setTooltip("Newly opened top-level windows will open with this height.")
.setMin(80);
declareBool("show-statusbar", "Show Status Bar", true)
declareBool(mValues->mWindows.mShowStatusbar, "Show Status Bar")
.setTooltip(
"If a newly open top level window is showing status bars or not. "
" Note that this does not affect existing windows.");
declareSeparator();
declareBool("reuse", "Reuse Subviews", true)
declareBool(mValues->mWindows.mReuse, "Reuse Subviews")
.setTooltip(
"When a new subview is requested and a matching subview already "
" exist, do not open a new subview and use the existing one instead.");
declareInt("max-subviews", "Maximum number of subviews per top-level window", 256)
declareInt(mValues->mWindows.mMaxSubviews, "Maximum number of subviews per top-level window")
.setTooltip(
"If the maximum number is reached and a new subview is opened "
"it will be placed into a new top-level window.")
.setRange(1, 256);
declareBool("hide-subview", "Hide single subview", false)
declareBool(mValues->mWindows.mHideSubview, "Hide single subview")
.setTooltip(
"When a view contains only a single subview, hide the subview title "
"bar and if this subview is closed also close the view (unless it is the last "
"view for this document)");
declareInt("minimum-width", "Minimum subview width", 325)
declareInt(mValues->mWindows.mMinimumWidth, "Minimum subview width")
.setTooltip("Minimum width of subviews.")
.setRange(50, 10000);
declareSeparator();
EnumValue scrollbarOnly("Scrollbar Only",
"Simple addition of scrollbars, the view window "
"does not grow automatically.");
declareEnum("mainwindow-scrollbar", "Horizontal scrollbar mode for main window.", scrollbarOnly)
.addValue(scrollbarOnly)
.addValue("Grow Only", "The view window grows as subviews are added. No scrollbars.")
.addValue("Grow then Scroll", "The view window grows. The scrollbar appears once it cannot grow any further.");
declareEnum(mValues->mWindows.mMainwindowScrollbar, "Horizontal scrollbar mode for main window.");
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
declareBool("grow-limit", "Grow Limit Screen", false)
declareBool(mValues->mWindows.mGrowLimit, "Grow Limit Screen")
.setTooltip(
"When \"Grow then Scroll\" option is selected, the window size grows to"
" the width of the virtual desktop. \nIf this option is selected the the window growth"
@ -75,378 +69,343 @@ void CSMPrefs::State::declare()
#endif
declareCategory("Records");
EnumValue iconAndText("Icon and Text");
EnumValues recordValues;
recordValues.add(iconAndText).add("Icon Only").add("Text Only");
declareEnum("status-format", "Modification status display format", iconAndText).addValues(recordValues);
declareEnum("type-format", "ID type display format", iconAndText).addValues(recordValues);
declareEnum(mValues->mRecords.mStatusFormat, "Modification status display format");
declareEnum(mValues->mRecords.mTypeFormat, "ID type display format");
declareCategory("ID Tables");
EnumValue inPlaceEdit("Edit in Place", "Edit the clicked cell");
EnumValue editRecord("Edit Record", "Open a dialogue subview for the clicked record");
EnumValue view("View", "Open a scene subview for the clicked record (not available everywhere)");
EnumValue editRecordAndClose("Edit Record and Close");
EnumValues doubleClickValues;
doubleClickValues.add(inPlaceEdit)
.add(editRecord)
.add(view)
.add("Revert")
.add("Delete")
.add(editRecordAndClose)
.add("View and Close", "Open a scene subview for the clicked record and close the table subview");
declareEnum("double", "Double Click", inPlaceEdit).addValues(doubleClickValues);
declareEnum("double-s", "Shift Double Click", editRecord).addValues(doubleClickValues);
declareEnum("double-c", "Control Double Click", view).addValues(doubleClickValues);
declareEnum("double-sc", "Shift Control Double Click", editRecordAndClose).addValues(doubleClickValues);
declareSeparator();
EnumValue jumpAndSelect("Jump and Select", "Scroll new record into view and make it the selection");
declareEnum("jump-to-added", "Action on adding or cloning a record", jumpAndSelect)
.addValue(jumpAndSelect)
.addValue("Jump Only", "Scroll new record into view")
.addValue("No Jump", "No special action");
declareBool("extended-config", "Manually specify affected record types for an extended delete/revert", false)
declareEnum(mValues->mIdTables.mDouble, "Double Click");
declareEnum(mValues->mIdTables.mDoubleS, "Shift Double Click");
declareEnum(mValues->mIdTables.mDoubleC, "Control Double Click");
declareEnum(mValues->mIdTables.mDoubleSc, "Shift Control Double Click");
declareEnum(mValues->mIdTables.mJumpToAdded, "Action on adding or cloning a record");
declareBool(
mValues->mIdTables.mExtendedConfig, "Manually specify affected record types for an extended delete/revert")
.setTooltip(
"Delete and revert commands have an extended form that also affects "
"associated records.\n\n"
"If this option is enabled, types of affected records are selected "
"manually before a command execution.\nOtherwise, all associated "
"records are deleted/reverted immediately.");
declareBool("subview-new-window", "Open Record in new window", false)
declareBool(mValues->mIdTables.mSubviewNewWindow, "Open Record in new window")
.setTooltip(
"When editing a record, open the view in a new window,"
" rather than docked in the main view.");
declareCategory("ID Dialogues");
declareBool("toolbar", "Show toolbar", true);
declareBool(mValues->mIdDialogues.mToolbar, "Show toolbar");
declareCategory("Reports");
EnumValue actionNone("None");
EnumValue actionEdit("Edit", "Open a table or dialogue suitable for addressing the listed report");
EnumValue actionRemove("Remove", "Remove the report from the report table");
EnumValue actionEditAndRemove("Edit And Remove",
"Open a table or dialogue suitable for addressing the listed report, then remove the report from the report "
"table");
EnumValues reportValues;
reportValues.add(actionNone).add(actionEdit).add(actionRemove).add(actionEditAndRemove);
declareEnum("double", "Double Click", actionEdit).addValues(reportValues);
declareEnum("double-s", "Shift Double Click", actionRemove).addValues(reportValues);
declareEnum("double-c", "Control Double Click", actionEditAndRemove).addValues(reportValues);
declareEnum("double-sc", "Shift Control Double Click", actionNone).addValues(reportValues);
declareBool("ignore-base-records", "Ignore base records in verifier", false);
declareEnum(mValues->mReports.mDouble, "Double Click");
declareEnum(mValues->mReports.mDoubleS, "Shift Double Click");
declareEnum(mValues->mReports.mDoubleC, "Control Double Click");
declareEnum(mValues->mReports.mDoubleSc, "Shift Control Double Click");
declareBool(mValues->mReports.mIgnoreBaseRecords, "Ignore base records in verifier");
declareCategory("Search & Replace");
declareInt("char-before", "Characters before search string", 10)
declareInt(mValues->mSearchAndReplace.mCharBefore, "Characters before search string")
.setTooltip("Maximum number of character to display in search result before the searched text");
declareInt("char-after", "Characters after search string", 10)
declareInt(mValues->mSearchAndReplace.mCharAfter, "Characters after search string")
.setTooltip("Maximum number of character to display in search result after the searched text");
declareBool("auto-delete", "Delete row from result table after a successful replace", true);
declareBool(mValues->mSearchAndReplace.mAutoDelete, "Delete row from result table after a successful replace");
declareCategory("Scripts");
declareBool("show-linenum", "Show Line Numbers", true)
declareBool(mValues->mScripts.mShowLinenum, "Show Line Numbers")
.setTooltip(
"Show line numbers to the left of the script editor window."
"The current row and column numbers of the text cursor are shown at the bottom.");
declareBool("wrap-lines", "Wrap Lines", false).setTooltip("Wrap lines longer than width of script editor.");
declareBool("mono-font", "Use monospace font", true);
declareInt("tab-width", "Tab Width", 4).setTooltip("Number of characters for tab width").setRange(1, 10);
EnumValue warningsNormal("Normal", "Report warnings as warning");
declareEnum("warnings", "Warning Mode", warningsNormal)
.addValue("Ignore", "Do not report warning")
.addValue(warningsNormal)
.addValue("Strict", "Promote warning to an error");
declareBool("toolbar", "Show toolbar", true);
declareInt("compile-delay", "Delay between updating of source errors", 100)
declareBool(mValues->mScripts.mWrapLines, "Wrap Lines")
.setTooltip("Wrap lines longer than width of script editor.");
declareBool(mValues->mScripts.mMonoFont, "Use monospace font");
declareInt(mValues->mScripts.mTabWidth, "Tab Width")
.setTooltip("Number of characters for tab width")
.setRange(1, 10);
declareEnum(mValues->mScripts.mWarnings, "Warning Mode");
declareBool(mValues->mScripts.mToolbar, "Show toolbar");
declareInt(mValues->mScripts.mCompileDelay, "Delay between updating of source errors")
.setTooltip("Delay in milliseconds")
.setRange(0, 10000);
declareInt("error-height", "Initial height of the error panel", 100).setRange(100, 10000);
declareBool("highlight-occurrences", "Highlight other occurrences of selected names", true);
declareColour("colour-highlight", "Colour of highlighted occurrences", QColor("lightcyan"));
declareSeparator();
declareColour("colour-int", "Highlight Colour: Integer Literals", QColor("darkmagenta"));
declareColour("colour-float", "Highlight Colour: Float Literals", QColor("magenta"));
declareColour("colour-name", "Highlight Colour: Names", QColor("grey"));
declareColour("colour-keyword", "Highlight Colour: Keywords", QColor("red"));
declareColour("colour-special", "Highlight Colour: Special Characters", QColor("darkorange"));
declareColour("colour-comment", "Highlight Colour: Comments", QColor("green"));
declareColour("colour-id", "Highlight Colour: IDs", QColor("blue"));
declareInt(mValues->mScripts.mErrorHeight, "Initial height of the error panel").setRange(100, 10000);
declareBool(mValues->mScripts.mHighlightOccurrences, "Highlight other occurrences of selected names");
declareColour(mValues->mScripts.mColourHighlight, "Colour of highlighted occurrences");
declareColour(mValues->mScripts.mColourInt, "Highlight Colour: Integer Literals");
declareColour(mValues->mScripts.mColourFloat, "Highlight Colour: Float Literals");
declareColour(mValues->mScripts.mColourName, "Highlight Colour: Names");
declareColour(mValues->mScripts.mColourKeyword, "Highlight Colour: Keywords");
declareColour(mValues->mScripts.mColourSpecial, "Highlight Colour: Special Characters");
declareColour(mValues->mScripts.mColourComment, "Highlight Colour: Comments");
declareColour(mValues->mScripts.mColourId, "Highlight Colour: IDs");
declareCategory("General Input");
declareBool("cycle", "Cyclic next/previous", false)
declareBool(mValues->mGeneralInput.mCycle, "Cyclic next/previous")
.setTooltip(
"When using next/previous functions at the last/first item of a "
"list go to the first/last item");
declareCategory("3D Scene Input");
declareDouble("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareSeparator();
declareDouble(mValues->mSceneInput.mNaviWheelFactor, "Camera Zoom Sensitivity").setRange(-100.0, 100.0);
declareDouble(mValues->mSceneInput.mSNaviSensitivity, "Secondary Camera Movement Sensitivity")
.setRange(-1000.0, 1000.0);
declareDouble("p-navi-free-sensitivity", "Free Camera Sensitivity", 1 / 650.).setPrecision(5).setRange(0.0, 1.0);
declareBool("p-navi-free-invert", "Invert Free Camera Mouse Input", false);
declareDouble("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0);
declareDouble("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareDouble("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareSeparator();
declareDouble("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1 / 650.).setPrecision(5).setRange(0.0, 1.0);
declareBool("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
declareDouble("navi-orbit-rot-speed", "Orbital Camera Rotational Speed", 3.14 / 4).setRange(0.001, 6.28);
declareDouble("navi-orbit-speed-mult", "Orbital Camera Speed Multiplier (from Modifier)", 4)
declareDouble(mValues->mSceneInput.mPNaviFreeSensitivity, "Free Camera Sensitivity")
.setPrecision(5)
.setRange(0.0, 1.0);
declareBool(mValues->mSceneInput.mPNaviFreeInvert, "Invert Free Camera Mouse Input");
declareDouble(mValues->mSceneInput.mNaviFreeLinSpeed, "Free Camera Linear Speed").setRange(1.0, 10000.0);
declareDouble(mValues->mSceneInput.mNaviFreeRotSpeed, "Free Camera Rotational Speed").setRange(0.001, 6.28);
declareDouble(mValues->mSceneInput.mNaviFreeSpeedMult, "Free Camera Speed Multiplier (from Modifier)")
.setRange(0.001, 1000.0);
declareBool("navi-orbit-const-roll", "Keep camera roll constant for orbital camera", true);
declareSeparator();
declareBool("context-select", "Context Sensitive Selection", false);
declareDouble("drag-factor", "Mouse sensitivity during drag operations", 1.0).setRange(0.001, 100.0);
declareDouble("drag-wheel-factor", "Mouse wheel sensitivity during drag operations", 1.0).setRange(0.001, 100.0);
declareDouble("drag-shift-factor", "Shift-acceleration factor during drag operations", 4.0)
declareDouble(mValues->mSceneInput.mPNaviOrbitSensitivity, "Orbit Camera Sensitivity")
.setPrecision(5)
.setRange(0.0, 1.0);
declareBool(mValues->mSceneInput.mPNaviOrbitInvert, "Invert Orbit Camera Mouse Input");
declareDouble(mValues->mSceneInput.mNaviOrbitRotSpeed, "Orbital Camera Rotational Speed").setRange(0.001, 6.28);
declareDouble(mValues->mSceneInput.mNaviOrbitSpeedMult, "Orbital Camera Speed Multiplier (from Modifier)")
.setRange(0.001, 1000.0);
declareBool(mValues->mSceneInput.mNaviOrbitConstRoll, "Keep camera roll constant for orbital camera");
declareBool(mValues->mSceneInput.mContextSelect, "Context Sensitive Selection");
declareDouble(mValues->mSceneInput.mDragFactor, "Mouse sensitivity during drag operations").setRange(0.001, 100.0);
declareDouble(mValues->mSceneInput.mDragWheelFactor, "Mouse wheel sensitivity during drag operations")
.setRange(0.001, 100.0);
declareDouble(mValues->mSceneInput.mDragShiftFactor, "Shift-acceleration factor during drag operations")
.setTooltip("Acceleration factor during drag operations while holding down shift")
.setRange(0.001, 100.0);
declareDouble("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareDouble(mValues->mSceneInput.mRotateFactor, "Free rotation factor").setPrecision(4).setRange(0.0001, 0.1);
declareCategory("Rendering");
declareInt("framerate-limit", "FPS limit", 60)
declareInt(mValues->mRendering.mFramerateLimit, "FPS limit")
.setTooltip("Framerate limit in 3D preview windows. Zero value means \"unlimited\".")
.setRange(0, 10000);
declareInt("camera-fov", "Camera FOV", 90).setRange(10, 170);
declareBool("camera-ortho", "Orthographic projection for camera", false);
declareInt("camera-ortho-size", "Orthographic projection size parameter", 100)
declareInt(mValues->mRendering.mCameraFov, "Camera FOV").setRange(10, 170);
declareBool(mValues->mRendering.mCameraOrtho, "Orthographic projection for camera");
declareInt(mValues->mRendering.mCameraOrthoSize, "Orthographic projection size parameter")
.setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.")
.setRange(10, 10000);
declareDouble("object-marker-alpha", "Object Marker Transparency", 0.5).setPrecision(2).setRange(0, 1);
declareBool("scene-use-gradient", "Use Gradient Background", true);
declareColour("scene-day-background-colour", "Day Background Colour", QColor(110, 120, 128, 255));
declareColour("scene-day-gradient-colour", "Day Gradient Colour", QColor(47, 51, 51, 255))
declareDouble(mValues->mRendering.mObjectMarkerAlpha, "Object Marker Transparency").setPrecision(2).setRange(0, 1);
declareBool(mValues->mRendering.mSceneUseGradient, "Use Gradient Background");
declareColour(mValues->mRendering.mSceneDayBackgroundColour, "Day Background Colour");
declareColour(mValues->mRendering.mSceneDayGradientColour, "Day Gradient Colour")
.setTooltip(
"Sets the gradient color to use in conjunction with the day background color. Ignored if "
"the gradient option is disabled.");
declareColour("scene-bright-background-colour", "Scene Bright Background Colour", QColor(79, 87, 92, 255));
declareColour("scene-bright-gradient-colour", "Scene Bright Gradient Colour", QColor(47, 51, 51, 255))
declareColour(mValues->mRendering.mSceneBrightBackgroundColour, "Scene Bright Background Colour");
declareColour(mValues->mRendering.mSceneBrightGradientColour, "Scene Bright Gradient Colour")
.setTooltip(
"Sets the gradient color to use in conjunction with the bright background color. Ignored if "
"the gradient option is disabled.");
declareColour("scene-night-background-colour", "Scene Night Background Colour", QColor(64, 77, 79, 255));
declareColour("scene-night-gradient-colour", "Scene Night Gradient Colour", QColor(47, 51, 51, 255))
declareColour(mValues->mRendering.mSceneNightBackgroundColour, "Scene Night Background Colour");
declareColour(mValues->mRendering.mSceneNightGradientColour, "Scene Night Gradient Colour")
.setTooltip(
"Sets the gradient color to use in conjunction with the night background color. Ignored if "
"the gradient option is disabled.");
declareBool("scene-day-night-switch-nodes", "Use Day/Night Switch Nodes", true);
declareBool(mValues->mRendering.mSceneDayNightSwitchNodes, "Use Day/Night Switch Nodes");
declareCategory("Tooltips");
declareBool("scene", "Show Tooltips in 3D scenes", true);
declareBool("scene-hide-basic", "Hide basic 3D scenes tooltips", false);
declareInt("scene-delay", "Tooltip delay in milliseconds", 500).setMin(1);
EnumValue createAndInsert("Create cell and insert");
EnumValue showAndInsert("Show cell and insert");
EnumValue dontInsert("Discard");
EnumValue insertAnyway("Insert anyway");
EnumValues insertOutsideCell;
insertOutsideCell.add(createAndInsert).add(dontInsert).add(insertAnyway);
EnumValues insertOutsideVisibleCell;
insertOutsideVisibleCell.add(showAndInsert).add(dontInsert).add(insertAnyway);
EnumValue createAndLandEdit("Create cell and land, then edit");
EnumValue showAndLandEdit("Show cell and edit");
EnumValue dontLandEdit("Discard");
EnumValues landeditOutsideCell;
landeditOutsideCell.add(createAndLandEdit).add(dontLandEdit);
EnumValues landeditOutsideVisibleCell;
landeditOutsideVisibleCell.add(showAndLandEdit).add(dontLandEdit);
EnumValue SelectOnly("Select only");
EnumValue SelectAdd("Add to selection");
EnumValue SelectRemove("Remove from selection");
EnumValue selectInvert("Invert selection");
EnumValues primarySelectAction;
primarySelectAction.add(SelectOnly).add(SelectAdd).add(SelectRemove).add(selectInvert);
EnumValues secondarySelectAction;
secondarySelectAction.add(SelectOnly).add(SelectAdd).add(SelectRemove).add(selectInvert);
declareBool(mValues->mTooltips.mScene, "Show Tooltips in 3D scenes");
declareBool(mValues->mTooltips.mSceneHideBasic, "Hide basic 3D scenes tooltips");
declareInt(mValues->mTooltips.mSceneDelay, "Tooltip delay in milliseconds").setMin(1);
declareCategory("3D Scene Editing");
declareDouble("gridsnap-movement", "Grid snap size", 16);
declareDouble("gridsnap-rotation", "Angle snap size", 15);
declareDouble("gridsnap-scale", "Scale snap size", 0.25);
declareInt("distance", "Drop Distance", 50)
declareDouble(mValues->mSceneEditing.mGridsnapMovement, "Grid snap size");
declareDouble(mValues->mSceneEditing.mGridsnapRotation, "Angle snap size");
declareDouble(mValues->mSceneEditing.mGridsnapScale, "Scale snap size");
declareInt(mValues->mSceneEditing.mDistance, "Drop Distance")
.setTooltip(
"If an instance drop can not be placed against another object at the "
"insert point, it will be placed by this distance from the insert point instead");
declareEnum("outside-drop", "Handling drops outside of cells", createAndInsert).addValues(insertOutsideCell);
declareEnum("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert)
.addValues(insertOutsideVisibleCell);
declareEnum("outside-landedit", "Handling terrain edit outside of cells", createAndLandEdit)
.setTooltip("Behavior of terrain editing, if land editing brush reaches an area without cell record.")
.addValues(landeditOutsideCell);
declareEnum("outside-visible-landedit", "Handling terrain edit outside of visible cells", showAndLandEdit)
.setTooltip("Behavior of terrain editing, if land editing brush reaches an area that is not currently visible.")
.addValues(landeditOutsideVisibleCell);
declareInt("texturebrush-maximumsize", "Maximum texture brush size", 50).setMin(1);
declareInt("shapebrush-maximumsize", "Maximum height edit brush size", 100)
declareEnum(mValues->mSceneEditing.mOutsideDrop, "Handling drops outside of cells");
declareEnum(mValues->mSceneEditing.mOutsideVisibleDrop, "Handling drops outside of visible cells");
declareEnum(mValues->mSceneEditing.mOutsideLandedit, "Handling terrain edit outside of cells")
.setTooltip("Behavior of terrain editing, if land editing brush reaches an area without cell record.");
declareEnum(mValues->mSceneEditing.mOutsideVisibleLandedit, "Handling terrain edit outside of visible cells")
.setTooltip(
"Behavior of terrain editing, if land editing brush reaches an area that is not currently visible.");
declareInt(mValues->mSceneEditing.mTexturebrushMaximumsize, "Maximum texture brush size").setMin(1);
declareInt(mValues->mSceneEditing.mShapebrushMaximumsize, "Maximum height edit brush size")
.setTooltip("Setting for the slider range of brush size in terrain height editing.")
.setMin(1);
declareBool("landedit-post-smoothpainting", "Smooth land after painting height", false)
declareBool(mValues->mSceneEditing.mLandeditPostSmoothpainting, "Smooth land after painting height")
.setTooltip("Raise and lower tools will leave bumpy finish without this option");
declareDouble("landedit-post-smoothstrength", "Smoothing strength (post-edit)", 0.25)
declareDouble(mValues->mSceneEditing.mLandeditPostSmoothstrength, "Smoothing strength (post-edit)")
.setTooltip(
"If smoothing land after painting height is used, this is the percentage of smooth applied afterwards. "
"Negative values may be used to roughen instead of smooth.")
.setMin(-1)
.setMax(1);
declareBool("open-list-view", "Open displays list view", false)
declareBool(mValues->mSceneEditing.mOpenListView, "Open displays list view")
.setTooltip(
"When opening a reference from the scene view, it will open the"
" instance list view instead of the individual instance record view.");
declareEnum("primary-select-action", "Action for primary select", SelectOnly)
declareEnum(mValues->mSceneEditing.mPrimarySelectAction, "Action for primary select")
.setTooltip(
"Selection can be chosen between select only, add to selection, remove from selection and invert "
"selection.")
.addValues(primarySelectAction);
declareEnum("secondary-select-action", "Action for secondary select", SelectAdd)
"selection.");
declareEnum(mValues->mSceneEditing.mSecondarySelectAction, "Action for secondary select")
.setTooltip(
"Selection can be chosen between select only, add to selection, remove from selection and invert "
"selection.")
.addValues(secondarySelectAction);
"selection.");
declareCategory("Key Bindings");
declareSubcategory("Document");
declareShortcut("document-file-newgame", "New Game", QKeySequence(Qt::ControlModifier | Qt::Key_N));
declareShortcut("document-file-newaddon", "New Addon", QKeySequence());
declareShortcut("document-file-open", "Open", QKeySequence(Qt::ControlModifier | Qt::Key_O));
declareShortcut("document-file-save", "Save", QKeySequence(Qt::ControlModifier | Qt::Key_S));
declareShortcut("document-help-help", "Help", QKeySequence(Qt::Key_F1));
declareShortcut("document-help-tutorial", "Tutorial", QKeySequence());
declareShortcut("document-file-verify", "Verify", QKeySequence());
declareShortcut("document-file-merge", "Merge", QKeySequence());
declareShortcut("document-file-errorlog", "Open Load Error Log", QKeySequence());
declareShortcut("document-file-metadata", "Meta Data", QKeySequence());
declareShortcut("document-file-close", "Close Document", QKeySequence(Qt::ControlModifier | Qt::Key_W));
declareShortcut("document-file-exit", "Exit Application", QKeySequence(Qt::ControlModifier | Qt::Key_Q));
declareShortcut("document-edit-undo", "Undo", QKeySequence(Qt::ControlModifier | Qt::Key_Z));
declareShortcut("document-edit-redo", "Redo", QKeySequence(Qt::ControlModifier | Qt::ShiftModifier | Qt::Key_Z));
declareShortcut("document-edit-preferences", "Open Preferences", QKeySequence());
declareShortcut("document-edit-search", "Search", QKeySequence(Qt::ControlModifier | Qt::Key_F));
declareShortcut("document-view-newview", "New View", QKeySequence());
declareShortcut("document-view-statusbar", "Toggle Status Bar", QKeySequence());
declareShortcut("document-view-filters", "Open Filter List", QKeySequence());
declareShortcut("document-world-regions", "Open Region List", QKeySequence());
declareShortcut("document-world-cells", "Open Cell List", QKeySequence());
declareShortcut("document-world-referencables", "Open Object List", QKeySequence());
declareShortcut("document-world-references", "Open Instance List", QKeySequence());
declareShortcut("document-world-lands", "Open Lands List", QKeySequence());
declareShortcut("document-world-landtextures", "Open Land Textures List", QKeySequence());
declareShortcut("document-world-pathgrid", "Open Pathgrid List", QKeySequence());
declareShortcut("document-world-regionmap", "Open Region Map", QKeySequence());
declareShortcut("document-mechanics-globals", "Open Global List", QKeySequence());
declareShortcut("document-mechanics-gamesettings", "Open Game Settings", QKeySequence());
declareShortcut("document-mechanics-scripts", "Open Script List", QKeySequence());
declareShortcut("document-mechanics-spells", "Open Spell List", QKeySequence());
declareShortcut("document-mechanics-enchantments", "Open Enchantment List", QKeySequence());
declareShortcut("document-mechanics-magiceffects", "Open Magic Effect List", QKeySequence());
declareShortcut("document-mechanics-startscripts", "Open Start Script List", QKeySequence());
declareShortcut("document-character-skills", "Open Skill List", QKeySequence());
declareShortcut("document-character-classes", "Open Class List", QKeySequence());
declareShortcut("document-character-factions", "Open Faction List", QKeySequence());
declareShortcut("document-character-races", "Open Race List", QKeySequence());
declareShortcut("document-character-birthsigns", "Open Birthsign List", QKeySequence());
declareShortcut("document-character-topics", "Open Topic List", QKeySequence());
declareShortcut("document-character-journals", "Open Journal List", QKeySequence());
declareShortcut("document-character-topicinfos", "Open Topic Info List", QKeySequence());
declareShortcut("document-character-journalinfos", "Open Journal Info List", QKeySequence());
declareShortcut("document-character-bodyparts", "Open Body Part List", QKeySequence());
declareShortcut("document-assets-reload", "Reload Assets", QKeySequence(Qt::Key_F5));
declareShortcut("document-assets-sounds", "Open Sound Asset List", QKeySequence());
declareShortcut("document-assets-soundgens", "Open Sound Generator List", QKeySequence());
declareShortcut("document-assets-meshes", "Open Mesh Asset List", QKeySequence());
declareShortcut("document-assets-icons", "Open Icon Asset List", QKeySequence());
declareShortcut("document-assets-music", "Open Music Asset List", QKeySequence());
declareShortcut("document-assets-soundres", "Open Sound File List", QKeySequence());
declareShortcut("document-assets-textures", "Open Texture Asset List", QKeySequence());
declareShortcut("document-assets-videos", "Open Video Asset List", QKeySequence());
declareShortcut("document-debug-run", "Run Debug", QKeySequence());
declareShortcut("document-debug-shutdown", "Stop Debug", QKeySequence());
declareShortcut("document-debug-profiles", "Debug Profiles", QKeySequence());
declareShortcut("document-debug-runlog", "Open Run Log", QKeySequence());
declareShortcut(mValues->mKeyBindings.mDocumentFileNewgame, "New Game");
declareShortcut(mValues->mKeyBindings.mDocumentFileNewaddon, "New Addon");
declareShortcut(mValues->mKeyBindings.mDocumentFileOpen, "Open");
declareShortcut(mValues->mKeyBindings.mDocumentFileSave, "Save");
declareShortcut(mValues->mKeyBindings.mDocumentHelpHelp, "Help");
declareShortcut(mValues->mKeyBindings.mDocumentHelpTutorial, "Tutorial");
declareShortcut(mValues->mKeyBindings.mDocumentFileVerify, "Verify");
declareShortcut(mValues->mKeyBindings.mDocumentFileMerge, "Merge");
declareShortcut(mValues->mKeyBindings.mDocumentFileErrorlog, "Open Load Error Log");
declareShortcut(mValues->mKeyBindings.mDocumentFileMetadata, "Meta Data");
declareShortcut(mValues->mKeyBindings.mDocumentFileClose, "Close Document");
declareShortcut(mValues->mKeyBindings.mDocumentFileExit, "Exit Application");
declareShortcut(mValues->mKeyBindings.mDocumentEditUndo, "Undo");
declareShortcut(mValues->mKeyBindings.mDocumentEditRedo, "Redo");
declareShortcut(mValues->mKeyBindings.mDocumentEditPreferences, "Open Preferences");
declareShortcut(mValues->mKeyBindings.mDocumentEditSearch, "Search");
declareShortcut(mValues->mKeyBindings.mDocumentViewNewview, "New View");
declareShortcut(mValues->mKeyBindings.mDocumentViewStatusbar, "Toggle Status Bar");
declareShortcut(mValues->mKeyBindings.mDocumentViewFilters, "Open Filter List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldRegions, "Open Region List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldCells, "Open Cell List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldReferencables, "Open Object List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldReferences, "Open Instance List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldLands, "Open Lands List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldLandtextures, "Open Land Textures List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldPathgrid, "Open Pathgrid List");
declareShortcut(mValues->mKeyBindings.mDocumentWorldRegionmap, "Open Region Map");
declareShortcut(mValues->mKeyBindings.mDocumentMechanicsGlobals, "Open Global List");
declareShortcut(mValues->mKeyBindings.mDocumentMechanicsGamesettings, "Open Game Settings");
declareShortcut(mValues->mKeyBindings.mDocumentMechanicsScripts, "Open Script List");
declareShortcut(mValues->mKeyBindings.mDocumentMechanicsSpells, "Open Spell List");
declareShortcut(mValues->mKeyBindings.mDocumentMechanicsEnchantments, "Open Enchantment List");
declareShortcut(mValues->mKeyBindings.mDocumentMechanicsMagiceffects, "Open Magic Effect List");
declareShortcut(mValues->mKeyBindings.mDocumentMechanicsStartscripts, "Open Start Script List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterSkills, "Open Skill List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterClasses, "Open Class List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterFactions, "Open Faction List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterRaces, "Open Race List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterBirthsigns, "Open Birthsign List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterTopics, "Open Topic List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterJournals, "Open Journal List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterTopicinfos, "Open Topic Info List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterJournalinfos, "Open Journal Info List");
declareShortcut(mValues->mKeyBindings.mDocumentCharacterBodyparts, "Open Body Part List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsReload, "Reload Assets");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsSounds, "Open Sound Asset List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsSoundgens, "Open Sound Generator List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsMeshes, "Open Mesh Asset List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsIcons, "Open Icon Asset List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsMusic, "Open Music Asset List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsSoundres, "Open Sound File List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsTextures, "Open Texture Asset List");
declareShortcut(mValues->mKeyBindings.mDocumentAssetsVideos, "Open Video Asset List");
declareShortcut(mValues->mKeyBindings.mDocumentDebugRun, "Run Debug");
declareShortcut(mValues->mKeyBindings.mDocumentDebugShutdown, "Stop Debug");
declareShortcut(mValues->mKeyBindings.mDocumentDebugProfiles, "Debug Profiles");
declareShortcut(mValues->mKeyBindings.mDocumentDebugRunlog, "Open Run Log");
declareSubcategory("Table");
declareShortcut("table-edit", "Edit Record", QKeySequence());
declareShortcut("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A));
declareShortcut("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D));
declareShortcut("touch-record", "Touch Record", QKeySequence());
declareShortcut("table-revert", "Revert Record", QKeySequence());
declareShortcut("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete));
declareShortcut("table-moveup", "Move Record Up", QKeySequence());
declareShortcut("table-movedown", "Move Record Down", QKeySequence());
declareShortcut("table-view", "View Record", QKeySequence(Qt::ShiftModifier | Qt::Key_C));
declareShortcut("table-preview", "Preview Record", QKeySequence(Qt::ShiftModifier | Qt::Key_V));
declareShortcut("table-extendeddelete", "Extended Record Deletion", QKeySequence());
declareShortcut("table-extendedrevert", "Extended Record Revertion", QKeySequence());
declareShortcut(mValues->mKeyBindings.mTableEdit, "Edit Record");
declareShortcut(mValues->mKeyBindings.mTableAdd, "Add Row/Record");
declareShortcut(mValues->mKeyBindings.mTableClone, "Clone Record");
declareShortcut(mValues->mKeyBindings.mTouchRecord, "Touch Record");
declareShortcut(mValues->mKeyBindings.mTableRevert, "Revert Record");
declareShortcut(mValues->mKeyBindings.mTableRemove, "Remove Row/Record");
declareShortcut(mValues->mKeyBindings.mTableMoveup, "Move Record Up");
declareShortcut(mValues->mKeyBindings.mTableMovedown, "Move Record Down");
declareShortcut(mValues->mKeyBindings.mTableView, "View Record");
declareShortcut(mValues->mKeyBindings.mTablePreview, "Preview Record");
declareShortcut(mValues->mKeyBindings.mTableExtendeddelete, "Extended Record Deletion");
declareShortcut(mValues->mKeyBindings.mTableExtendedrevert, "Extended Record Revertion");
declareSubcategory("Report Table");
declareShortcut("reporttable-show", "Show Report", QKeySequence());
declareShortcut("reporttable-remove", "Remove Report", QKeySequence(Qt::Key_Delete));
declareShortcut("reporttable-replace", "Replace Report", QKeySequence());
declareShortcut("reporttable-refresh", "Refresh Report", QKeySequence());
declareShortcut(mValues->mKeyBindings.mReporttableShow, "Show Report");
declareShortcut(mValues->mKeyBindings.mReporttableRemove, "Remove Report");
declareShortcut(mValues->mKeyBindings.mReporttableReplace, "Replace Report");
declareShortcut(mValues->mKeyBindings.mReporttableRefresh, "Refresh Report");
declareSubcategory("Scene");
declareShortcut("scene-navi-primary", "Camera Rotation From Mouse Movement", QKeySequence(Qt::LeftButton));
declareShortcut("scene-navi-secondary", "Camera Translation From Mouse Movement",
QKeySequence(Qt::ControlModifier | (int)Qt::LeftButton));
declareShortcut("scene-open-primary", "Primary Open", QKeySequence(Qt::ShiftModifier | (int)Qt::LeftButton));
declareShortcut("scene-edit-primary", "Primary Edit", QKeySequence(Qt::RightButton));
declareShortcut("scene-edit-secondary", "Secondary Edit", QKeySequence(Qt::ControlModifier | (int)Qt::RightButton));
declareShortcut("scene-select-primary", "Primary Select", QKeySequence(Qt::MiddleButton));
declareShortcut(
"scene-select-secondary", "Secondary Select", QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton));
declareShortcut(
"scene-select-tertiary", "Tertiary Select", QKeySequence(Qt::ShiftModifier | (int)Qt::MiddleButton));
declareModifier("scene-speed-modifier", "Speed Modifier", Qt::Key_Shift);
declareShortcut("scene-delete", "Delete Instance", QKeySequence(Qt::Key_Delete));
declareShortcut("scene-instance-drop-terrain", "Drop to terrain level", QKeySequence(Qt::Key_G));
declareShortcut("scene-instance-drop-collision", "Drop to collision", QKeySequence(Qt::Key_H));
declareShortcut("scene-instance-drop-terrain-separately", "Drop to terrain level separately", QKeySequence());
declareShortcut("scene-instance-drop-collision-separately", "Drop to collision separately", QKeySequence());
declareShortcut("scene-load-cam-cell", "Load Camera Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_5));
declareShortcut("scene-load-cam-eastcell", "Load East Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_6));
declareShortcut("scene-load-cam-northcell", "Load North Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_8));
declareShortcut("scene-load-cam-westcell", "Load West Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_4));
declareShortcut("scene-load-cam-southcell", "Load South Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_2));
declareShortcut("scene-edit-abort", "Abort", QKeySequence(Qt::Key_Escape));
declareShortcut("scene-focus-toolbar", "Toggle Toolbar Focus", QKeySequence(Qt::Key_T));
declareShortcut("scene-render-stats", "Debug Rendering Stats", QKeySequence(Qt::Key_F3));
declareShortcut(mValues->mKeyBindings.mSceneNaviPrimary, "Camera Rotation From Mouse Movement");
declareShortcut(mValues->mKeyBindings.mSceneNaviSecondary, "Camera Translation From Mouse Movement");
declareShortcut(mValues->mKeyBindings.mSceneOpenPrimary, "Primary Open");
declareShortcut(mValues->mKeyBindings.mSceneEditPrimary, "Primary Edit");
declareShortcut(mValues->mKeyBindings.mSceneEditSecondary, "Secondary Edit");
declareShortcut(mValues->mKeyBindings.mSceneSelectPrimary, "Primary Select");
declareShortcut(mValues->mKeyBindings.mSceneSelectSecondary, "Secondary Select");
declareShortcut(mValues->mKeyBindings.mSceneSelectTertiary, "Tertiary Select");
declareModifier(mValues->mKeyBindings.mSceneSpeedModifier, "Speed Modifier");
declareShortcut(mValues->mKeyBindings.mSceneDelete, "Delete Instance");
declareShortcut(mValues->mKeyBindings.mSceneInstanceDropTerrain, "Drop to terrain level");
declareShortcut(mValues->mKeyBindings.mSceneInstanceDropCollision, "Drop to collision");
declareShortcut(mValues->mKeyBindings.mSceneInstanceDropTerrainSeparately, "Drop to terrain level separately");
declareShortcut(mValues->mKeyBindings.mSceneInstanceDropCollisionSeparately, "Drop to collision separately");
declareShortcut(mValues->mKeyBindings.mSceneLoadCamCell, "Load Camera Cell");
declareShortcut(mValues->mKeyBindings.mSceneLoadCamEastcell, "Load East Cell");
declareShortcut(mValues->mKeyBindings.mSceneLoadCamNorthcell, "Load North Cell");
declareShortcut(mValues->mKeyBindings.mSceneLoadCamWestcell, "Load West Cell");
declareShortcut(mValues->mKeyBindings.mSceneLoadCamSouthcell, "Load South Cell");
declareShortcut(mValues->mKeyBindings.mSceneEditAbort, "Abort");
declareShortcut(mValues->mKeyBindings.mSceneFocusToolbar, "Toggle Toolbar Focus");
declareShortcut(mValues->mKeyBindings.mSceneRenderStats, "Debug Rendering Stats");
declareShortcut(mValues->mKeyBindings.mSceneDuplicate, "Duplicate Instance");
declareShortcut(mValues->mKeyBindings.mSceneClearSelection, "Clear Selection");
declareShortcut(mValues->mKeyBindings.mSceneUnhideAll, "Unhide All Objects");
declareShortcut(mValues->mKeyBindings.mSceneToggleVisibility, "Toggle Selection Visibility");
declareShortcut(mValues->mKeyBindings.mSceneGroup0, "Selection Group 0");
declareShortcut(mValues->mKeyBindings.mSceneSave0, "Save Group 0");
declareShortcut(mValues->mKeyBindings.mSceneGroup1, "Select Group 1");
declareShortcut(mValues->mKeyBindings.mSceneSave1, "Save Group 1");
declareShortcut(mValues->mKeyBindings.mSceneGroup2, "Select Group 2");
declareShortcut(mValues->mKeyBindings.mSceneSave2, "Save Group 2");
declareShortcut(mValues->mKeyBindings.mSceneGroup3, "Select Group 3");
declareShortcut(mValues->mKeyBindings.mSceneSave3, "Save Group 3");
declareShortcut(mValues->mKeyBindings.mSceneGroup4, "Select Group 4");
declareShortcut(mValues->mKeyBindings.mSceneSave4, "Save Group 4");
declareShortcut(mValues->mKeyBindings.mSceneGroup5, "Selection Group 5");
declareShortcut(mValues->mKeyBindings.mSceneSave5, "Save Group 5");
declareShortcut(mValues->mKeyBindings.mSceneGroup6, "Selection Group 6");
declareShortcut(mValues->mKeyBindings.mSceneSave6, "Save Group 6");
declareShortcut(mValues->mKeyBindings.mSceneGroup7, "Selection Group 7");
declareShortcut(mValues->mKeyBindings.mSceneSave7, "Save Group 7");
declareShortcut(mValues->mKeyBindings.mSceneGroup8, "Selection Group 8");
declareShortcut(mValues->mKeyBindings.mSceneSave8, "Save Group 8");
declareShortcut(mValues->mKeyBindings.mSceneGroup9, "Selection Group 9");
declareShortcut(mValues->mKeyBindings.mSceneSave9, "Save Group 9");
declareSubcategory("1st/Free Camera");
declareShortcut("free-forward", "Forward", QKeySequence(Qt::Key_W));
declareShortcut("free-backward", "Backward", QKeySequence(Qt::Key_S));
declareShortcut("free-left", "Left", QKeySequence(Qt::Key_A));
declareShortcut("free-right", "Right", QKeySequence(Qt::Key_D));
declareShortcut("free-roll-left", "Roll Left", QKeySequence(Qt::Key_Q));
declareShortcut("free-roll-right", "Roll Right", QKeySequence(Qt::Key_E));
declareShortcut("free-speed-mode", "Toggle Speed Mode", QKeySequence(Qt::Key_F));
declareShortcut(mValues->mKeyBindings.mFreeForward, "Forward");
declareShortcut(mValues->mKeyBindings.mFreeBackward, "Backward");
declareShortcut(mValues->mKeyBindings.mFreeLeft, "Left");
declareShortcut(mValues->mKeyBindings.mFreeRight, "Right");
declareShortcut(mValues->mKeyBindings.mFreeRollLeft, "Roll Left");
declareShortcut(mValues->mKeyBindings.mFreeRollRight, "Roll Right");
declareShortcut(mValues->mKeyBindings.mFreeSpeedMode, "Toggle Speed Mode");
declareSubcategory("Orbit Camera");
declareShortcut("orbit-up", "Up", QKeySequence(Qt::Key_W));
declareShortcut("orbit-down", "Down", QKeySequence(Qt::Key_S));
declareShortcut("orbit-left", "Left", QKeySequence(Qt::Key_A));
declareShortcut("orbit-right", "Right", QKeySequence(Qt::Key_D));
declareShortcut("orbit-roll-left", "Roll Left", QKeySequence(Qt::Key_Q));
declareShortcut("orbit-roll-right", "Roll Right", QKeySequence(Qt::Key_E));
declareShortcut("orbit-speed-mode", "Toggle Speed Mode", QKeySequence(Qt::Key_F));
declareShortcut("orbit-center-selection", "Center On Selected", QKeySequence(Qt::Key_C));
declareShortcut(mValues->mKeyBindings.mOrbitUp, "Up");
declareShortcut(mValues->mKeyBindings.mOrbitDown, "Down");
declareShortcut(mValues->mKeyBindings.mOrbitLeft, "Left");
declareShortcut(mValues->mKeyBindings.mOrbitRight, "Right");
declareShortcut(mValues->mKeyBindings.mOrbitRollLeft, "Roll Left");
declareShortcut(mValues->mKeyBindings.mOrbitRollRight, "Roll Right");
declareShortcut(mValues->mKeyBindings.mOrbitSpeedMode, "Toggle Speed Mode");
declareShortcut(mValues->mKeyBindings.mOrbitCenterSelection, "Center On Selected");
declareSubcategory("Script Editor");
declareShortcut("script-editor-comment", "Comment Selection", QKeySequence());
declareShortcut("script-editor-uncomment", "Uncomment Selection", QKeySequence());
declareShortcut(mValues->mKeyBindings.mScriptEditorComment, "Comment Selection");
declareShortcut(mValues->mKeyBindings.mScriptEditorUncomment, "Uncomment Selection");
declareCategory("Models");
declareString("baseanim", "base animations", "meshes/base_anim.nif")
.setTooltip("3rd person base model with textkeys-data");
declareString("baseanimkna", "base animations, kna", "meshes/base_animkna.nif")
declareString(mValues->mModels.mBaseanim, "base animations").setTooltip("3rd person base model with textkeys-data");
declareString(mValues->mModels.mBaseanimkna, "base animations, kna")
.setTooltip("3rd person beast race base model with textkeys-data");
declareString("baseanimfemale", "base animations, female", "meshes/base_anim_female.nif")
declareString(mValues->mModels.mBaseanimfemale, "base animations, female")
.setTooltip("3rd person female base model with textkeys-data");
declareString("wolfskin", "base animations, wolf", "meshes/wolf/skin.nif").setTooltip("3rd person werewolf skin");
declareString(mValues->mModels.mWolfskin, "base animations, wolf").setTooltip("3rd person werewolf skin");
}
void CSMPrefs::State::declareCategory(const std::string& key)
@ -463,71 +422,52 @@ void CSMPrefs::State::declareCategory(const std::string& key)
}
}
CSMPrefs::IntSetting& CSMPrefs::State::declareInt(const std::string& key, const std::string& label, int default_)
CSMPrefs::IntSetting& CSMPrefs::State::declareInt(Settings::SettingValue<int>& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
setDefault(key, std::to_string(default_));
default_ = Settings::Manager::getInt(key, mCurrentCategory->second.getKey());
CSMPrefs::IntSetting* setting = new CSMPrefs::IntSetting(&mCurrentCategory->second, &mMutex, key, label, default_);
CSMPrefs::IntSetting* setting
= new CSMPrefs::IntSetting(&mCurrentCategory->second, &mMutex, value.mName, label, *mIndex);
mCurrentCategory->second.addSetting(setting);
return *setting;
}
CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble(
const std::string& key, const std::string& label, double default_)
CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble(Settings::SettingValue<double>& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
std::ostringstream stream;
stream << default_;
setDefault(key, stream.str());
default_ = Settings::Manager::getFloat(key, mCurrentCategory->second.getKey());
CSMPrefs::DoubleSetting* setting
= new CSMPrefs::DoubleSetting(&mCurrentCategory->second, &mMutex, key, label, default_);
= new CSMPrefs::DoubleSetting(&mCurrentCategory->second, &mMutex, value.mName, label, *mIndex);
mCurrentCategory->second.addSetting(setting);
return *setting;
}
CSMPrefs::BoolSetting& CSMPrefs::State::declareBool(const std::string& key, const std::string& label, bool default_)
CSMPrefs::BoolSetting& CSMPrefs::State::declareBool(Settings::SettingValue<bool>& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
setDefault(key, default_ ? "true" : "false");
default_ = Settings::Manager::getBool(key, mCurrentCategory->second.getKey());
CSMPrefs::BoolSetting* setting
= new CSMPrefs::BoolSetting(&mCurrentCategory->second, &mMutex, key, label, default_);
= new CSMPrefs::BoolSetting(&mCurrentCategory->second, &mMutex, value.mName, label, *mIndex);
mCurrentCategory->second.addSetting(setting);
return *setting;
}
CSMPrefs::EnumSetting& CSMPrefs::State::declareEnum(
const std::string& key, const std::string& label, EnumValue default_)
CSMPrefs::EnumSetting& CSMPrefs::State::declareEnum(EnumSettingValue& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
setDefault(key, default_.mValue);
default_.mValue = Settings::Manager::getString(key, mCurrentCategory->second.getKey());
CSMPrefs::EnumSetting* setting
= new CSMPrefs::EnumSetting(&mCurrentCategory->second, &mMutex, key, label, default_);
CSMPrefs::EnumSetting* setting = new CSMPrefs::EnumSetting(
&mCurrentCategory->second, &mMutex, value.getValue().mName, label, value.getEnumValues(), *mIndex);
mCurrentCategory->second.addSetting(setting);
@ -535,18 +475,13 @@ CSMPrefs::EnumSetting& CSMPrefs::State::declareEnum(
}
CSMPrefs::ColourSetting& CSMPrefs::State::declareColour(
const std::string& key, const std::string& label, QColor default_)
Settings::SettingValue<std::string>& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
setDefault(key, default_.name().toUtf8().data());
default_.setNamedColor(
QString::fromUtf8(Settings::Manager::getString(key, mCurrentCategory->second.getKey()).c_str()));
CSMPrefs::ColourSetting* setting
= new CSMPrefs::ColourSetting(&mCurrentCategory->second, &mMutex, key, label, default_);
= new CSMPrefs::ColourSetting(&mCurrentCategory->second, &mMutex, value.mName, label, *mIndex);
mCurrentCategory->second.addSetting(setting);
@ -554,39 +489,32 @@ CSMPrefs::ColourSetting& CSMPrefs::State::declareColour(
}
CSMPrefs::ShortcutSetting& CSMPrefs::State::declareShortcut(
const std::string& key, const std::string& label, const QKeySequence& default_)
Settings::SettingValue<std::string>& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
std::string seqStr = getShortcutManager().convertToString(default_);
setDefault(key, seqStr);
// Setup with actual data
QKeySequence sequence;
getShortcutManager().convertFromString(
Settings::Manager::getString(key, mCurrentCategory->second.getKey()), sequence);
getShortcutManager().setSequence(key, sequence);
getShortcutManager().convertFromString(value, sequence);
getShortcutManager().setSequence(value.mName, sequence);
CSMPrefs::ShortcutSetting* setting = new CSMPrefs::ShortcutSetting(&mCurrentCategory->second, &mMutex, key, label);
CSMPrefs::ShortcutSetting* setting
= new CSMPrefs::ShortcutSetting(&mCurrentCategory->second, &mMutex, value.mName, label, *mIndex);
mCurrentCategory->second.addSetting(setting);
return *setting;
}
CSMPrefs::StringSetting& CSMPrefs::State::declareString(
const std::string& key, const std::string& label, std::string default_)
Settings::SettingValue<std::string>& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
setDefault(key, default_);
default_ = Settings::Manager::getString(key, mCurrentCategory->second.getKey());
CSMPrefs::StringSetting* setting
= new CSMPrefs::StringSetting(&mCurrentCategory->second, &mMutex, key, label, default_);
= new CSMPrefs::StringSetting(&mCurrentCategory->second, &mMutex, value.mName, label, *mIndex);
mCurrentCategory->second.addSetting(setting);
@ -594,55 +522,31 @@ CSMPrefs::StringSetting& CSMPrefs::State::declareString(
}
CSMPrefs::ModifierSetting& CSMPrefs::State::declareModifier(
const std::string& key, const std::string& label, int default_)
Settings::SettingValue<std::string>& value, const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
std::string modStr = getShortcutManager().convertToString(default_);
setDefault(key, modStr);
// Setup with actual data
int modifier;
getShortcutManager().convertFromString(
Settings::Manager::getString(key, mCurrentCategory->second.getKey()), modifier);
getShortcutManager().setModifier(key, modifier);
getShortcutManager().convertFromString(value.get(), modifier);
getShortcutManager().setModifier(value.mName, modifier);
CSMPrefs::ModifierSetting* setting = new CSMPrefs::ModifierSetting(&mCurrentCategory->second, &mMutex, key, label);
CSMPrefs::ModifierSetting* setting
= new CSMPrefs::ModifierSetting(&mCurrentCategory->second, &mMutex, value.mName, label, *mIndex);
mCurrentCategory->second.addSetting(setting);
return *setting;
}
void CSMPrefs::State::declareSeparator()
void CSMPrefs::State::declareSubcategory(const QString& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
CSMPrefs::Setting* setting = new CSMPrefs::Setting(&mCurrentCategory->second, &mMutex, "", "");
mCurrentCategory->second.addSetting(setting);
}
void CSMPrefs::State::declareSubcategory(const std::string& label)
{
if (mCurrentCategory == mCategories.end())
throw std::logic_error("no category for setting");
CSMPrefs::Setting* setting = new CSMPrefs::Setting(&mCurrentCategory->second, &mMutex, "", label);
mCurrentCategory->second.addSetting(setting);
}
void CSMPrefs::State::setDefault(const std::string& key, const std::string& default_)
{
Settings::CategorySetting fullKey(mCurrentCategory->second.getKey(), key);
Settings::CategorySettingValueMap::iterator iter = Settings::Manager::mDefaultSettings.find(fullKey);
if (iter == Settings::Manager::mDefaultSettings.end())
Settings::Manager::mDefaultSettings.insert(std::make_pair(fullKey, default_));
mCurrentCategory->second.addSubcategory(
new CSMPrefs::Subcategory(&mCurrentCategory->second, &mMutex, label, *mIndex));
}
CSMPrefs::State::State(const Files::ConfigurationManager& configurationManager)
@ -650,6 +554,8 @@ CSMPrefs::State::State(const Files::ConfigurationManager& configurationManager)
, mDefaultConfigFile("defaults-cs.bin")
, mConfigurationManager(configurationManager)
, mCurrentCategory(mCategories.end())
, mIndex(std::make_unique<Settings::Index>())
, mValues(std::make_unique<Values>(*mIndex))
{
if (sThis)
throw std::logic_error("An instance of CSMPRefs::State already exists");
@ -709,27 +615,13 @@ CSMPrefs::State& CSMPrefs::State::get()
void CSMPrefs::State::resetCategory(const std::string& category)
{
for (Settings::CategorySettingValueMap::iterator i = Settings::Manager::mUserSettings.begin();
i != Settings::Manager::mUserSettings.end(); ++i)
{
// if the category matches
if (i->first.first == category)
{
// mark the setting as changed
Settings::Manager::mChangedSettings.insert(std::make_pair(i->first.first, i->first.second));
// reset the value to the default
i->second = Settings::Manager::mDefaultSettings[i->first];
}
}
Collection::iterator container = mCategories.find(category);
if (container != mCategories.end())
{
Category settings = container->second;
for (Category::Iterator i = settings.begin(); i != settings.end(); ++i)
for (Setting* setting : container->second)
{
(*i)->updateWidget();
update(**i);
setting->reset();
update(*setting);
}
}
}

View File

@ -17,6 +17,11 @@
class QColor;
namespace Settings
{
class Index;
}
namespace CSMPrefs
{
class IntSetting;
@ -27,6 +32,8 @@ namespace CSMPrefs
class ModifierSetting;
class Setting;
class StringSetting;
class EnumSettingValue;
struct Values;
/// \brief User settings state
///
@ -50,43 +57,40 @@ namespace CSMPrefs
Collection mCategories;
Iterator mCurrentCategory;
QMutex mMutex;
std::unique_ptr<Settings::Index> mIndex;
std::unique_ptr<Values> mValues;
// not implemented
State(const State&);
State& operator=(const State&);
private:
void declare();
void declareCategory(const std::string& key);
IntSetting& declareInt(const std::string& key, const std::string& label, int default_);
DoubleSetting& declareDouble(const std::string& key, const std::string& label, double default_);
IntSetting& declareInt(Settings::SettingValue<int>& value, const QString& label);
BoolSetting& declareBool(const std::string& key, const std::string& label, bool default_);
DoubleSetting& declareDouble(Settings::SettingValue<double>& value, const QString& label);
EnumSetting& declareEnum(const std::string& key, const std::string& label, EnumValue default_);
BoolSetting& declareBool(Settings::SettingValue<bool>& value, const QString& label);
ColourSetting& declareColour(const std::string& key, const std::string& label, QColor default_);
EnumSetting& declareEnum(EnumSettingValue& value, const QString& label);
ShortcutSetting& declareShortcut(
const std::string& key, const std::string& label, const QKeySequence& default_);
ColourSetting& declareColour(Settings::SettingValue<std::string>& value, const QString& label);
StringSetting& declareString(const std::string& key, const std::string& label, std::string default_);
ShortcutSetting& declareShortcut(Settings::SettingValue<std::string>& value, const QString& label);
ModifierSetting& declareModifier(const std::string& key, const std::string& label, int modifier_);
StringSetting& declareString(Settings::SettingValue<std::string>& value, const QString& label);
void declareSeparator();
ModifierSetting& declareModifier(Settings::SettingValue<std::string>& value, const QString& label);
void declareSubcategory(const std::string& label);
void setDefault(const std::string& key, const std::string& default_);
void declareSubcategory(const QString& label);
public:
State(const Files::ConfigurationManager& configurationManager);
State(const State&) = delete;
~State();
State& operator=(const State&) = delete;
void save();
Iterator begin();

View File

@ -12,9 +12,8 @@
#include "state.hpp"
CSMPrefs::StringSetting::StringSetting(
Category* parent, QMutex* mutex, const std::string& key, const std::string& label, std::string_view default_)
: Setting(parent, mutex, key, label)
, mDefault(default_)
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index)
: TypedSetting(parent, mutex, key, label, index)
, mWidget(nullptr)
{
}
@ -25,9 +24,9 @@ CSMPrefs::StringSetting& CSMPrefs::StringSetting::setTooltip(const std::string&
return *this;
}
std::pair<QWidget*, QWidget*> CSMPrefs::StringSetting::makeWidgets(QWidget* parent)
CSMPrefs::SettingWidgets CSMPrefs::StringSetting::makeWidgets(QWidget* parent)
{
mWidget = new QLineEdit(QString::fromUtf8(mDefault.c_str()), parent);
mWidget = new QLineEdit(QString::fromStdString(getValue()), parent);
if (!mTooltip.empty())
{
@ -37,23 +36,17 @@ std::pair<QWidget*, QWidget*> CSMPrefs::StringSetting::makeWidgets(QWidget* pare
connect(mWidget, &QLineEdit::textChanged, this, &StringSetting::textChanged);
return std::make_pair(static_cast<QWidget*>(nullptr), mWidget);
return SettingWidgets{ .mLabel = nullptr, .mInput = mWidget };
}
void CSMPrefs::StringSetting::updateWidget()
{
if (mWidget)
{
mWidget->setText(QString::fromStdString(Settings::Manager::getString(getKey(), getParent()->getKey())));
}
mWidget->setText(QString::fromStdString(getValue()));
}
void CSMPrefs::StringSetting::textChanged(const QString& text)
{
{
QMutexLocker lock(getMutex());
Settings::Manager::setString(getKey(), getParent()->getKey(), text.toStdString());
}
setValue(text.toStdString());
getParent()->getState()->update(*this);
}

View File

@ -14,22 +14,22 @@ class QWidget;
namespace CSMPrefs
{
class Category;
class StringSetting : public Setting
class StringSetting final : public TypedSetting<std::string>
{
Q_OBJECT
std::string mTooltip;
std::string mDefault;
QLineEdit* mWidget;
public:
StringSetting(Category* parent, QMutex* mutex, const std::string& key, const std::string& label,
std::string_view default_);
explicit StringSetting(
Category* parent, QMutex* mutex, std::string_view key, const QString& label, Settings::Index& index);
StringSetting& setTooltip(const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent) override;
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override;

View File

@ -0,0 +1,18 @@
#include "subcategory.hpp"
#include <QGridLayout>
namespace CSMPrefs
{
class Category;
Subcategory::Subcategory(Category* parent, QMutex* mutex, const QString& label, Settings::Index& index)
: Setting(parent, mutex, "", label, index)
{
}
SettingWidgets Subcategory::makeWidgets(QWidget* /*parent*/)
{
return SettingWidgets{ .mLabel = nullptr, .mInput = nullptr };
}
}

View File

@ -0,0 +1,28 @@
#ifndef OPENMW_APPS_OPENCS_MODEL_PREFS_SUBCATEGORY_H
#define OPENMW_APPS_OPENCS_MODEL_PREFS_SUBCATEGORY_H
#include "setting.hpp"
#include <string>
#include <utility>
namespace CSMPrefs
{
class Category;
class Subcategory final : public Setting
{
Q_OBJECT
public:
explicit Subcategory(Category* parent, QMutex* mutex, const QString& label, Settings::Index& index);
SettingWidgets makeWidgets(QWidget* parent) override;
void updateWidget() override {}
void reset() override {}
};
}
#endif

View File

@ -0,0 +1,546 @@
#ifndef OPENMW_APPS_OPENCS_MODEL_PREFS_VALUES_H
#define OPENMW_APPS_OPENCS_MODEL_PREFS_VALUES_H
#include "enumvalueview.hpp"
#include <components/settings/sanitizer.hpp>
#include <components/settings/settingvalue.hpp>
#include <Qt>
#include <QtGlobal>
#include <array>
#include <memory>
#include <span>
#include <string>
#include <string_view>
namespace CSMPrefs
{
class EnumSanitizer final : public Settings::Sanitizer<std::string>
{
public:
explicit EnumSanitizer(std::span<const EnumValueView> values)
: mValues(values)
{
}
std::string apply(const std::string& value) const override
{
const auto hasValue = [&](const EnumValueView& v) { return v.mValue == value; };
if (std::find_if(mValues.begin(), mValues.end(), hasValue) == mValues.end())
{
std::ostringstream message;
message << "Invalid enum value: " << value;
throw std::runtime_error(message.str());
}
return value;
}
private:
std::span<const EnumValueView> mValues;
};
inline std::unique_ptr<Settings::Sanitizer<std::string>> makeEnumSanitizerString(
std::span<const EnumValueView> values)
{
return std::make_unique<EnumSanitizer>(values);
}
class EnumSettingValue
{
public:
explicit EnumSettingValue(Settings::Index& index, std::string_view category, std::string_view name,
std::span<const EnumValueView> values, std::size_t defaultValueIndex)
: mValue(
index, category, name, std::string(values[defaultValueIndex].mValue), makeEnumSanitizerString(values))
, mEnumValues(values)
{
}
Settings::SettingValue<std::string>& getValue() { return mValue; }
std::span<const EnumValueView> getEnumValues() const { return mEnumValues; }
private:
Settings::SettingValue<std::string> mValue;
std::span<const EnumValueView> mEnumValues;
};
struct WindowsCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Windows";
static constexpr std::array<EnumValueView, 3> sMainwindowScrollbarValues{
EnumValueView{
"Scrollbar Only", "Simple addition of scrollbars, the view window does not grow automatically." },
EnumValueView{ "Grow Only", "The view window grows as subviews are added. No scrollbars." },
EnumValueView{
"Grow then Scroll", "The view window grows. The scrollbar appears once it cannot grow any further." },
};
Settings::SettingValue<int> mDefaultWidth{ mIndex, sName, "default-width", 800 };
Settings::SettingValue<int> mDefaultHeight{ mIndex, sName, "default-height", 600 };
Settings::SettingValue<bool> mShowStatusbar{ mIndex, sName, "show-statusbar", true };
Settings::SettingValue<bool> mReuse{ mIndex, sName, "reuse", true };
Settings::SettingValue<int> mMaxSubviews{ mIndex, sName, "max-subviews", 256 };
Settings::SettingValue<bool> mHideSubview{ mIndex, sName, "hide-subview", false };
Settings::SettingValue<int> mMinimumWidth{ mIndex, sName, "minimum-width", 325 };
EnumSettingValue mMainwindowScrollbar{ mIndex, sName, "mainwindow-scrollbar", sMainwindowScrollbarValues, 0 };
Settings::SettingValue<bool> mGrowLimit{ mIndex, sName, "grow-limit", false };
};
struct RecordsCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Records";
static constexpr std::array<EnumValueView, 3> sRecordValues{
EnumValueView{ "Icon and Text", "" },
EnumValueView{ "Icon Only", "" },
EnumValueView{ "Text Only", "" },
};
EnumSettingValue mStatusFormat{ mIndex, sName, "status-format", sRecordValues, 0 };
EnumSettingValue mTypeFormat{ mIndex, sName, "type-format", sRecordValues, 0 };
};
struct IdTablesCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "ID Tables";
static constexpr std::array<EnumValueView, 7> sDoubleClickValues{
EnumValueView{ "Edit in Place", "Edit the clicked cell" },
EnumValueView{ "Edit Record", "Open a dialogue subview for the clicked record" },
EnumValueView{ "View", "Open a scene subview for the clicked record (not available everywhere)" },
EnumValueView{ "Revert", "" },
EnumValueView{ "Delete", "" },
EnumValueView{ "Edit Record and Close", "" },
EnumValueView{
"View and Close", "Open a scene subview for the clicked record and close the table subview" },
};
static constexpr std::array<EnumValueView, 3> sJumpAndSelectValues{
EnumValueView{ "Jump and Select", "Scroll new record into view and make it the selection" },
EnumValueView{ "Jump Only", "Scroll new record into view" },
EnumValueView{ "No Jump", "No special action" },
};
EnumSettingValue mDouble{ mIndex, sName, "double", sDoubleClickValues, 0 };
EnumSettingValue mDoubleS{ mIndex, sName, "double-s", sDoubleClickValues, 1 };
EnumSettingValue mDoubleC{ mIndex, sName, "double-c", sDoubleClickValues, 2 };
EnumSettingValue mDoubleSc{ mIndex, sName, "double-sc", sDoubleClickValues, 5 };
EnumSettingValue mJumpToAdded{ mIndex, sName, "jump-to-added", sJumpAndSelectValues, 0 };
Settings::SettingValue<bool> mExtendedConfig{ mIndex, sName, "extended-config", false };
Settings::SettingValue<bool> mSubviewNewWindow{ mIndex, sName, "subview-new-window", false };
};
struct IdDialoguesCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "ID Dialogues";
Settings::SettingValue<bool> mToolbar{ mIndex, sName, "toolbar", true };
};
struct ReportsCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Reports";
static constexpr std::array<EnumValueView, 4> sReportValues{
EnumValueView{ "None", "" },
EnumValueView{ "Edit", "Open a table or dialogue suitable for addressing the listed report" },
EnumValueView{ "Remove", "Remove the report from the report table" },
EnumValueView{ "Edit And Remove",
"Open a table or dialogue suitable for addressing the listed report, then remove the report from the "
"report table" },
};
EnumSettingValue mDouble{ mIndex, sName, "double", sReportValues, 1 };
EnumSettingValue mDoubleS{ mIndex, sName, "double-s", sReportValues, 2 };
EnumSettingValue mDoubleC{ mIndex, sName, "double-c", sReportValues, 3 };
EnumSettingValue mDoubleSc{ mIndex, sName, "double-sc", sReportValues, 0 };
Settings::SettingValue<bool> mIgnoreBaseRecords{ mIndex, sName, "ignore-base-records", false };
};
struct SearchAndReplaceCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Search & Replace";
Settings::SettingValue<int> mCharBefore{ mIndex, sName, "char-before", 10 };
Settings::SettingValue<int> mCharAfter{ mIndex, sName, "char-after", 10 };
Settings::SettingValue<bool> mAutoDelete{ mIndex, sName, "auto-delete", true };
};
struct ScriptsCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Scripts";
static constexpr std::array<EnumValueView, 3> sWarningValues{
EnumValueView{ "Ignore", "Do not report warning" },
EnumValueView{ "Normal", "Report warnings as warning" },
EnumValueView{ "Strict", "Promote warning to an error" },
};
Settings::SettingValue<bool> mShowLinenum{ mIndex, sName, "show-linenum", true };
Settings::SettingValue<bool> mWrapLines{ mIndex, sName, "wrap-lines", false };
Settings::SettingValue<bool> mMonoFont{ mIndex, sName, "mono-font", true };
Settings::SettingValue<int> mTabWidth{ mIndex, sName, "tab-width", 4 };
EnumSettingValue mWarnings{ mIndex, sName, "warnings", sWarningValues, 1 };
Settings::SettingValue<bool> mToolbar{ mIndex, sName, "toolbar", true };
Settings::SettingValue<int> mCompileDelay{ mIndex, sName, "compile-delay", 100 };
Settings::SettingValue<int> mErrorHeight{ mIndex, sName, "error-height", 100 };
Settings::SettingValue<bool> mHighlightOccurrences{ mIndex, sName, "highlight-occurrences", true };
Settings::SettingValue<std::string> mColourHighlight{ mIndex, sName, "colour-highlight", "lightcyan" };
Settings::SettingValue<std::string> mColourInt{ mIndex, sName, "colour-int", "darkmagenta" };
Settings::SettingValue<std::string> mColourFloat{ mIndex, sName, "colour-float", "magenta" };
Settings::SettingValue<std::string> mColourName{ mIndex, sName, "colour-name", "grey" };
Settings::SettingValue<std::string> mColourKeyword{ mIndex, sName, "colour-keyword", "red" };
Settings::SettingValue<std::string> mColourSpecial{ mIndex, sName, "colour-special", "darkorange" };
Settings::SettingValue<std::string> mColourComment{ mIndex, sName, "colour-comment", "green" };
Settings::SettingValue<std::string> mColourId{ mIndex, sName, "colour-id", "blue" };
};
struct GeneralInputCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "General Input";
Settings::SettingValue<bool> mCycle{ mIndex, sName, "cycle", false };
};
struct SceneInputCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "3D Scene Input";
Settings::SettingValue<double> mNaviWheelFactor{ mIndex, sName, "navi-wheel-factor", 8 };
Settings::SettingValue<double> mSNaviSensitivity{ mIndex, sName, "s-navi-sensitivity", 50 };
Settings::SettingValue<double> mPNaviFreeSensitivity{ mIndex, sName, "p-navi-free-sensitivity", 1 / 650.0 };
Settings::SettingValue<bool> mPNaviFreeInvert{ mIndex, sName, "p-navi-free-invert", false };
Settings::SettingValue<double> mNaviFreeLinSpeed{ mIndex, sName, "navi-free-lin-speed", 1000 };
Settings::SettingValue<double> mNaviFreeRotSpeed{ mIndex, sName, "navi-free-rot-speed", 3.14 / 2 };
Settings::SettingValue<double> mNaviFreeSpeedMult{ mIndex, sName, "navi-free-speed-mult", 8 };
Settings::SettingValue<double> mPNaviOrbitSensitivity{ mIndex, sName, "p-navi-orbit-sensitivity", 1 / 650.0 };
Settings::SettingValue<bool> mPNaviOrbitInvert{ mIndex, sName, "p-navi-orbit-invert", false };
Settings::SettingValue<double> mNaviOrbitRotSpeed{ mIndex, sName, "navi-orbit-rot-speed", 3.14 / 4 };
Settings::SettingValue<double> mNaviOrbitSpeedMult{ mIndex, sName, "navi-orbit-speed-mult", 4 };
Settings::SettingValue<bool> mNaviOrbitConstRoll{ mIndex, sName, "navi-orbit-const-roll", true };
Settings::SettingValue<bool> mContextSelect{ mIndex, sName, "context-select", false };
Settings::SettingValue<double> mDragFactor{ mIndex, sName, "drag-factor", 1 };
Settings::SettingValue<double> mDragWheelFactor{ mIndex, sName, "drag-wheel-factor", 1 };
Settings::SettingValue<double> mDragShiftFactor{ mIndex, sName, "drag-shift-factor", 4 };
Settings::SettingValue<double> mRotateFactor{ mIndex, sName, "rotate-factor", 0.007 };
};
struct RenderingCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Rendering";
Settings::SettingValue<int> mFramerateLimit{ mIndex, sName, "framerate-limit", 60 };
Settings::SettingValue<int> mCameraFov{ mIndex, sName, "camera-fov", 90 };
Settings::SettingValue<bool> mCameraOrtho{ mIndex, sName, "camera-ortho", false };
Settings::SettingValue<int> mCameraOrthoSize{ mIndex, sName, "camera-ortho-size", 100 };
Settings::SettingValue<double> mObjectMarkerAlpha{ mIndex, sName, "object-marker-alpha", 0.5 };
Settings::SettingValue<bool> mSceneUseGradient{ mIndex, sName, "scene-use-gradient", true };
Settings::SettingValue<std::string> mSceneDayBackgroundColour{ mIndex, sName, "scene-day-background-colour",
"#6e7880" };
Settings::SettingValue<std::string> mSceneDayGradientColour{ mIndex, sName, "scene-day-gradient-colour",
"#2f3333" };
Settings::SettingValue<std::string> mSceneBrightBackgroundColour{ mIndex, sName,
"scene-bright-background-colour", "#4f575c" };
Settings::SettingValue<std::string> mSceneBrightGradientColour{ mIndex, sName, "scene-bright-gradient-colour",
"#2f3333" };
Settings::SettingValue<std::string> mSceneNightBackgroundColour{ mIndex, sName, "scene-night-background-colour",
"#404d4f" };
Settings::SettingValue<std::string> mSceneNightGradientColour{ mIndex, sName, "scene-night-gradient-colour",
"#2f3333" };
Settings::SettingValue<bool> mSceneDayNightSwitchNodes{ mIndex, sName, "scene-day-night-switch-nodes", true };
};
struct TooltipsCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Tooltips";
Settings::SettingValue<bool> mScene{ mIndex, sName, "scene", true };
Settings::SettingValue<bool> mSceneHideBasic{ mIndex, sName, "scene-hide-basic", false };
Settings::SettingValue<int> mSceneDelay{ mIndex, sName, "scene-delay", 500 };
};
struct SceneEditingCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "3D Scene Editing";
static constexpr std::array<EnumValueView, 3> sInsertOutsideCellValues{
EnumValueView{ "Create cell and insert", "" },
EnumValueView{ "Discard", "" },
EnumValueView{ "Insert anyway", "" },
};
static constexpr std::array<EnumValueView, 3> sInsertOutsideVisibleCellValues{
EnumValueView{ "Show cell and insert", "" },
EnumValueView{ "Discard", "" },
EnumValueView{ "Insert anyway", "" },
};
static constexpr std::array<EnumValueView, 2> sLandEditOutsideCellValues{
EnumValueView{ "Create cell and land, then edit", "" },
EnumValueView{ "Discard", "" },
};
static constexpr std::array<EnumValueView, 2> sLandEditOutsideVisibleCellValues{
EnumValueView{ "Show cell and edit", "" },
EnumValueView{ "Discard", "" },
};
static constexpr std::array<EnumValueView, 4> sSelectAction{
EnumValueView{ "Select only", "" },
EnumValueView{ "Add to selection", "" },
EnumValueView{ "Remove from selection", "" },
EnumValueView{ "Invert selection", "" },
};
Settings::SettingValue<double> mGridsnapMovement{ mIndex, sName, "gridsnap-movement", 16 };
Settings::SettingValue<double> mGridsnapRotation{ mIndex, sName, "gridsnap-rotation", 15 };
Settings::SettingValue<double> mGridsnapScale{ mIndex, sName, "gridsnap-scale", 0.25 };
Settings::SettingValue<int> mDistance{ mIndex, sName, "distance", 50 };
EnumSettingValue mOutsideDrop{ mIndex, sName, "outside-drop", sInsertOutsideCellValues, 0 };
EnumSettingValue mOutsideVisibleDrop{ mIndex, sName, "outside-visible-drop", sInsertOutsideVisibleCellValues,
0 };
EnumSettingValue mOutsideLandedit{ mIndex, sName, "outside-landedit", sLandEditOutsideCellValues, 0 };
EnumSettingValue mOutsideVisibleLandedit{ mIndex, sName, "outside-visible-landedit",
sLandEditOutsideVisibleCellValues, 0 };
Settings::SettingValue<int> mTexturebrushMaximumsize{ mIndex, sName, "texturebrush-maximumsize", 50 };
Settings::SettingValue<int> mShapebrushMaximumsize{ mIndex, sName, "shapebrush-maximumsize", 100 };
Settings::SettingValue<bool> mLandeditPostSmoothpainting{ mIndex, sName, "landedit-post-smoothpainting",
false };
Settings::SettingValue<double> mLandeditPostSmoothstrength{ mIndex, sName, "landedit-post-smoothstrength",
0.25 };
Settings::SettingValue<bool> mOpenListView{ mIndex, sName, "open-list-view", false };
EnumSettingValue mPrimarySelectAction{ mIndex, sName, "primary-select-action", sSelectAction, 0 };
EnumSettingValue mSecondarySelectAction{ mIndex, sName, "secondary-select-action", sSelectAction, 1 };
};
struct KeyBindingsCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Key Bindings";
Settings::SettingValue<std::string> mDocumentFileNewgame{ mIndex, sName, "document-file-newgame", "Ctrl+N" };
Settings::SettingValue<std::string> mDocumentFileNewaddon{ mIndex, sName, "document-file-newaddon", "" };
Settings::SettingValue<std::string> mDocumentFileOpen{ mIndex, sName, "document-file-open", "Ctrl+O" };
Settings::SettingValue<std::string> mDocumentFileSave{ mIndex, sName, "document-file-save", "Ctrl+S" };
Settings::SettingValue<std::string> mDocumentHelpHelp{ mIndex, sName, "document-help-help", "F1" };
Settings::SettingValue<std::string> mDocumentHelpTutorial{ mIndex, sName, "document-help-tutorial", "" };
Settings::SettingValue<std::string> mDocumentFileVerify{ mIndex, sName, "document-file-verify", "" };
Settings::SettingValue<std::string> mDocumentFileMerge{ mIndex, sName, "document-file-merge", "" };
Settings::SettingValue<std::string> mDocumentFileErrorlog{ mIndex, sName, "document-file-errorlog", "" };
Settings::SettingValue<std::string> mDocumentFileMetadata{ mIndex, sName, "document-file-metadata", "" };
Settings::SettingValue<std::string> mDocumentFileClose{ mIndex, sName, "document-file-close", "Ctrl+W" };
Settings::SettingValue<std::string> mDocumentFileExit{ mIndex, sName, "document-file-exit", "Ctrl+Q" };
Settings::SettingValue<std::string> mDocumentEditUndo{ mIndex, sName, "document-edit-undo", "Ctrl+Z" };
Settings::SettingValue<std::string> mDocumentEditRedo{ mIndex, sName, "document-edit-redo", "Ctrl+Shift+Z" };
Settings::SettingValue<std::string> mDocumentEditPreferences{ mIndex, sName, "document-edit-preferences", "" };
Settings::SettingValue<std::string> mDocumentEditSearch{ mIndex, sName, "document-edit-search", "Ctrl+F" };
Settings::SettingValue<std::string> mDocumentViewNewview{ mIndex, sName, "document-view-newview", "" };
Settings::SettingValue<std::string> mDocumentViewStatusbar{ mIndex, sName, "document-view-statusbar", "" };
Settings::SettingValue<std::string> mDocumentViewFilters{ mIndex, sName, "document-view-filters", "" };
Settings::SettingValue<std::string> mDocumentWorldRegions{ mIndex, sName, "document-world-regions", "" };
Settings::SettingValue<std::string> mDocumentWorldCells{ mIndex, sName, "document-world-cells", "" };
Settings::SettingValue<std::string> mDocumentWorldReferencables{ mIndex, sName, "document-world-referencables",
"" };
Settings::SettingValue<std::string> mDocumentWorldReferences{ mIndex, sName, "document-world-references", "" };
Settings::SettingValue<std::string> mDocumentWorldLands{ mIndex, sName, "document-world-lands", "" };
Settings::SettingValue<std::string> mDocumentWorldLandtextures{ mIndex, sName, "document-world-landtextures",
"" };
Settings::SettingValue<std::string> mDocumentWorldPathgrid{ mIndex, sName, "document-world-pathgrid", "" };
Settings::SettingValue<std::string> mDocumentWorldRegionmap{ mIndex, sName, "document-world-regionmap", "" };
Settings::SettingValue<std::string> mDocumentMechanicsGlobals{ mIndex, sName, "document-mechanics-globals",
"" };
Settings::SettingValue<std::string> mDocumentMechanicsGamesettings{ mIndex, sName,
"document-mechanics-gamesettings", "" };
Settings::SettingValue<std::string> mDocumentMechanicsScripts{ mIndex, sName, "document-mechanics-scripts",
"" };
Settings::SettingValue<std::string> mDocumentMechanicsSpells{ mIndex, sName, "document-mechanics-spells", "" };
Settings::SettingValue<std::string> mDocumentMechanicsEnchantments{ mIndex, sName,
"document-mechanics-enchantments", "" };
Settings::SettingValue<std::string> mDocumentMechanicsMagiceffects{ mIndex, sName,
"document-mechanics-magiceffects", "" };
Settings::SettingValue<std::string> mDocumentMechanicsStartscripts{ mIndex, sName,
"document-mechanics-startscripts", "" };
Settings::SettingValue<std::string> mDocumentCharacterSkills{ mIndex, sName, "document-character-skills", "" };
Settings::SettingValue<std::string> mDocumentCharacterClasses{ mIndex, sName, "document-character-classes",
"" };
Settings::SettingValue<std::string> mDocumentCharacterFactions{ mIndex, sName, "document-character-factions",
"" };
Settings::SettingValue<std::string> mDocumentCharacterRaces{ mIndex, sName, "document-character-races", "" };
Settings::SettingValue<std::string> mDocumentCharacterBirthsigns{ mIndex, sName,
"document-character-birthsigns", "" };
Settings::SettingValue<std::string> mDocumentCharacterTopics{ mIndex, sName, "document-character-topics", "" };
Settings::SettingValue<std::string> mDocumentCharacterJournals{ mIndex, sName, "document-character-journals",
"" };
Settings::SettingValue<std::string> mDocumentCharacterTopicinfos{ mIndex, sName,
"document-character-topicinfos", "" };
Settings::SettingValue<std::string> mDocumentCharacterJournalinfos{ mIndex, sName,
"document-character-journalinfos", "" };
Settings::SettingValue<std::string> mDocumentCharacterBodyparts{ mIndex, sName, "document-character-bodyparts",
"" };
Settings::SettingValue<std::string> mDocumentAssetsReload{ mIndex, sName, "document-assets-reload", "F5" };
Settings::SettingValue<std::string> mDocumentAssetsSounds{ mIndex, sName, "document-assets-sounds", "" };
Settings::SettingValue<std::string> mDocumentAssetsSoundgens{ mIndex, sName, "document-assets-soundgens", "" };
Settings::SettingValue<std::string> mDocumentAssetsMeshes{ mIndex, sName, "document-assets-meshes", "" };
Settings::SettingValue<std::string> mDocumentAssetsIcons{ mIndex, sName, "document-assets-icons", "" };
Settings::SettingValue<std::string> mDocumentAssetsMusic{ mIndex, sName, "document-assets-music", "" };
Settings::SettingValue<std::string> mDocumentAssetsSoundres{ mIndex, sName, "document-assets-soundres", "" };
Settings::SettingValue<std::string> mDocumentAssetsTextures{ mIndex, sName, "document-assets-textures", "" };
Settings::SettingValue<std::string> mDocumentAssetsVideos{ mIndex, sName, "document-assets-videos", "" };
Settings::SettingValue<std::string> mDocumentDebugRun{ mIndex, sName, "document-debug-run", "" };
Settings::SettingValue<std::string> mDocumentDebugShutdown{ mIndex, sName, "document-debug-shutdown", "" };
Settings::SettingValue<std::string> mDocumentDebugProfiles{ mIndex, sName, "document-debug-profiles", "" };
Settings::SettingValue<std::string> mDocumentDebugRunlog{ mIndex, sName, "document-debug-runlog", "" };
Settings::SettingValue<std::string> mTableEdit{ mIndex, sName, "table-edit", "" };
Settings::SettingValue<std::string> mTableAdd{ mIndex, sName, "table-add", "Shift+A" };
Settings::SettingValue<std::string> mTableClone{ mIndex, sName, "table-clone", "Shift+D" };
Settings::SettingValue<std::string> mTouchRecord{ mIndex, sName, "touch-record", "" };
Settings::SettingValue<std::string> mTableRevert{ mIndex, sName, "table-revert", "" };
Settings::SettingValue<std::string> mTableRemove{ mIndex, sName, "table-remove", "Delete" };
Settings::SettingValue<std::string> mTableMoveup{ mIndex, sName, "table-moveup", "" };
Settings::SettingValue<std::string> mTableMovedown{ mIndex, sName, "table-movedown", "" };
Settings::SettingValue<std::string> mTableView{ mIndex, sName, "table-view", "Shift+C" };
Settings::SettingValue<std::string> mTablePreview{ mIndex, sName, "table-preview", "Shift+V" };
Settings::SettingValue<std::string> mTableExtendeddelete{ mIndex, sName, "table-extendeddelete", "" };
Settings::SettingValue<std::string> mTableExtendedrevert{ mIndex, sName, "table-extendedrevert", "" };
Settings::SettingValue<std::string> mReporttableShow{ mIndex, sName, "reporttable-show", "" };
Settings::SettingValue<std::string> mReporttableRemove{ mIndex, sName, "reporttable-remove", "Delete" };
Settings::SettingValue<std::string> mReporttableReplace{ mIndex, sName, "reporttable-replace", "" };
Settings::SettingValue<std::string> mReporttableRefresh{ mIndex, sName, "reporttable-refresh", "" };
Settings::SettingValue<std::string> mSceneNaviPrimary{ mIndex, sName, "scene-navi-primary", "LMB" };
Settings::SettingValue<std::string> mSceneNaviSecondary{ mIndex, sName, "scene-navi-secondary", "Ctrl+LMB" };
Settings::SettingValue<std::string> mSceneOpenPrimary{ mIndex, sName, "scene-open-primary", "Shift+LMB" };
Settings::SettingValue<std::string> mSceneEditPrimary{ mIndex, sName, "scene-edit-primary", "RMB" };
Settings::SettingValue<std::string> mSceneEditSecondary{ mIndex, sName, "scene-edit-secondary", "Ctrl+RMB" };
Settings::SettingValue<std::string> mSceneSelectPrimary{ mIndex, sName, "scene-select-primary", "MMB" };
Settings::SettingValue<std::string> mSceneSelectSecondary{ mIndex, sName, "scene-select-secondary",
"Ctrl+MMB" };
Settings::SettingValue<std::string> mSceneSelectTertiary{ mIndex, sName, "scene-select-tertiary", "Shift+MMB" };
Settings::SettingValue<std::string> mSceneSpeedModifier{ mIndex, sName, "scene-speed-modifier", "Shift" };
Settings::SettingValue<std::string> mSceneDelete{ mIndex, sName, "scene-delete", "Delete" };
Settings::SettingValue<std::string> mSceneInstanceDropTerrain{ mIndex, sName, "scene-instance-drop-terrain",
"G" };
Settings::SettingValue<std::string> mSceneInstanceDropCollision{ mIndex, sName, "scene-instance-drop-collision",
"H" };
Settings::SettingValue<std::string> mSceneInstanceDropTerrainSeparately{ mIndex, sName,
"scene-instance-drop-terrain-separately", "" };
Settings::SettingValue<std::string> mSceneInstanceDropCollisionSeparately{ mIndex, sName,
"scene-instance-drop-collision-separately", "" };
Settings::SettingValue<std::string> mSceneDuplicate{ mIndex, sName, "scene-duplicate", "Shift+C" };
Settings::SettingValue<std::string> mSceneLoadCamCell{ mIndex, sName, "scene-load-cam-cell", "Keypad+5" };
Settings::SettingValue<std::string> mSceneLoadCamEastcell{ mIndex, sName, "scene-load-cam-eastcell",
"Keypad+6" };
Settings::SettingValue<std::string> mSceneLoadCamNorthcell{ mIndex, sName, "scene-load-cam-northcell",
"Keypad+8" };
Settings::SettingValue<std::string> mSceneLoadCamWestcell{ mIndex, sName, "scene-load-cam-westcell",
"Keypad+4" };
Settings::SettingValue<std::string> mSceneLoadCamSouthcell{ mIndex, sName, "scene-load-cam-southcell",
"Keypad+2" };
Settings::SettingValue<std::string> mSceneEditAbort{ mIndex, sName, "scene-edit-abort", "Escape" };
Settings::SettingValue<std::string> mSceneFocusToolbar{ mIndex, sName, "scene-focus-toolbar", "T" };
Settings::SettingValue<std::string> mSceneRenderStats{ mIndex, sName, "scene-render-stats", "F3" };
Settings::SettingValue<std::string> mSceneClearSelection{ mIndex, sName, "scene-clear-selection", "Space" };
Settings::SettingValue<std::string> mSceneUnhideAll{ mIndex, sName, "scene-unhide-all", "Alt+H" };
Settings::SettingValue<std::string> mSceneToggleVisibility{ mIndex, sName, "scene-toggle-visibility", "H" };
Settings::SettingValue<std::string> mSceneGroup0{ mIndex, sName, "scene-group-0", "0" };
Settings::SettingValue<std::string> mSceneSave0{ mIndex, sName, "scene-save-0", "Ctrl+0" };
Settings::SettingValue<std::string> mSceneGroup1{ mIndex, sName, "scene-group-1", "1" };
Settings::SettingValue<std::string> mSceneSave1{ mIndex, sName, "scene-save-1", "Ctrl+1" };
Settings::SettingValue<std::string> mSceneGroup2{ mIndex, sName, "scene-group-2", "2" };
Settings::SettingValue<std::string> mSceneSave2{ mIndex, sName, "scene-save-2", "Ctrl+2" };
Settings::SettingValue<std::string> mSceneGroup3{ mIndex, sName, "scene-group-3", "3" };
Settings::SettingValue<std::string> mSceneSave3{ mIndex, sName, "scene-save-3", "Ctrl+3" };
Settings::SettingValue<std::string> mSceneGroup4{ mIndex, sName, "scene-group-4", "4" };
Settings::SettingValue<std::string> mSceneSave4{ mIndex, sName, "scene-save-4", "Ctrl+4" };
Settings::SettingValue<std::string> mSceneGroup5{ mIndex, sName, "scene-group-5", "5" };
Settings::SettingValue<std::string> mSceneSave5{ mIndex, sName, "scene-save-5", "Ctrl+5" };
Settings::SettingValue<std::string> mSceneGroup6{ mIndex, sName, "scene-group-6", "6" };
Settings::SettingValue<std::string> mSceneSave6{ mIndex, sName, "scene-save-6", "Ctrl+6" };
Settings::SettingValue<std::string> mSceneGroup7{ mIndex, sName, "scene-group-7", "7" };
Settings::SettingValue<std::string> mSceneSave7{ mIndex, sName, "scene-save-7", "Ctrl+7" };
Settings::SettingValue<std::string> mSceneGroup8{ mIndex, sName, "scene-group-8", "8" };
Settings::SettingValue<std::string> mSceneSave8{ mIndex, sName, "scene-save-8", "Ctrl+8" };
Settings::SettingValue<std::string> mSceneGroup9{ mIndex, sName, "scene-group-9", "9" };
Settings::SettingValue<std::string> mSceneSave9{ mIndex, sName, "scene-save-9", "Ctrl+9" };
Settings::SettingValue<std::string> mFreeForward{ mIndex, sName, "free-forward", "W" };
Settings::SettingValue<std::string> mFreeBackward{ mIndex, sName, "free-backward", "S" };
Settings::SettingValue<std::string> mFreeLeft{ mIndex, sName, "free-left", "A" };
Settings::SettingValue<std::string> mFreeRight{ mIndex, sName, "free-right", "D" };
Settings::SettingValue<std::string> mFreeRollLeft{ mIndex, sName, "free-roll-left", "Q" };
Settings::SettingValue<std::string> mFreeRollRight{ mIndex, sName, "free-roll-right", "E" };
Settings::SettingValue<std::string> mFreeSpeedMode{ mIndex, sName, "free-speed-mode", "F" };
Settings::SettingValue<std::string> mOrbitUp{ mIndex, sName, "orbit-up", "W" };
Settings::SettingValue<std::string> mOrbitDown{ mIndex, sName, "orbit-down", "S" };
Settings::SettingValue<std::string> mOrbitLeft{ mIndex, sName, "orbit-left", "A" };
Settings::SettingValue<std::string> mOrbitRight{ mIndex, sName, "orbit-right", "D" };
Settings::SettingValue<std::string> mOrbitRollLeft{ mIndex, sName, "orbit-roll-left", "Q" };
Settings::SettingValue<std::string> mOrbitRollRight{ mIndex, sName, "orbit-roll-right", "E" };
Settings::SettingValue<std::string> mOrbitSpeedMode{ mIndex, sName, "orbit-speed-mode", "F" };
Settings::SettingValue<std::string> mOrbitCenterSelection{ mIndex, sName, "orbit-center-selection", "C" };
Settings::SettingValue<std::string> mScriptEditorComment{ mIndex, sName, "script-editor-comment", "" };
Settings::SettingValue<std::string> mScriptEditorUncomment{ mIndex, sName, "script-editor-uncomment", "" };
};
struct ModelsCategory : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
static constexpr std::string_view sName = "Models";
Settings::SettingValue<std::string> mBaseanim{ mIndex, sName, "baseanim", "meshes/base_anim.nif" };
Settings::SettingValue<std::string> mBaseanimkna{ mIndex, sName, "baseanimkna", "meshes/base_animkna.nif" };
Settings::SettingValue<std::string> mBaseanimfemale{ mIndex, sName, "baseanimfemale",
"meshes/base_anim_female.nif" };
Settings::SettingValue<std::string> mWolfskin{ mIndex, sName, "wolfskin", "meshes/wolf/skin.nif" };
};
struct Values : Settings::WithIndex
{
using Settings::WithIndex::WithIndex;
WindowsCategory mWindows{ mIndex };
RecordsCategory mRecords{ mIndex };
IdTablesCategory mIdTables{ mIndex };
IdDialoguesCategory mIdDialogues{ mIndex };
ReportsCategory mReports{ mIndex };
SearchAndReplaceCategory mSearchAndReplace{ mIndex };
ScriptsCategory mScripts{ mIndex };
GeneralInputCategory mGeneralInput{ mIndex };
SceneInputCategory mSceneInput{ mIndex };
RenderingCategory mRendering{ mIndex };
TooltipsCategory mTooltips{ mIndex };
SceneEditingCategory mSceneEditing{ mIndex };
KeyBindingsCategory mKeyBindings{ mIndex };
ModelsCategory mModels{ mIndex };
};
}
#endif

View File

@ -41,17 +41,17 @@ void CSMTools::RaceCheckStage::performPerRecord(int stage, CSMDoc::Messages& mes
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
// test for positive height
if (race.mData.mHeight.mMale <= 0)
if (race.mData.mMaleHeight <= 0)
messages.add(id, "Male height is non-positive", "", CSMDoc::Message::Severity_Error);
if (race.mData.mHeight.mFemale <= 0)
if (race.mData.mFemaleHeight <= 0)
messages.add(id, "Female height is non-positive", "", CSMDoc::Message::Severity_Error);
// test for non-negative weight
if (race.mData.mWeight.mMale < 0)
if (race.mData.mMaleWeight < 0)
messages.add(id, "Male weight is negative", "", CSMDoc::Message::Severity_Error);
if (race.mData.mWeight.mFemale < 0)
if (race.mData.mFemaleWeight < 0)
messages.add(id, "Female weight is negative", "", CSMDoc::Message::Severity_Error);
/// \todo check data members that can't be edited in the table view

View File

@ -693,22 +693,12 @@ void CSMTools::ReferenceableCheckStage::npcCheck(
}
else if (npc.mNpdt.mHealth != 0)
{
if (npc.mNpdt.mStrength == 0)
messages.add(id, "Strength is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mIntelligence == 0)
messages.add(id, "Intelligence is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mWillpower == 0)
messages.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mAgility == 0)
messages.add(id, "Agility is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mSpeed == 0)
messages.add(id, "Speed is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mEndurance == 0)
messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mPersonality == 0)
messages.add(id, "Personality is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mLuck == 0)
messages.add(id, "Luck is equal to zero", "", CSMDoc::Message::Severity_Warning);
for (size_t i = 0; i < npc.mNpdt.mAttributes.size(); ++i)
{
if (npc.mNpdt.mAttributes[i] == 0)
messages.add(id, ESM::Attribute::indexToRefId(i).getRefIdString() + " is equal to zero", {},
CSMDoc::Message::Severity_Warning);
}
}
if (level <= 0)

View File

@ -98,9 +98,8 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages& message
if (cellRef.mEnchantmentCharge < -1)
messages.add(id, "Negative number of enchantment points", "", CSMDoc::Message::Severity_Error);
// Check if gold value isn't negative
if (cellRef.mGoldValue < 0)
messages.add(id, "Negative gold value", "", CSMDoc::Message::Severity_Error);
if (cellRef.mCount < 1)
messages.add(id, "Reference without count", {}, CSMDoc::Message::Severity_Error);
}
int CSMTools::ReferenceCheckStage::setup()

View File

@ -7,6 +7,7 @@
#include <string_view>
#include <vector>
#include <apps/opencs/model/prefs/state.hpp>
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/idcollection.hpp>
#include <apps/opencs/model/world/record.hpp>
@ -132,11 +133,11 @@ namespace CSMWorld
bool beast = mRaceData ? mRaceData->isBeast() : false;
if (beast)
return Settings::Manager::getString("baseanimkna", "Models");
return CSMPrefs::get()["Models"]["baseanimkna"].toString();
else if (mFemale)
return Settings::Manager::getString("baseanimfemale", "Models");
return CSMPrefs::get()["Models"]["baseanimfemale"].toString();
else
return Settings::Manager::getString("baseanim", "Models");
return CSMPrefs::get()["Models"]["baseanim"].toString();
}
ESM::RefId ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const

View File

@ -333,6 +333,37 @@ namespace CSMWorld
return true;
}
SelectionGroupColumn::SelectionGroupColumn()
: Column<ESM::SelectionGroup>(Columns::ColumnId_SelectionGroupObjects, ColumnBase::Display_None)
{
}
QVariant SelectionGroupColumn::get(const Record<ESM::SelectionGroup>& record) const
{
QVariant data;
QStringList selectionInfo;
const std::vector<std::string>& instances = record.get().selectedInstances;
for (const std::string& instance : instances)
selectionInfo << QString::fromStdString(instance);
data.setValue(selectionInfo);
return data;
}
void SelectionGroupColumn::set(Record<ESM::SelectionGroup>& record, const QVariant& data)
{
ESM::SelectionGroup record2 = record.get();
for (const auto& item : data.toStringList())
record2.selectedInstances.push_back(item.toStdString());
record.setModified(record2);
}
bool SelectionGroupColumn::isEditable() const
{
return false;
}
std::optional<std::uint32_t> getSkillIndex(std::string_view value)
{
int index = ESM::Skill::refIdToIndex(ESM::RefId::stringRefId(value));

View File

@ -16,6 +16,7 @@
#include <components/esm3/loadinfo.hpp>
#include <components/esm3/loadrace.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/esm3/selectiongroup.hpp>
#include <components/esm3/variant.hpp>
#include <optional>
@ -570,19 +571,34 @@ namespace CSMWorld
QVariant get(const Record<ESXRecordT>& record) const override
{
const ESM::Race::MaleFemaleF& value = mWeight ? record.get().mData.mWeight : record.get().mData.mHeight;
return mMale ? value.mMale : value.mFemale;
if (mWeight)
{
if (mMale)
return record.get().mData.mMaleWeight;
return record.get().mData.mFemaleWeight;
}
if (mMale)
return record.get().mData.mMaleHeight;
return record.get().mData.mFemaleHeight;
}
void set(Record<ESXRecordT>& record, const QVariant& data) override
{
ESXRecordT record2 = record.get();
ESM::Race::MaleFemaleF& value = mWeight ? record2.mData.mWeight : record2.mData.mHeight;
(mMale ? value.mMale : value.mFemale) = data.toFloat();
if (mWeight)
{
if (mMale)
record2.mData.mMaleWeight = data.toFloat();
else
record2.mData.mFemaleWeight = data.toFloat();
}
else
{
if (mMale)
record2.mData.mMaleHeight = data.toFloat();
else
record2.mData.mFemaleHeight = data.toFloat();
}
record.setModified(record2);
}
@ -1095,19 +1111,19 @@ namespace CSMWorld
};
template <typename ESXRecordT>
struct GoldValueColumn : public Column<ESXRecordT>
struct StackSizeColumn : public Column<ESXRecordT>
{
GoldValueColumn()
: Column<ESXRecordT>(Columns::ColumnId_CoinValue, ColumnBase::Display_Integer)
StackSizeColumn()
: Column<ESXRecordT>(Columns::ColumnId_StackCount, ColumnBase::Display_Integer)
{
}
QVariant get(const Record<ESXRecordT>& record) const override { return record.get().mGoldValue; }
QVariant get(const Record<ESXRecordT>& record) const override { return record.get().mCount; }
void set(Record<ESXRecordT>& record, const QVariant& data) override
{
ESXRecordT record2 = record.get();
record2.mGoldValue = data.toInt();
record2.mCount = data.toInt();
record.setModified(record2);
}
@ -2376,6 +2392,17 @@ namespace CSMWorld
void set(Record<ESM::BodyPart>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct SelectionGroupColumn : public Column<ESM::SelectionGroup>
{
SelectionGroupColumn();
QVariant get(const Record<ESM::SelectionGroup>& record) const override;
void set(Record<ESM::SelectionGroup>& record, const QVariant& data) override;
bool isEditable() const override;
};
}
// This is required to access the type as a QVariant.

View File

@ -56,7 +56,7 @@ namespace CSMWorld
{ ColumnId_FactionIndex, "Faction Index" },
{ ColumnId_Charges, "Charges" },
{ ColumnId_Enchantment, "Enchantment" },
{ ColumnId_CoinValue, "Coin Value" },
{ ColumnId_StackCount, "Count" },
{ ColumnId_Teleport, "Teleport" },
{ ColumnId_TeleportCell, "Teleport Cell" },
{ ColumnId_LockLevel, "Lock Level" },

View File

@ -45,7 +45,7 @@ namespace CSMWorld
ColumnId_FactionIndex = 31,
ColumnId_Charges = 32,
ColumnId_Enchantment = 33,
ColumnId_CoinValue = 34,
ColumnId_StackCount = 34,
ColumnId_Teleport = 35,
ColumnId_TeleportCell = 36,
ColumnId_LockLevel = 37,
@ -347,6 +347,8 @@ namespace CSMWorld
ColumnId_LevelledCreatureId = 315,
ColumnId_SelectionGroupObjects = 316,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,

View File

@ -587,7 +587,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mRefs.addColumn(new FactionIndexColumn<CellRef>);
mRefs.addColumn(new ChargesColumn<CellRef>);
mRefs.addColumn(new EnchantmentChargesColumn<CellRef>);
mRefs.addColumn(new GoldValueColumn<CellRef>);
mRefs.addColumn(new StackSizeColumn<CellRef>);
mRefs.addColumn(new TeleportColumn<CellRef>);
mRefs.addColumn(new TeleportCellColumn<CellRef>);
mRefs.addColumn(new PosColumn<CellRef>(&CellRef::mDoorDest, 0, true));
@ -620,6 +620,11 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mDebugProfiles.addColumn(new DescriptionColumn<ESM::DebugProfile>);
mDebugProfiles.addColumn(new ScriptColumn<ESM::DebugProfile>(ScriptColumn<ESM::DebugProfile>::Type_Lines));
mSelectionGroups.addColumn(new StringIdColumn<ESM::SelectionGroup>);
mSelectionGroups.addColumn(new RecordStateColumn<ESM::SelectionGroup>);
mSelectionGroups.addColumn(new FixedRecordTypeColumn<ESM::SelectionGroup>(UniversalId::Type_SelectionGroup));
mSelectionGroups.addColumn(new SelectionGroupColumn);
mMetaData.appendBlankRecord(ESM::RefId::stringRefId("sys::meta"));
mMetaData.addColumn(new StringIdColumn<MetaData>(true));
@ -664,6 +669,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
addModel(new ResourceTable(&mResourcesManager.get(UniversalId::Type_Textures)), UniversalId::Type_Texture);
addModel(new ResourceTable(&mResourcesManager.get(UniversalId::Type_Videos)), UniversalId::Type_Video);
addModel(new IdTable(&mMetaData), UniversalId::Type_MetaData);
addModel(new IdTable(&mSelectionGroups), UniversalId::Type_SelectionGroup);
mActorAdapter = std::make_unique<ActorAdapter>(*this);
@ -908,6 +914,16 @@ CSMWorld::IdCollection<ESM::DebugProfile>& CSMWorld::Data::getDebugProfiles()
return mDebugProfiles;
}
CSMWorld::IdCollection<ESM::SelectionGroup>& CSMWorld::Data::getSelectionGroups()
{
return mSelectionGroups;
}
const CSMWorld::IdCollection<ESM::SelectionGroup>& CSMWorld::Data::getSelectionGroups() const
{
return mSelectionGroups;
}
const CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand() const
{
return mLand;
@ -1369,6 +1385,17 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
mDebugProfiles.load(*mReader, mBase);
break;
case ESM::REC_SELG:
if (!mProject)
{
unhandledRecord = true;
break;
}
mSelectionGroups.load(*mReader, mBase);
break;
default:
unhandledRecord = true;

View File

@ -33,6 +33,7 @@
#include <components/esm3/loadsoun.hpp>
#include <components/esm3/loadspel.hpp>
#include <components/esm3/loadsscr.hpp>
#include <components/esm3/selectiongroup.hpp>
#include <components/files/multidircollection.hpp>
#include <components/misc/algorithm.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -105,6 +106,7 @@ namespace CSMWorld
IdCollection<ESM::BodyPart> mBodyParts;
IdCollection<ESM::MagicEffect> mMagicEffects;
IdCollection<ESM::DebugProfile> mDebugProfiles;
IdCollection<ESM::SelectionGroup> mSelectionGroups;
IdCollection<ESM::SoundGenerator> mSoundGens;
IdCollection<ESM::StartScript> mStartScripts;
NestedInfoCollection mTopicInfos;
@ -251,6 +253,10 @@ namespace CSMWorld
IdCollection<ESM::DebugProfile>& getDebugProfiles();
const IdCollection<ESM::SelectionGroup>& getSelectionGroups() const;
IdCollection<ESM::SelectionGroup>& getSelectionGroups();
const IdCollection<CSMWorld::Land>& getLand() const;
IdCollection<CSMWorld::Land>& getLand();

View File

@ -741,8 +741,8 @@ namespace CSMWorld
QVariant RaceAttributeAdapter::getData(const Record<ESM::Race>& record, int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length)
ESM::RefId attribute = ESM::Attribute::indexToRefId(subRowIndex);
if (attribute.empty())
throw std::runtime_error("index out of range");
switch (subColIndex)
@ -750,9 +750,9 @@ namespace CSMWorld
case 0:
return subRowIndex;
case 1:
return race.mData.mAttributeValues[subRowIndex].mMale;
return race.mData.getAttribute(attribute, true);
case 2:
return race.mData.mAttributeValues[subRowIndex].mFemale;
return race.mData.getAttribute(attribute, false);
default:
throw std::runtime_error("Race Attribute subcolumn index out of range");
}
@ -762,8 +762,8 @@ namespace CSMWorld
Record<ESM::Race>& record, const QVariant& value, int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length)
ESM::RefId attribute = ESM::Attribute::indexToRefId(subRowIndex);
if (attribute.empty())
throw std::runtime_error("index out of range");
switch (subColIndex)
@ -771,10 +771,10 @@ namespace CSMWorld
case 0:
return; // throw an exception here?
case 1:
race.mData.mAttributeValues[subRowIndex].mMale = value.toInt();
race.mData.setAttribute(attribute, true, value.toInt());
break;
case 2:
race.mData.mAttributeValues[subRowIndex].mFemale = value.toInt();
race.mData.setAttribute(attribute, false, value.toInt());
break;
default:
throw std::runtime_error("Race Attribute subcolumn index out of range");

Some files were not shown because too many files have changed in this diff Show More